[jboss-cvs] JBossAS SVN: r59745 - in branches/Branch_4_2/tomcat: src/main/org/jboss/net/ssl and 9 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Wed Jan 17 21:36:31 EST 2007


Author: remy.maucherat at jboss.com
Date: 2007-01-17 21:36:30 -0500 (Wed, 17 Jan 2007)
New Revision: 59745

Added:
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
   branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml
Removed:
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/tc5/
   branches/Branch_4_2/tomcat/src/resources/tc5-cluster-service.xml
Modified:
   branches/Branch_4_2/tomcat/build.xml
   branches/Branch_4_2/tomcat/src/main/org/jboss/net/ssl/JBossSocketFactory.java
   branches/Branch_4_2/tomcat/src/resources/META-INF/jboss-aop.xml
   branches/Branch_4_2/tomcat/src/resources/jboss-service-all.xml
   branches/Branch_4_2/tomcat/src/resources/jboss-service.xml
   branches/Branch_4_2/tomcat/src/resources/server.xml
   branches/Branch_4_2/tomcat/src/resources/standalone-server.xml
   branches/Branch_4_2/tomcat/src/resources/web.xml
   branches/Branch_4_2/tomcat/src/resources/webserver-xmbean.xml
   branches/Branch_4_2/tomcat/src/webapps/ROOT.war/WEB-INF/web.xml
Log:
- Migrate from Tomcat to JBoss Web 2.

Modified: branches/Branch_4_2/tomcat/build.xml
===================================================================
--- branches/Branch_4_2/tomcat/build.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/build.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -20,7 +20,7 @@
 <!-- $Id$ -->
 
 
-<project default="main" name="JBoss/Tomcat-5">
+<project default="main" name="JBoss/Web">
 
   <!--
      | Include the common Buildmagic elements.
@@ -46,8 +46,8 @@
   <target name="configure" unless="configure.disable">
 
     <!-- Module name(s) & version -->
-    <property name="module.name" value="tomcat4"/>
-    <property name="module.Name" value="JBoss Tomcat4 Plugin"/>
+    <property name="module.name" value="tomcat"/>
+    <property name="module.Name" value="JBoss Web Plugin"/>
     <property file="module.version"/>
 
     &libraries;
@@ -59,7 +59,6 @@
       <path refid="dom4j.dom4j.classpath"/>
       <path refid="oswego.concurrent.classpath"/>
       <path refid="jgroups.jgroups.classpath"/>
-      <path refid="apache.modeler.classpath"/>
     </path>
 
     <!-- ======= -->
@@ -114,21 +113,21 @@
     <property name="local.classpath" value=""/>
 
     <!-- The classpath required to build classes. -->
-    <path id="javac.tomcat55.classpath">
+    <path id="javac.jboss.web.classpath">
       <pathelement path="${classpath}"/>
       <pathelement path="${local.classpath}"/>
       <path refid="thirdparty.classpath"/>
-      <path refid="apache.tomcat55.classpath"/>
+      <path refid="jboss.web.classpath"/>
     </path>
 
     <!-- The classpath required to build classes. -->
     <path id="javac.classpath">
-      <path refid="javac.tomcat55.classpath"/>
+      <path refid="javac.jboss.web.classpath"/>
     </path>
 
     <!-- The classpath required to build javadocs. -->
     <path id="javadoc.classpath">
-      <path refid="javac.tomcat55.classpath"/>
+      <path refid="javac.jboss.web.classpath"/>
     </path>
 
     <!-- Packages to include when generating api documentation -->
@@ -163,7 +162,7 @@
       failonerror="${javac.fail.onerror}">
       <src path="${source.java}"/>
       <src path="${source.tests}"/>
-      <classpath refid="javac.tomcat55.classpath"/>
+      <classpath refid="javac.jboss.web.classpath"/>
     </javac>
     <copy todir="${build.classes}">
       <fileset dir="${source.java}">
@@ -205,34 +204,28 @@
     <call target="compile"/>
 
     <mkdir dir="${build.lib}"/>
-    <mkdir dir="${build.deploy}/jbossweb-tomcat55.sar/META-INF"/>
+    <mkdir dir="${build.deploy}/jboss-web.sar/META-INF"/>
 
-    <!-- Build the jbossweb-tomcat55.sar -->
+    <!-- Build the jboss-web.sar -->
     <copy file="${build.resources}/jboss-service.xml"
-      tofile="${build.deploy}/jbossweb-tomcat55.sar/META-INF/jboss-service.xml"/>
+      tofile="${build.deploy}/jboss-web.sar/META-INF/jboss-service.xml"/>
     <copy file="${build.resources}/context.xml"
-      todir="${build.deploy}/jbossweb-tomcat55.sar"/>
+      todir="${build.deploy}/jboss-web.sar"/>
     <copy file="${build.resources}/web.xml"
-      tofile="${build.deploy}/jbossweb-tomcat55.sar/conf/web.xml"/>
+      tofile="${build.deploy}/jboss-web.sar/conf/web.xml"/>
     <copy file="${build.resources}/server.xml"
-      tofile="${build.deploy}/jbossweb-tomcat55.sar/server.xml"/>
+      tofile="${build.deploy}/jboss-web.sar/server.xml"/>
     <!-- TDI 20041108 breaks the build
     <copy file="${build.resources}/sso-channel.xml"
-       tofile="${build.deploy}/jbossweb-tomcat55.sar/sso-channel.xml" />
+       tofile="${build.deploy}/jboss-web.sar/sso-channel.xml" />
     -->
-    <copy todir="${build.deploy}/jbossweb-tomcat55.sar">
-      <fileset dir="${apache.tomcat55.lib}">
+    <copy todir="${build.deploy}/jboss-web.sar">
+      <fileset dir="${jboss.web.lib}">
         <include name="*.jar"/>
       </fileset>
     </copy>
 
-   <copy todir="${build.deploy}/jbossweb-tomcat55.sar"
-        file="${apache.modeler.lib}/commons-modeler.jar" />
-
-     <copy todir="${build.deploy}/jbossweb-tomcat55.sar"
-        file="${commons.el.lib}/commons-el.jar" />
-
-     <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs">
+     <copy todir="${build.deploy}/jboss-web.sar/jsf-libs">
         <fileset dir="${apache.myfaces.lib}">
            <include name="jstl.jar"/>
            <include name="myfaces-api.jar"/>
@@ -240,25 +233,16 @@
         </fileset>
      </copy>
 
-     <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs"
-        file="${apache.collections.lib}/commons-collections.jar" />
-     <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs"
-        file="${apache.beanutils.lib}/commons-beanutils.jar" />
-     <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs"
-        file="${apache.digester.lib}/commons-digester.jar" />
-     <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs"
-        file="${apache.lang.lib}/commons-lang.jar" />
-
-    <jar jarfile="${build.deploy}/jbossweb-tomcat55.sar/tomcat55-service.jar"
+    <jar jarfile="${build.deploy}/jboss-web.sar/jbossweb-service.jar"
       manifest="${build.etc}/default.mf">
       <!-- The service classes -->
       <fileset dir="${build.classes}">
         <exclude name="org/jboss/**/*.properties"/>
-        <exclude name="org/jboss/web/tomcat/tc5/session/AopMarker*"/>
+        <exclude name="org/jboss/web/tomcat/service/session/AopMarker*"/>
         <include name="org/jboss/net/**"/>
         <include name="org/jboss/web/tomcat/filters/**"/>
         <include name="org/jboss/web/tomcat/servlets/**"/>
-        <include name="org/jboss/web/tomcat/tc5/**"/>
+        <include name="org/jboss/web/tomcat/service/**"/>
         <include name="org/jboss/web/tomcat/security/**"/>
         <include name="org/jboss/web/tomcat/statistics/**"/>
       </fileset>
@@ -267,17 +251,17 @@
         <include name="org/jboss/**/*.properties"/>
       </fileset>
     </jar>
-    <copy todir="${build.deploy}/jbossweb-tomcat55.sar">
+    <copy todir="${build.deploy}/jboss-web.sar">
       <fileset dir="${source.webapps}">
       </fileset>
     </copy>
 
    <!-- jar for fine grained aop -->
    <!-- package in .aop. This is used for fine-grained replication -->
-   <jar destfile="${build.lib}/tc5-cluster.aop">
+   <jar destfile="${build.lib}/jboss-web-cluster.aop">
       <fileset dir="${build.classes}">
-         <include name="org/jboss/web/tomcat/tc5/session/AopMarker.class"/>
-         <include name="org/jboss/web/tomcat/tc5/session/InstanceOfAopMarker.class"/>
+         <include name="org/jboss/web/tomcat/service/session/AopMarker.class"/>
+         <include name="org/jboss/web/tomcat/service/session/InstanceOfAopMarker.class"/>
       </fileset>
       <fileset dir="${build.resources}">
          <include name="META-INF/jboss-aop.xml"/>
@@ -285,11 +269,11 @@
    </jar>
 
    <!-- Cluster service sar and aop descriptor used for fine-grained replication -->
-  	<mkdir dir="${build.deploy}/tc5-cluster.sar/META-INF"/>
-   <copy todir="${build.deploy}/tc5-cluster.sar"
-         file="${build.lib}/tc5-cluster.aop"/>
-  	<copy tofile="${build.deploy}/tc5-cluster.sar/META-INF/jboss-service.xml"
-  	      file="${build.resources}/tc5-cluster-service.xml"/>
+  	<mkdir dir="${build.deploy}/jboss-web-cluster.sar/META-INF"/>
+   <copy todir="${build.deploy}/jboss-web-cluster.sar"
+         file="${build.lib}/jboss-web-cluster.aop"/>
+  	<copy tofile="${build.deploy}/jboss-web-cluster.sar/META-INF/jboss-service.xml"
+  	      file="${build.resources}/jboss-web-cluster-service.xml"/>
 
     <!-- Update the build marker to allow bypassing -->
     <touch file="${build-bypass.marker}"/>

Modified: branches/Branch_4_2/tomcat/src/main/org/jboss/net/ssl/JBossSocketFactory.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/net/ssl/JBossSocketFactory.java	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/net/ssl/JBossSocketFactory.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -32,7 +32,7 @@
 import javax.net.ssl.KeyManagerFactory;
 
 import org.jboss.security.SecurityDomain;
-import org.apache.tomcat.util.net.jsse.JSSE14SocketFactory;
+import org.apache.tomcat.util.net.jsse.JSSESocketFactory;
 
 /**
  * Extends the tomcat JSSE14SocketFactory to obtain the server key and trust
@@ -41,7 +41,7 @@
  * 
  */ 
 public class JBossSocketFactory
-   extends JSSE14SocketFactory
+   extends JSSESocketFactory
 {
    private SecurityDomain securityDomain;
 

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,351 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import javax.management.ObjectName;
+
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+
+/**
+ * The tomcat war deployer configuration passed in from the web container.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public class DeployerConfig
+{
+   /**
+    * The tomcat sar class loader
+    */
+   private ClassLoader serviceClassLoader;
+   /**
+    * The domain used for the tomcat mbeans
+    */
+   private String catalinaDomain = "Catalina";
+
+   /**
+    * The fully qualified name of the class that will be used for session
+    * management if <tt>distributable</tt> is set to true.
+    */
+   private String managerClass = "org.jboss.web.tomcat.service.session.JBossManager";
+
+   /**
+    * JMX Object name of the TreeCache MBean. Used by JBossCacheManager
+    */
+   private String cacheName = "jboss.cache:service=TreeCache";
+
+   /**
+    * The web context class to create
+    */
+   private String contextClassName;
+   /**
+    * The parent class loader first model flag
+    */
+   private boolean java2ClassLoadingCompliance = false;
+   /**
+    * A flag indicating if war archives should be unpacked
+    */
+   private boolean unpackWars = true;
+   /**
+    * If true, ejb-links that don't resolve don't cause an error
+    * (fallback to jndi-name)
+    */
+   private boolean lenientEjbLink = false;
+   /**
+    * The tomcat service JMX object name
+    */
+   private ObjectName serviceName;
+   /**
+    * The catalina debug level
+    */
+   private int debugLevel;
+   /**
+    * A flag indicating if the JBoss UCL should be used
+    */
+   private boolean useJBossWebLoader = true;
+   /**
+    * A flag indicating if the working dir for a war deployment should be
+    * delete when the war is undeployed.
+    */
+   private boolean deleteWorkDirs = true;
+   /**
+    * Which snapshot mode should be used in clustered environment?
+    * Default: instant
+    */
+   private String snapshotMode = "instant"; // instant or interval
+   /**
+    * With IntervalSnapshotManager use this interval (in ms) for snapshotting
+    */
+   private int snapshotInterval = 1000;
+
+   /**
+    * Should the clustering code use caching or not?
+    */
+   private boolean useLocalCache;
+
+   /**
+    * Whether to use MOD_JK(2) for sticky session combined with JvmRoute. If set to true,
+    * it will insert a JvmRouteFilter to intercept every request and replace the JvmRoute
+    * if it detects a failover.
+    */
+   private boolean useJK = false;
+
+   /**
+    * Get the request attribute name under which the JAAS Subject is store
+    */
+   private String subjectAttributeName = null;
+   /**
+    * The default security-domain name to use
+    */
+   private String defaultSecurityDomain;
+   /** Package names that should be ignored for class loading */
+   private String[] filteredPackages;
+
+   /**
+    * Flag indicating whether web-app specific context xmls may set the privileged flag.
+    */
+   private boolean allowSelfPrivilegedWebApps = false;
+   /** The service used to flush authentication cache on session invalidation. */
+   private JaasSecurityManagerServiceMBean secMgrService;
+
+   public ClassLoader getServiceClassLoader()
+   {
+      return serviceClassLoader;
+   }
+
+   public void setServiceClassLoader(ClassLoader serviceClassLoader)
+   {
+      this.serviceClassLoader = serviceClassLoader;
+   }
+
+   public String getManagerClass()
+   {
+      return managerClass;
+   }
+
+   public void setManagerClass(String managerClass)
+   {
+      this.managerClass = managerClass;
+   }
+
+   public String getCacheName()
+   {
+      return cacheName;
+   }
+
+   public void setCacheName(String cacheName)
+   {
+      this.cacheName = cacheName;
+   }
+
+   public String getCatalinaDomain()
+   {
+      return catalinaDomain;
+   }
+
+   public void setCatalinaDomain(String catalinaDomain)
+   {
+      this.catalinaDomain = catalinaDomain;
+   }
+
+   public String getContextClassName()
+   {
+      return contextClassName;
+   }
+
+   public void setContextClassName(String contextClassName)
+   {
+      this.contextClassName = contextClassName;
+   }
+
+   public boolean isJava2ClassLoadingCompliance()
+   {
+      return java2ClassLoadingCompliance;
+   }
+
+   public void setJava2ClassLoadingCompliance(boolean java2ClassLoadingCompliance)
+   {
+      this.java2ClassLoadingCompliance = java2ClassLoadingCompliance;
+   }
+
+   public boolean isUnpackWars()
+   {
+      return unpackWars;
+   }
+
+   public void setUnpackWars(boolean unpackWars)
+   {
+      this.unpackWars = unpackWars;
+   }
+
+   public boolean isLenientEjbLink()
+   {
+      return lenientEjbLink;
+   }
+
+   public void setLenientEjbLink(boolean lenientEjbLink)
+   {
+      this.lenientEjbLink = lenientEjbLink;
+   }
+
+   public ObjectName getServiceName()
+   {
+      return serviceName;
+   }
+
+   public void setServiceName(ObjectName serviceName)
+   {
+      this.serviceName = serviceName;
+   }
+
+   public int getDebugLevel()
+   {
+      return debugLevel;
+   }
+
+   public void setDebugLevel(int debugLevel)
+   {
+      this.debugLevel = debugLevel;
+   }
+
+   public boolean isUseJBossWebLoader()
+   {
+      return useJBossWebLoader;
+   }
+
+   public void setUseJBossWebLoader(boolean useJBossWebLoader)
+   {
+      this.useJBossWebLoader = useJBossWebLoader;
+   }
+
+   public boolean isDeleteWorkDirs()
+   {
+      return deleteWorkDirs;
+   }
+
+   public void setDeleteWorkDirs(boolean deleteWorkDirs)
+   {
+      this.deleteWorkDirs = deleteWorkDirs;
+   }
+
+   public String getSnapshotMode()
+   {
+      return snapshotMode;
+   }
+
+   public void setSnapshotMode(String snapshotMode)
+   {
+      this.snapshotMode = snapshotMode;
+   }
+
+   public int getSnapshotInterval()
+   {
+      return snapshotInterval;
+   }
+
+   public void setSnapshotInterval(int snapshotInterval)
+   {
+      this.snapshotInterval = snapshotInterval;
+   }
+
+   public boolean isUseLocalCache()
+   {
+      return useLocalCache;
+   }
+
+   public void setUseLocalCache(boolean useLocalCache)
+   {
+      this.useLocalCache = useLocalCache;
+   }
+
+   public boolean isUseJK()
+   {
+      return useJK;
+   }
+
+   public void setUseJK(boolean useJK)
+   {
+      this.useJK = useJK;
+   }
+
+   public String getSubjectAttributeName()
+   {
+      return subjectAttributeName;
+   }
+
+   public void setSubjectAttributeName(String subjectAttributeName)
+   {
+      this.subjectAttributeName = subjectAttributeName;
+   }
+
+   /**
+    * Get the default security domain implementation to use if a war
+    * does not declare a security-domain.
+    *
+    * @return jndi name of the security domain binding to use.
+    * @jmx:managed-attribute
+    */
+   public String getDefaultSecurityDomain()
+   {
+      return defaultSecurityDomain;
+   }
+
+   /**
+    * Set the default security domain implementation to use if a war
+    * does not declare a security-domain.
+    *
+    * @param defaultSecurityDomain - jndi name of the security domain binding
+    *                              to use.
+    * @jmx:managed-attribute
+    */
+   public void setDefaultSecurityDomain(String defaultSecurityDomain)
+   {
+      this.defaultSecurityDomain = defaultSecurityDomain;
+   }
+
+   public boolean isAllowSelfPrivilegedWebApps()
+   {
+      return allowSelfPrivilegedWebApps;
+   }
+
+   public void setAllowSelfPrivilegedWebApps(boolean allowSelfPrivilegedWebApps)
+   {
+      this.allowSelfPrivilegedWebApps = allowSelfPrivilegedWebApps;
+   }
+   public JaasSecurityManagerServiceMBean getSecurityManagerService()
+   {
+      return secMgrService;
+   }
+   public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr)
+   {
+      this.secMgrService = mgr;
+   }
+
+   public String[] getFilteredPackages()
+   {
+      return filteredPackages;
+   }
+   public void setFilteredPackages(String[] filteredPackages)
+   {
+      this.filteredPackages = filteredPackages;
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,729 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.security.jacc.PolicyContext;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Connector;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployment.DeploymentInfo;
+import org.jboss.deployment.SubDeployerExt;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+import org.jboss.system.ServiceControllerMBean;
+import org.jboss.system.server.Server;
+import org.jboss.system.server.ServerImplMBean;
+import org.jboss.web.AbstractWebContainer;
+import org.jboss.web.AbstractWebDeployer;
+import org.jboss.web.tomcat.security.HttpServletRequestPolicyContextHandler;
+import org.jboss.web.tomcat.service.session.SessionIDGenerator;
+
+
+/**
+ * An implementation of the AbstractWebContainer for the Jakarta Tomcat5
+ * servlet container. It has no code dependency on tomcat - only the new JMX
+ * model is used.
+ * <p/>
+ * Tomcat5 is organized as a set of mbeans - just like jboss.
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @author Wonne.Keysers at realsoftware.be
+ * @author Dimitris.Andreadis at jboss.org
+ * @author Anil.Saldhana at jboss.org
+ * @version $Revision: 57206 $
+ * @see org.jboss.web.AbstractWebContainer
+ */
+public class JBossWeb extends AbstractWebContainer
+   implements JBossWebMBean, NotificationListener
+{
+   // Constants -----------------------------------------------------
+   public static final String NAME = "JBossWeb";
+
+   /**
+    * Default value for property <code>cacheName</code>. This name will be used by JBossCache exclusively
+    * for Tomcat clustering, e.g., session and sso replication.
+    */
+   public static final String DEFAULT_CACHE_NAME =
+      "jboss.cache:service=TomcatClusteringCache";
+
+   // XXX We could make this configurable - so it can support other containers
+   // that provide JMX-based deployment.
+   private String contextClassName =
+      "org.apache.catalina.core.StandardContext"; 
+   
+   /**
+    * Configurable map of tomcat authenticators
+    * Keyed in by the http auth method that gets 
+    * plugged into the Context Config and then into the StandardContext 
+    */
+   private Properties authenticators = null;
+   
+   /**
+    * Domain for tomcat5 mbeans
+    */
+   private String catalinaDomain = "Catalina";
+
+   /**
+    * ObjectName of a shared TreeCache used for clustered session replication
+    * and clustered single-sign-on
+    */
+   private String cacheName = DEFAULT_CACHE_NAME;
+
+   /**
+    * The fully qualified name of the class that will be used for session
+    * management if <tt>distributable</tt> is set to true.
+    */
+   protected String managerClass = "org.jboss.web.tomcat.service.session.JBossCacheManager";
+
+   /**
+    * With IntervalSnapshotManager use this interval (in ms)
+    * for snapshotting
+    */
+   private int snapshotInterval = 1000;
+
+   /**
+    * Which snapshot mode should be used in clustered environment?
+    * Default: instant
+    */
+   private String snapshotMode = "instant"; // instant or interval
+
+   /**
+    * Should the clustering code use a local cache for the sessions?
+    */
+   private boolean useLocalCache = true;
+
+   /**
+    * Whether we are using Apache MOD_JK(2) module or not
+    */
+   private boolean useJK = false;
+
+   /**
+    * A flag indicating if the JBoss Loader should be used
+    */
+   private boolean useJBossWebLoader = true;
+
+   /**
+    * JBAS-3358: Work directory shouldn't be deleted on Context Destroy
+    */
+   private boolean deleteWorkDirOnContextDestroy = false;
+   
+   /**
+    * JBAS-2283: Provide custom header based auth support
+    */
+   private String httpHeaderForSSOAuth = null;
+   private String sessionCookieForSSOAuth = null;
+   
+   /**
+    * The server xml configuration file name
+    */
+   private String serverConfigFile = "server.xml";
+
+   /**
+    * Get the request attribute name under which the JAAS Subject is store
+    */
+   private String subjectAttributeName = null;
+   
+   /**
+    * Flag indicating whether web-app specific context xmls may set the privileged flag.
+    */
+   private boolean allowSelfPrivilegedWebApps = false;
+   
+   /** The service used to flush authentication cache on session invalidation. */
+   private JaasSecurityManagerServiceMBean secMgrService;
+   
+   /** */
+   private String[] filteredPackages;
+   
+   /** Hold a proxy reference to myself, used when registering to MainDeployer */
+   private SubDeployerExt thisProxy;
+
+   public String getName()
+   {
+      return NAME;
+   }
+
+
+   public String getManagerClass()
+   {
+      return managerClass;
+   }
+
+   public void setManagerClass(String managerClass)
+   {
+      this.managerClass = managerClass;
+   }
+
+
+   public String getDomain()
+   {
+      return this.catalinaDomain;
+   }
+   public Properties getAuthenticators()
+   {
+      return this.authenticators; 
+   }
+   
+   public void setAuthenticators(Properties prop)
+   {
+      this.authenticators = prop;
+      log.debug("Passed set of authenticators=" + prop);
+   }
+   
+   /**
+    * The most important atteribute - defines the managed domain.
+    * A catalina instance (engine) corresponds to a JMX domain, that's
+    * how we know where to deploy webapps.
+    *
+    * @param catalinaDomain the domain portion of the JMX ObjectNames
+    */
+   public void setDomain(String catalinaDomain)
+   {
+      this.catalinaDomain = catalinaDomain;
+   }
+
+   public void setContextMBeanCode(String className)
+   {
+      this.contextClassName = className;
+   }
+
+   public String getContextMBeanCode()
+   {
+      return contextClassName;
+   }
+
+   /**
+    * Set the snapshot interval in milliseconds for snapshot mode = interval
+    */
+   public void setSnapshotInterval(int interval)
+   {
+      this.snapshotInterval = interval;
+   }
+
+   /**
+    * Get the snapshot interval
+    */
+   public int getSnapshotInterval()
+   {
+      return this.snapshotInterval;
+   }
+
+   /**
+    * Set the snapshot mode. Currently supported: instant or interval
+    */
+   public void setSnapshotMode(String mode)
+   {
+      this.snapshotMode = mode;
+   }
+
+   /**
+    * Get the snapshot mode
+    */
+   public String getSnapshotMode()
+   {
+      return this.snapshotMode;
+   }
+
+   /**
+    * Gets the JMX object name of a shared TreeCache to be used for clustered
+    * single-sign-on.
+    *
+    * @see #DEFAULT_CACHE_NAME
+    * @see org.jboss.web.tomcat.service.sso.TreeCacheSSOClusterManager
+    */
+   public String getCacheName()
+   {
+      return cacheName;
+   }
+
+   /**
+    * Gets the JMX object name of a shared TreeCache to be used for clustered
+    * single-sign-on.
+    * <p/>
+    * <b>NOTE:</b> TreeCache must be deployed before this service.
+    *
+    * @see #DEFAULT_CACHE_NAME
+    * @see org.jboss.web.tomcat.service.sso.TreeCacheSSOClusterManager
+    */
+   public void setCacheName(String cacheName)
+   {
+      this.cacheName = cacheName;
+   }
+
+   public boolean isUseLocalCache()
+   {
+      return useLocalCache;
+   }
+
+   public void setUseLocalCache(boolean useLocalCache)
+   {
+      this.useLocalCache = useLocalCache;
+   }
+
+   public boolean isUseJK()
+   {
+      return useJK;
+   }
+
+   public void setUseJK(boolean useJK)
+   {
+      this.useJK = useJK;
+   } 
+
+   public boolean getDeleteWorkDirOnContextDestroy()
+   {
+      return deleteWorkDirOnContextDestroy;
+   }
+
+   public void setDeleteWorkDirOnContextDestroy(boolean deleteFlag)
+   {
+      this.deleteWorkDirOnContextDestroy = deleteFlag;
+   } 
+
+   public String getHttpHeaderForSSOAuth()
+   {
+      return httpHeaderForSSOAuth;
+   }
+
+   public void setHttpHeaderForSSOAuth(String httpHeader)
+   {
+      this.httpHeaderForSSOAuth = httpHeader;
+   }
+
+   public String getSessionCookieForSSOAuth()
+   {
+      return sessionCookieForSSOAuth;
+   }
+
+   public void setSessionCookieForSSOAuth(String sessionC)
+   {
+      this.sessionCookieForSSOAuth = sessionC;
+   }
+
+   /**
+    * The SessionIdAlphabet is the set of characters used to create a session Id
+    */
+   public void setSessionIdAlphabet(String sessionIdAlphabet)
+   {
+       SessionIDGenerator.getInstance().setSessionIdAlphabet(sessionIdAlphabet);
+   }
+
+   /**
+    * The SessionIdAlphabet is the set of characters used to create a session Id
+    */
+   public String getSessionIdAlphabet()
+   {
+       return SessionIDGenerator.getInstance().getSessionIdAlphabet();
+   }
+   
+   public boolean getUseJBossWebLoader()
+   {
+      return useJBossWebLoader;
+   }
+
+   public void setUseJBossWebLoader(boolean flag)
+   {
+      this.useJBossWebLoader = flag;
+   }
+
+   public String getConfigFile()
+   {
+      return serverConfigFile;
+   }
+
+   public void setConfigFile(String configFile)
+   {
+      this.serverConfigFile = configFile;
+   }
+
+   public String getSubjectAttributeName()
+   {
+      return this.subjectAttributeName;
+   }
+
+   public void setSubjectAttributeName(String name)
+   {
+      this.subjectAttributeName = name;
+   }
+
+   public boolean isAllowSelfPrivilegedWebApps()
+   {
+      return allowSelfPrivilegedWebApps;
+   }
+
+   public void setAllowSelfPrivilegedWebApps(boolean allowSelfPrivilegedWebApps)
+   {
+      this.allowSelfPrivilegedWebApps = allowSelfPrivilegedWebApps;
+   }
+
+   public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr)
+   {
+      this.secMgrService = mgr;
+   }
+
+   public String[] getFilteredPackages()
+   {
+      return filteredPackages;
+   }
+   public void setFilteredPackages(String[] pkgs)
+   {
+      this.filteredPackages = pkgs;
+   }
+
+   public void startService()
+      throws Exception
+   {
+
+      System.setProperty("catalina.ext.dirs",
+         (System.getProperty("jboss.server.home.dir")
+         + File.separator + "lib"));
+
+      String objectNameS = catalinaDomain + ":type=server";
+      ObjectName objectName = new ObjectName(objectNameS);
+
+      // Set the modeler Registry MBeanServer to the that of the tomcat service
+      Registry.getRegistry().setMBeanServer(server);
+
+      Registry.getRegistry().registerComponent(Class.forName("org.apache.catalina.startup.Catalina").newInstance(), 
+              objectName, "org.apache.catalina.startup.Catalina");
+
+      server.setAttribute(objectName, new Attribute
+         ("catalinaHome",
+            System.getProperty("jboss.server.home.dir")));
+      server.setAttribute(objectName, new Attribute
+         ("configFile", serverConfigFile));
+      server.setAttribute(objectName, new Attribute
+         ("useNaming", new Boolean(false)));
+      server.setAttribute(objectName, new Attribute
+         ("useShutdownHook", new Boolean(false)));
+      server.setAttribute(objectName, new Attribute
+         ("await", new Boolean(false)));
+      server.setAttribute(objectName, new Attribute
+         ("redirectStreams", new Boolean(false)));
+
+      server.invoke(objectName, "create", new Object[]{},
+         new String[]{});
+
+      server.invoke(objectName, "start", new Object[]{},
+         new String[]{});
+
+      // Configure any SingleSignOn valves      
+      
+      ObjectName ssoQuery = new ObjectName("*:type=Valve,*");
+      Iterator iterator = server.queryMBeans(ssoQuery, null).iterator();
+      while (iterator.hasNext())
+      {
+         ObjectName ssoObjectName =
+            ((ObjectInstance) iterator.next()).getObjectName();
+         String name = ssoObjectName.getKeyProperty("name");
+         
+         /* Ensure that the SingleSignOn valve requires that each
+            request be reauthenticated to the security mgr. Should not
+            be neccessary now that we cache the principal in the session.
+         if ((name != null) && (name.indexOf("SingleSignOn") >= 0))
+         {
+            log.info("Turning on reauthentication of each request on " +
+                     ssoObjectName);
+            server.setAttribute(ssoObjectName, new Attribute
+               ("requireReauthentication", Boolean.TRUE));
+         }
+         */
+            
+         // If the valve is a ClusteredSingleSignOn and we have a shared
+         // TreeCache configured, configure the valve to use the shared one
+         if (cacheName != null && "ClusteredSingleSignOn".equals(name))
+         {
+            String tcName = (String) server.getAttribute(ssoObjectName,
+               "treeCacheName");
+            tcName = (tcName != null ? tcName : DEFAULT_CACHE_NAME);
+            ObjectName ssoCacheName = new ObjectName(tcName);
+            // Only override if the valve's cacheName property was not
+            // explicitly set in server.xml to a non-default value
+            if (ssoCacheName.equals(new ObjectName(DEFAULT_CACHE_NAME)))
+            {
+               log.info("Setting the cache name to " + cacheName +
+                  " on " + ssoObjectName);
+               server.setAttribute(ssoObjectName,
+                  new Attribute("treeCacheName", cacheName));
+            }
+         }
+      }
+
+      // Register the web container JACC PolicyContextHandlers
+      HttpServletRequestPolicyContextHandler handler = new HttpServletRequestPolicyContextHandler();
+      PolicyContext.registerHandler(HttpServletRequestPolicyContextHandler.WEB_REQUEST_KEY,
+         handler, true);
+
+      // The ServiceController used to control web app startup dependencies
+      serviceController = (ServiceControllerMBean)
+         MBeanProxyExt.create(ServiceControllerMBean.class, ServiceControllerMBean.OBJECT_NAME, server);
+
+      // make a proxy to myself, so that calls from the MainDeployer
+      // can go through the MBeanServer, so interceptors can be added
+      thisProxy = (SubDeployerExt)
+         MBeanProxyExt.create(SubDeployerExt.class, super.getServiceName(), super.getServer());
+
+      // Register with the main deployer
+      mainDeployer.addDeployer(thisProxy);
+
+      // If we are hot-deployed *after* the overall server is started
+      // we'll never receive Server.START_NOTIFICATION_TYPE, so check
+      // with the Server and start the connectors immediately, if this is the case.
+      // Otherwise register to receive the server start-up notification.
+      Boolean started = (Boolean)server.getAttribute(ServerImplMBean.OBJECT_NAME, "Started");
+      if (started.booleanValue() == true)
+      {
+         log.debug("Server '" + ServerImplMBean.OBJECT_NAME +
+               "' already started, starting connectors now");         
+         
+         startConnectors();
+      }
+      else
+      {
+         // Register for notification of the overall server startup
+         log.debug("Server '" + ServerImplMBean.OBJECT_NAME +
+               "' not started, registering for start-up notification");
+         
+         server.addNotificationListener(ServerImplMBean.OBJECT_NAME, this, null, null);         
+      }
+   }
+
+
+   public void stopService()
+      throws Exception
+   {
+
+      String objectNameS = catalinaDomain + ":type=server";
+      ObjectName objectName = new ObjectName(objectNameS);
+
+      server.invoke(objectName, "stop", new Object[]{},
+         new String[]{});
+
+      server.invoke(objectName, "destroy", new Object[]{},
+         new String[]{});
+
+      server.unregisterMBean(objectName);
+
+      MBeanServer server2 = server;
+
+      // deregister with MainDeployer
+      mainDeployer.removeDeployer(thisProxy);
+
+      // Unregister any remaining jboss.web or Catalina MBeans
+      ObjectName queryObjectName = new ObjectName
+         (catalinaDomain + ":*");
+      Iterator iterator =
+         server2.queryMBeans(queryObjectName, null).iterator();
+      while (iterator.hasNext())
+      {
+         ObjectInstance oi = (ObjectInstance) iterator.next();
+         ObjectName toRemove = oi.getObjectName();
+         // Exception: Don't unregister the service right now
+         if (!"WebServer".equals(toRemove.getKeyProperty("service")))
+         {
+            if (server2.isRegistered(toRemove))
+            {
+               server2.unregisterMBean(toRemove);
+            }
+         }
+      }
+      queryObjectName = new ObjectName("Catalina:*");
+      iterator = server2.queryMBeans(queryObjectName, null).iterator();
+      while (iterator.hasNext())
+      {
+         ObjectInstance oi = (ObjectInstance) iterator.next();
+         ObjectName name = oi.getObjectName();
+         server2.unregisterMBean(name);
+      }
+
+   }
+
+   public void startConnectors() throws Exception
+   { 
+      ObjectName service = new ObjectName(catalinaDomain + ":type=Service,serviceName=jboss.web");
+      Object[] args = {};
+      String[] sig = {};
+      Connector[] connectors = (Connector[]) server.invoke(service,
+         "findConnectors", args, sig); 
+      for (int n = 0; n < connectors.length; n++)
+      {
+         Lifecycle lc = (Lifecycle) connectors[n];
+         lc.start();
+      }
+      
+      //There may be a need to start the connectors that are defined in
+      //the multiple services in tomcat server.xml
+      startAllConnectors();
+      
+      // Notify listeners that connectors have started processing requests
+      sendNotification(new Notification(TOMCAT_CONNECTORS_STARTED,
+            this, getNextNotificationSequenceNumber()));
+   }
+
+   public void stopConnectors() throws Exception
+   {
+      ObjectName service = new ObjectName(catalinaDomain + ":type=Service,serviceName=jboss.web");
+      Object[] args = {};
+      String[] sig = {};
+      Connector[] connectors = (Connector[]) server.invoke(service,
+         "findConnectors", args, sig);
+      for (int n = 0; n < connectors.length; n++)
+      {
+         Lifecycle lc = (Lifecycle) connectors[n];
+         lc.stop();
+      }
+      //There may be a need to stop the connectors that are defined in
+      //the multiple services in tomcat server.xml 
+      stopAllConnectors();
+   }
+
+   public void handleNotification(Notification msg, Object handback)
+   {
+      String type = msg.getType();
+      if (type.equals(Server.START_NOTIFICATION_TYPE))
+      {
+         log.debug("Saw " + type + " notification, starting connectors");
+         try
+         {
+            startConnectors();
+         }
+         catch (Exception e)
+         {
+            log.warn("Failed to startConnectors", e);
+         }
+      }
+   }
+
+   public AbstractWebDeployer getDeployer(DeploymentInfo di) throws Exception
+   {
+      ClassLoader loader = di.ucl;
+      Class deployerClass = loader.loadClass("org.jboss.web.tomcat.service.TomcatDeployer");
+      AbstractWebDeployer deployer = (AbstractWebDeployer) deployerClass.newInstance();
+      DeployerConfig config = new DeployerConfig();
+      config.setDefaultSecurityDomain(this.defaultSecurityDomain);
+      config.setSubjectAttributeName(this.subjectAttributeName);
+      config.setServiceClassLoader(getClass().getClassLoader());
+      config.setManagerClass(this.managerClass);
+      config.setJava2ClassLoadingCompliance(this.java2ClassLoadingCompliance);
+      config.setUnpackWars(this.unpackWars);
+      config.setLenientEjbLink(this.lenientEjbLink);
+      config.setCatalinaDomain(catalinaDomain);
+      config.setContextClassName(contextClassName);
+      config.setServiceName(serviceName);
+      config.setSnapshotInterval(this.snapshotInterval);
+      config.setSnapshotMode(this.snapshotMode);
+      config.setUseLocalCache(this.useLocalCache);
+      config.setUseJK(this.useJK);
+      config.setSubjectAttributeName(this.subjectAttributeName);
+      config.setUseJBossWebLoader(this.useJBossWebLoader);
+      config.setAllowSelfPrivilegedWebApps(this.allowSelfPrivilegedWebApps);
+      config.setSecurityManagerService(this.secMgrService);
+      config.setFilteredPackages(filteredPackages);
+      deployer.setServer(server);
+      deployer.init(config);
+      return deployer;
+   } 
+   
+   /**
+    * Start all the connectors
+    * 
+    * @throws JMException
+    * @throws LifecycleException
+    */
+   private void startAllConnectors() throws JMException, LifecycleException
+   {
+      /**
+       * Not able to query the Catalina server for the services that it has
+       * registered. A usuable solution is to query the MBean server for the
+       * tomcat services.
+       * http://www.jboss.com/index.html?module=bb&op=viewtopic&t=75353
+       */
+      ObjectName oname = new ObjectName("*:type=Service,*");
+      Set services = server.queryMBeans(oname,null);
+      Iterator iter = services.iterator();
+      while(iter.hasNext())
+      {
+         ObjectInstance oi = (ObjectInstance)iter.next();
+         ObjectName on = oi.getObjectName();  
+         //Ignore jboss.web:*
+         if(this.catalinaDomain.equals(on.getDomain()) )
+               continue;
+         String key = on.getKeyProperty("serviceName");
+         if(key != null)
+         {
+            Connector[] connectors = (Connector[]) server.invoke(on,
+                  "findConnectors", new Object[0], new String[0]); 
+               for (int n = 0; n < connectors.length; n++)
+               {
+                  Lifecycle lc = (Lifecycle) connectors[n];
+                  lc.start();
+               }
+         }
+      }
+   }
+   
+   /**
+    * Stop all the connectors
+    * 
+    * @throws JMException
+    * @throws LifecycleException
+    */
+   private void stopAllConnectors() throws JMException, LifecycleException
+   { 
+      ObjectName oname = new ObjectName("*:type=Service,*");
+      Set services = server.queryMBeans(oname,null);
+      Iterator iter = services.iterator();
+      while(iter.hasNext())
+      {
+         ObjectInstance oi = (ObjectInstance)iter.next();
+         ObjectName on = oi.getObjectName();  
+         //Ignore jboss.web:*
+         if(this.catalinaDomain.equals(on.getDomain()) )
+               continue;
+         String key = on.getKeyProperty("serviceName");
+         if(key != null)
+         {
+            Connector[] connectors = (Connector[]) server.invoke(on,
+                  "findConnectors", new Object[0], new String[0]); 
+               for (int n = 0; n < connectors.length; n++)
+               {
+                  Lifecycle lc = (Lifecycle) connectors[n];
+                  lc.stop();
+               }
+         }
+      }
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,216 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import org.jboss.web.AbstractWebContainerMBean;
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+
+/**
+ * An implementation of the AbstractWebContainer for the Jakarta Tomcat5
+ * servlet container. It has no code dependency on tomcat - only the new JMX
+ * model is used.
+ * <p/>
+ * Tomcat5 is organized as a set of mbeans - just like jboss.
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @version $Revision: 57206 $
+ * @see AbstractWebContainerMBean
+ */
+public interface JBossWebMBean extends AbstractWebContainerMBean
+{
+   /** JMX notification type to signal after-start connector event */ 
+   public final String TOMCAT_CONNECTORS_STARTED  = "jboss.tomcat.connectors.started";
+   
+   /**
+    * @return the jmx domain for the tomcat management mbeans
+    */
+   public String getDomain();
+
+   /**
+    * The most important attribute - defines the managed domain.
+    * A catalina instance (engine) corresponds to a JMX domain, that's
+    * how we know where to deploy webapps.
+    *
+    * @param domainName the jmx domain under which tc registers
+    */
+   public void setDomain(String domainName);
+
+   /**
+    * Set the snapshot mode in a clustered environment
+    */
+   public void setSnapshotMode(String mode);
+
+   /**
+    * Get the snapshot mode in a clustered environment
+    */
+   public String getSnapshotMode();
+
+   /**
+    * Set the snapshot interval in ms for the interval snapshot mode
+    */
+   public void setSnapshotInterval(int interval);
+
+   /**
+    * Get the snapshot interval
+    */
+   public int getSnapshotInterval();
+
+   /**
+    * Get the clustering code cache behaviour
+    */
+   public boolean isUseLocalCache();
+
+   /**
+    * Set the clustering code cache behaviour
+    */
+   public void setUseLocalCache(boolean useLocalCache);
+
+   /**
+    * Get the clustering code failover behaviour whether MOD_JK(2) is used or not.
+    */
+   public boolean isUseJK();
+
+   /**
+    * Set the clustering code failover behaviour whether MOD_JK(2) is used or not.
+    */
+   public void setUseJK(boolean useJK);
+
+   /**
+    * The SessionIdAlphabet is the set of characters used to create a session Id
+    */
+   public void setSessionIdAlphabet(String sessionIdAlphabet);
+
+   /**
+    * The SessionIdAlphabet is the set of characters used to create a session Id
+    */
+   public String getSessionIdAlphabet();
+   
+   /**
+    * Gets the JMX object name of a shared TreeCache to be used for clustered
+    * single-sign-on.
+    *
+    * @see org.jboss.web.tomcat.tc5.sso.TreeCacheSSOClusterManager
+    */
+   public String getCacheName();
+
+   /**
+    * Gets the JMX object name of a shared TreeCache to be used for clustered
+    * single-sign-on.
+    * <p/>
+    * <b>NOTE:</b> TreeCache must be deployed before this service.
+    *
+    * @see org.jboss.web.tomcat.tc5.sso.TreeCacheSSOClusterManager
+    */
+   public void setCacheName(String cacheName);
+
+   /**
+    * Get the JBoss UCL use flag
+    */
+   public boolean getUseJBossWebLoader();
+
+   /**
+    * Set the JBoss UCL use flag
+    */
+   public void setUseJBossWebLoader(boolean flag);
+
+   public String getManagerClass();
+
+   public void setManagerClass(String managerClass);
+
+   /** */
+   public String getContextMBeanCode();
+
+   /** */
+   public void setContextMBeanCode(String className);
+
+   /**
+    * Get the name of the external tomcat server configuration file.
+    *
+    * @return the config file name, server.xml for example
+    */
+   public String getConfigFile();
+
+   /**
+    * Set the name of the external tomcat server configuration file.
+    *
+    * @param configFile - the config file name, server.xml for example
+    */
+   public void setConfigFile(String configFile);
+
+   /**
+    * Get the request attribute name under which the JAAS Subject is store
+    */
+   public String getSubjectAttributeName();
+
+   /**
+    * Set the request attribute name under which the JAAS Subject will be
+    * stored when running with a security mgr that supports JAAS. If this is
+    * empty then the Subject will not be store in the request.
+    *
+    * @param name the HttpServletRequest attribute name to store the Subject
+    */
+   public void setSubjectAttributeName(String name);
+
+   /**
+    * Start all connectors of the Domain + ":type=Service,serviceName=jboss.web"
+    * service.
+    *
+    * @throws Exception
+    */
+   public void startConnectors() throws Exception;
+
+   /**
+    * Stop all connectors of the Domain + ":type=Service,serviceName=jboss.web"
+    * service.
+    *
+    * @throws Exception
+    */
+   public void stopConnectors() throws Exception;
+
+   /**
+    * Get whether web-apps are able to control the privileged flag
+    */
+   public boolean isAllowSelfPrivilegedWebApps();
+
+   /**
+    * Set whether web-apps are able to control the privileged flag
+    */
+   public void setAllowSelfPrivilegedWebApps(boolean flag);
+
+   /** Set the SecurityManagerService binding. This is used to flush any
+    * associated authentication cache on session invalidation.
+    * @param mgr the JaasSecurityManagerServiceMBean
+    */ 
+   public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr);
+
+   /**
+    * 
+    * @return
+    */ 
+   public String[] getFilteredPackages();
+   /**
+    * 
+    * @param pkgs
+    */ 
+   public void setFilteredPackages(String[] pkgs);
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,204 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+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;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Session;
+import org.jboss.logging.Logger;
+import org.jboss.util.NestedRuntimeException;
+import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+
+/**
+ * Web request filter to specifically handle Tomcat jvmRoute using mod_jk(2)
+ * module. We assume that the session is set by cookie only for now, i.e., no
+ * support of that from URL. Furthermore, the session id has a format of
+ * id.jvmRoute where jvmRoute is used by JK module to determine sticky session
+ * during load balancing.
+ *
+ * @author Ben Wang
+ * @author Marco Antonioni
+ * @version $Revision: 57206 $
+ * @deprecated 4.0.3
+ */
+public class JvmRouteFilter
+   implements Filter
+{
+   protected AbstractJBossManager manager_;
+   protected static Logger log_ = Logger.getLogger(JvmRouteFilter.class);
+
+   public void init(FilterConfig filterConfig) throws ServletException
+   {
+      if (log_.isDebugEnabled())
+      {
+         ServletContext sc = filterConfig.getServletContext();
+         Enumeration names = sc.getAttributeNames();
+         while (names.hasMoreElements())
+         {
+            String name = (String) names.nextElement();
+            Object value = sc.getAttribute(name);
+            log_.debug("name=" + name + ", value.className: [" + value.getClass().getName() + "] value.toString: [" + value.toString() + "]");
+         }
+      }
+      manager_ = (AbstractJBossManager) filterConfig.getServletContext().getAttribute("AbstractJBossManager");
+      if (manager_ == null)
+      {
+         throw new RuntimeException("JvmRouteFilter.init(): No AbstractJBossManager found for clustering support.");
+      }
+
+      if (log_.isDebugEnabled())
+         log_.debug("JvmRouteFilter.init(): initializing JvmRouteFilter");
+   }
+
+   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+      throws IOException, ServletException
+   {
+      // Check if request and response is valid
+      if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse))
+      {
+         // Don't know how to handle. There is a mistake.
+         throw new RuntimeException("JvmRouteFilter.doFilter(): Not a http request and response type.");
+      }
+
+      // get session id
+      HttpServletRequest req = (HttpServletRequest) request;
+      HttpServletResponse res = (HttpServletResponse) response;
+      HttpSession session = req.getSession(false);
+      if (session != null)
+      {
+         String sessionId = session.getId();
+   
+         // Obtain JvmRoute
+         String jvmRoute = manager_.getJvmRoute();
+         if (log_.isDebugEnabled())
+         {
+            log_.debug("doFilter(): check if need to re-route based on JvmRoute. Session id: " +
+               sessionId + " jvmRoute: " + jvmRoute);
+         }
+
+         if (jvmRoute == null)
+         {
+            throw new RuntimeException("JvmRouteFilter.doFilter(): Tomcat JvmRoute is null. " +
+               "Need to assign a value in Tomcat server.xml for load balancing.");
+         }
+   
+         // Check if incoming session id has JvmRoute appended. If not, append it.
+         // TODO. We handle only get session id by cookie only
+         if (req.isRequestedSessionIdFromURL())
+         {
+            // Warning but do nothing
+            log_.error("JvmRouteFilter.doFilter(): Can't handle clustering where session id is from URL. Will skip.");
+         }
+         else
+         {
+            handleJvmRoute(sessionId, jvmRoute, res);
+         }
+      }
+      chain.doFilter(request, response);
+   }
+
+   protected void handleJvmRoute(String sessionId, String jvmRoute, HttpServletResponse response)
+   {
+      // get requested jvmRoute.
+      // TODO. The current format is assumed to be id.jvmRoute. Can be generalized later.
+      String requestedJvmRoute = null;
+      int ind = sessionId.indexOf(".");
+      if (ind > 0)
+      {
+         requestedJvmRoute = sessionId.substring(sessionId.indexOf(".") + 1, sessionId.length());
+      }
+
+      String sid = sessionId;
+      if (requestedJvmRoute == null)
+      {
+         // If this filter is turned on, we assume we have an appendix of jvmRoute. So this request is new.
+         sid = sessionId + "." + jvmRoute;
+         manager_.setNewSessionCookie(sid, response);
+      }
+      else if (requestedJvmRoute.equals(jvmRoute))
+      {
+         return;  // Nothing more needs to be done.
+      }
+      else
+      {
+         // We just have a failover since jvmRoute does not match. We will replace the old one with the new one.
+         if (log_.isDebugEnabled())
+         {
+            log_.debug("handleJvmRoute(): We have detected a failover with differen jvmRoute." +
+               " old one: " + requestedJvmRoute + " new one: " + jvmRoute + ". Will reset the session id.");
+         }
+         int index = sessionId.indexOf(".");
+         if (index > 0)
+         {
+            String base = sessionId.substring(0, sessionId.indexOf("."));
+            sid = base + "." + jvmRoute;
+         }
+         else
+         {
+            throw new RuntimeException("JvmRouteFilter.handleJvmRoute(): session id doesn't JvmRoute.");
+         }
+
+         manager_.setNewSessionCookie(sid, response);
+         // Change the sessionId with the new one using local jvmRoute
+         Session catalinaSession = null;
+         try
+         {
+            catalinaSession = manager_.findSession(sessionId);
+            // change session id with the new one using local jvmRoute and update cluster if needed.
+            if( catalinaSession != null )
+            {
+               catalinaSession.setId(sid);
+               if (log_.isDebugEnabled())
+               {
+                  log_.debug("handleJvmRoute(): changed catalina session to= [" + sid + "] old one= [" + sessionId + "]");
+               }
+            }
+         }
+         catch (IOException e)
+         {
+            if (log_.isDebugEnabled())
+            {
+               log_.debug("handleJvmRoute(): manager_.findSession() unable to find session= [" + sessionId + "]", e);
+            }
+            throw new NestedRuntimeException("JvmRouteFilter.handleJvmRoute(): cannot find session [" + sessionId + "]", e);
+         }
+      }
+   }
+
+
+   public void destroy()
+   {
+      manager_ = null;
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,749 @@
+/*
+ * 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.
+ */
+/*
+ * JBoss, the OpenSource WebOS
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.web.tomcat.service;
+
+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.core.Constants;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.logging.Logger;
+
+
+/**
+ * Standard implementation of the <code>Service</code> interface.  The
+ * associated Container is generally an instance of Engine, but this is
+ * not required.
+ * <p/>
+ * This version does not start the connectors in the start method so that
+ * all web apps may be completely deployed before enabling the transport
+ * layer.
+ *
+ * @author Craig R. McClanahan
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 30190 $
+ */
+public class StandardService
+   implements Lifecycle, Service, MBeanRegistration
+{
+   private static Logger log = Logger.getLogger(StandardService.class);
+
+   // ----------------------------------------------------- Instance Variables
+
+
+   /**
+    * The set of Connectors associated with this Service.
+    */
+   private Connector connectors[] = new Connector[0];
+
+
+   /**
+    * The Container associated with this Service.
+    */
+   private Container container = null;
+
+
+   /**
+    * The debugging detail level for this component.
+    */
+   private int debug = 0;
+
+
+   /**
+    * Descriptive information about this component implementation.
+    */
+   private static final String info =
+      "org.jboss.web.tomcat.service.StandardService/1.0";
+
+
+   /**
+    * Has this component been initialized?
+    */
+   private boolean initialized = false;
+
+
+   /**
+    * 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);
+
+
+   // ------------------------------------------------------------- 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 the debugging detail level of this component.
+    */
+   public int getDebug()
+   {
+
+      return (this.debug);
+
+   }
+
+
+   /**
+    * Set the debugging detail level of this component.
+    *
+    * @param debug The new debugging detail level
+    */
+   public void setDebug(int debug)
+   {
+
+      this.debug = debug;
+
+   }
+
+
+   /**
+    * 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)
+            {
+               e.printStackTrace(System.err);
+            }
+         }
+
+         if (started && (connector instanceof Lifecycle))
+         {
+            try
+            {
+               ((Lifecycle) connector).start();
+            }
+            catch (LifecycleException 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)
+            {
+               ;
+            }
+         }
+         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.
+    *
+    * @throws 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("standardService.start.started"));
+      }
+
+      if (!initialized)
+         init(); 
+
+      // Notify our interested LifecycleListeners
+      lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+      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.
+    *
+    * @throws 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);
+
+      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();
+            }
+         }
+      }
+
+      // 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)
+      {
+         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("Error registering ", 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();
+      // unregister should be here probably
+   }
+
+   public void init()
+   {
+      try
+      {
+         initialize();
+      }
+      catch (Throwable t)
+      {
+         t.printStackTrace();
+      }
+   }
+
+   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()
+   {
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,388 @@
+/*
+ * 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.jboss.web.tomcat.service;
+
+
+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.tomcat.util.modeler.Registry;
+
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.manager.Constants;
+import org.apache.catalina.manager.StatusTransformer;
+
+/**
+ * This servlet will display a complete status of the HTTP/1.1 connector.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 41072 $ $Date: 2006-02-09 08:47:17 +0100 (jeu., 09 févr. 2006) $
+ */
+
+public class StatusServlet
+   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.getServer();
+
+      // 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
+    * @throws IOException      if an input/output error occurs
+    * @throws 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 ("true".equals(request.getParameter("XML")))
+      {
+         mode = 1;
+      }
+      StatusTransformer.setContentType(response, mode);
+
+      PrintWriter writer = response.getWriter();
+
+      boolean completeStatus = false;
+      if ("true".equals(request.getParameter("full")))
+      {
+         completeStatus = true;
+      }
+        
+      // use StatusTransformer to output status
+      if (mode == 0)
+      {
+         // HTML Header Section
+         writer.print(HTML_HEADER);
+      }
+      else if (mode == 1)
+      {
+         writer.write(Constants.XML_DECLARATION);
+         writer.write
+            (Constants.XML_STYLE);
+         writer.write("<status>");
+      }
+
+      try
+      {
+
+         // Display virtual machine statistics
+         StatusTransformer.writeVMState(writer, mode);
+
+         Enumeration i = threadPools.elements();
+         while (i.hasMoreElements())
+         {
+            ObjectName objectName = (ObjectName) i.nextElement();
+            String name = objectName.getKeyProperty("name");
+            // use StatusTransformer to output status
+            StatusTransformer.writeConnectorState
+               (writer, objectName,
+                  name, mBeanServer, globalRequestProcessors,
+                  requestProcessors, mode);
+         }
+
+         if (completeStatus)
+         {
+            // 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);
+      }
+
+      if (mode == 0)
+      {
+         writer.print(HTML_FOOTER);
+      }
+      else if (mode == 1)
+      {
+         writer.write("</status>");
+      }
+
+   }
+
+   // ------------------------------------------- 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)
+            {
+
+            }
+         }
+      }
+
+   }
+
+
+   // ------------------------------------------------------- Private Constats
+
+
+   private static final String HTML_HEADER =
+      "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
+      + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+      + "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+      + "<head>"
+      + "<title>Tomcat Status</title>"
+      + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />"
+      + "<link rel=\"StyleSheet\" href=\"jboss.css\" type=\"text/css\"/>"
+      + "</head>"
+      + "<body>"
+      + "<!-- header begin -->"
+      + "<a href=\"http://www.jboss.org\">"
+      + "<img src=\"logo.gif\" alt=\"JBoss\" id=\"logo\" width=\"226\" height=\"105\" />"
+      + "</a>"
+      + "<div id=\"header\">&nbsp;</div>"
+      + "<div id=\"navigation_bar\">"
+      + "</div>"
+      + "<!-- header end -->";
+
+   private static final String HTML_FOOTER =
+      "<!-- footer begin -->"
+      + "<div id=\"footer\">"
+      + "<div id=\"credits\">JBoss&trade; Application Server</div>"
+      + "<div id=\"footer_bar\">&nbsp;</div>"
+      + "</div>"
+      + "<!-- footer end -->"
+      + "</body>"
+      + "</html>";
+
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,553 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.cert.Certificate;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.management.Attribute;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Loader;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployment.DeploymentException;
+import org.jboss.deployment.DeploymentInfo;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.web.AbstractWebContainer;
+import org.jboss.web.AbstractWebDeployer;
+import org.jboss.web.WebApplication;
+import org.jboss.web.tomcat.security.CustomPrincipalValve;
+import org.jboss.web.tomcat.security.JaccContextValve;
+import org.jboss.web.tomcat.security.RunAsListener;
+import org.jboss.web.tomcat.security.SecurityAssociationValve;
+import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+import org.jboss.web.tomcat.service.session.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.JBossCacheManager;
+
+/**
+ * The tomcat web application deployer
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @version $Revision: 57206 $
+ */
+public class TomcatDeployer extends AbstractWebDeployer
+{
+   /**
+    * The name of the war level context configuration descriptor
+    */
+   private static final String CONTEXT_CONFIG_FILE = "WEB-INF/context.xml";
+
+   private DeployerConfig config;
+   private String[] javaVMs =
+      {" jboss.management.local:J2EEServer=Local,j2eeType=JVM,name=localhost"};
+   private String serverName = "jboss";
+   private HashMap vhostToHostNames = new HashMap();
+
+   public void init(Object containerConfig) throws Exception
+   {
+      this.config = (DeployerConfig) containerConfig;
+      super.setJava2ClassLoadingCompliance(config.isJava2ClassLoadingCompliance());
+      super.setUnpackWars(config.isUnpackWars());
+      super.setLenientEjbLink(config.isLenientEjbLink());
+      super.setDefaultSecurityDomain(config.getDefaultSecurityDomain());
+   }
+
+   /**
+    * Perform the tomcat specific deployment steps.
+    */
+   protected void performDeploy(WebApplication appInfo, String warUrl,
+      AbstractWebContainer.WebDescriptorParser webAppParser)
+      throws Exception
+   {
+      WebMetaData metaData = appInfo.getMetaData();
+      String hostName = null;
+      // Get any jboss-web/virtual-hosts
+      Iterator vhostNames = metaData.getVirtualHosts();
+      // Map the virtual hosts onto the configured hosts
+      Iterator hostNames = mapVirtualHosts(vhostNames);
+      if (hostNames.hasNext())
+      {
+         hostName = hostNames.next().toString();
+      }
+      performDeployInternal(hostName, appInfo, warUrl, webAppParser);
+      while (hostNames.hasNext())
+      {
+         String additionalHostName = hostNames.next().toString();
+         performDeployInternal(additionalHostName, appInfo, warUrl, webAppParser);
+      }
+   }
+
+   protected void performDeployInternal(String hostName,
+      WebApplication appInfo, String warUrl,
+      AbstractWebContainer.WebDescriptorParser webAppParser)
+      throws Exception
+   {
+
+      WebMetaData metaData = appInfo.getMetaData();
+      String ctxPath = metaData.getContextRoot();
+      if (ctxPath.equals("/") || ctxPath.equals("/ROOT") || ctxPath.equals(""))
+      {
+         log.debug("deploy root context=" + ctxPath);
+         ctxPath = "/";
+         metaData.setContextRoot(ctxPath);
+      }
+
+      log.info("deploy, ctxPath=" + ctxPath + ", warUrl=" + shortWarUrlFromServerHome(warUrl));
+
+      URL url = new URL(warUrl);
+
+      ClassLoader loader = Thread.currentThread().getContextClassLoader();
+      /* If we are using the jboss class loader we need to augment its path
+      to include the WEB-INF/{lib,classes} dirs or else scoped class loading
+      does not see the war level overrides. The call to setWarURL adds these
+      paths to the deployment UCL.
+      */
+      Loader webLoader = null;
+      if (config.isUseJBossWebLoader())
+      {
+         WebCtxLoader jbossLoader = new WebCtxLoader(loader);
+         jbossLoader.setWarURL(url);
+         webLoader = jbossLoader;
+      }
+      else
+      {
+         String[] pkgs = config.getFilteredPackages();
+         WebAppLoader jbossLoader = new WebAppLoader(loader, pkgs);
+         jbossLoader.setDelegate(getJava2ClassLoadingCompliance());
+         webLoader = jbossLoader;
+      }
+
+      // We need to establish the JNDI ENC prior to the start 
+      // of the web container so that init on startup servlets are able 
+      // to interact with their ENC. We hook into the context lifecycle 
+      // events to be notified of the start of the
+      // context as this occurs before the servlets are started.
+      if (appInfo.getAppData() == null)
+         webAppParser.parseWebAppDescriptors(loader, appInfo.getMetaData());
+
+      appInfo.setName(url.getPath());
+      appInfo.setClassLoader(loader);
+      appInfo.setURL(url);
+
+      String objectNameS = config.getCatalinaDomain()
+         + ":j2eeType=WebModule,name=//" +
+         ((hostName == null) ? "localhost" : hostName)
+         + ctxPath + ",J2EEApplication=none,J2EEServer=none";
+
+      ObjectName objectName = new ObjectName(objectNameS);
+
+      if (server.isRegistered(objectName))
+      {
+         log.debug("Already exists, destroying " + objectName);
+         server.invoke(objectName, "destroy", new Object[]{},
+            new String[]{});
+      }
+
+      Registry.getRegistry().registerComponent(Class.forName(config.getContextClassName()).newInstance(), 
+              objectName, "org.apache.catalina.core.StandardContext");
+
+      // Find and set config file on the context
+      // If WAR is packed, expand config file to temp folder
+      String ctxConfig = null;
+      try
+      {
+         ctxConfig = findConfig(url);
+      }
+      catch (IOException e)
+      {
+         log.debug("No " + CONTEXT_CONFIG_FILE + " in " + url, e);
+      }
+
+      server.setAttribute(objectName, new Attribute("docBase", url.getFile()));
+
+      server.setAttribute(objectName, new Attribute("configFile", ctxConfig));
+
+      server.setAttribute(objectName, new Attribute
+         ("defaultContextXml", "context.xml"));
+      server.setAttribute(objectName, new Attribute
+         ("defaultWebXml", "conf/web.xml"));
+
+      server.setAttribute(objectName, new Attribute("javaVMs", javaVMs));
+
+      server.setAttribute(objectName, new Attribute("server", serverName));
+
+      server.setAttribute(objectName, new Attribute
+         ("saveConfig", Boolean.FALSE));
+
+      if (webLoader != null)
+      {
+         server.setAttribute(objectName, new Attribute
+            ("loader", webLoader));
+      }
+      else
+      {
+         server.setAttribute(objectName, new Attribute
+            ("parentClassLoader", loader));
+      }
+
+      server.setAttribute(objectName, new Attribute
+         ("delegate", new Boolean(getJava2ClassLoadingCompliance())));
+
+      String[] jspCP = getCompileClasspath(loader);
+      StringBuffer classpath = new StringBuffer();
+      for (int u = 0; u < jspCP.length; u++)
+      {
+         String repository = jspCP[u];
+         if (repository == null)
+            continue;
+         if (repository.startsWith("file://"))
+            repository = repository.substring(7);
+         else if (repository.startsWith("file:"))
+            repository = repository.substring(5);
+         else
+            continue;
+         if (repository == null)
+            continue;
+         // ok it is a file.  Make sure that is is a directory or jar file
+         File fp = new File(repository);
+         if (!fp.isDirectory())
+         {
+            // if it is not a directory, try to open it as a zipfile.
+            try
+            {
+               // avoid opening .xml files
+               if (fp.getName().toLowerCase().endsWith(".xml"))
+                  continue;
+               
+               ZipFile zip = new ZipFile(fp);
+               zip.close();
+            }
+            catch (IOException e)
+            {
+               continue;
+            }
+
+         }
+         if (u > 0)
+            classpath.append(File.pathSeparator);
+         classpath.append(repository);
+      }
+
+      server.setAttribute(objectName, new Attribute
+         ("compilerClasspath", classpath.toString()));
+
+      // Set the session cookies flag according to metadata
+      switch (metaData.getSessionCookies())
+      {
+         case WebMetaData.SESSION_COOKIES_ENABLED:
+            server.setAttribute(objectName, new Attribute
+               ("cookies", new Boolean(true)));
+            log.debug("Enabling session cookies");
+            break;
+         case WebMetaData.SESSION_COOKIES_DISABLED:
+            server.setAttribute(objectName, new Attribute
+               ("cookies", new Boolean(false)));
+            log.debug("Disabling session cookies");
+            break;
+         default:
+            log.debug("Using session cookies default setting");
+      }
+
+      // Add a valve to estalish the JACC context before authorization valves
+      Certificate[] certs = null;
+      CodeSource cs = new CodeSource(url, certs);
+      JaccContextValve jaccValve = new JaccContextValve(metaData.getJaccContextID(), cs);
+      server.invoke(objectName, "addValve",
+         new Object[]{jaccValve},
+         new String[]{"org.apache.catalina.Valve"});
+
+      // Pass the metadata to the RunAsListener via a thread local
+      RunAsListener.metaDataLocal.set(metaData);
+
+      try
+      {
+         // Init the container; this will also start it
+         server.invoke(objectName, "init", new Object[]{}, new String[]{});
+      }
+      finally
+      {
+         RunAsListener.metaDataLocal.set(null);
+      }
+
+      // make the context class loader known to the WebMetaData, ws4ee needs it
+      // to instanciate service endpoint pojos that live in this webapp
+      Loader ctxLoader = (Loader)server.getAttribute(objectName, "loader");
+      metaData.setContextLoader(ctxLoader.getClassLoader());
+
+      // Clustering
+      if (metaData.getDistributable())
+      {
+         // Try to initate clustering, fallback to standard if no clustering is available
+         try
+         {
+            AbstractJBossManager manager = null;
+            String managerClassName = config.getManagerClass();
+            Class managerClass = Thread.currentThread().getContextClassLoader().loadClass(managerClassName);
+            manager = (AbstractJBossManager) managerClass.newInstance();
+
+            if (manager instanceof JBossCacheManager)
+            {        
+               // TODO either deprecate snapshot mode or move its config
+               // into jboss-web.xml.
+               String snapshotMode = config.getSnapshotMode();
+               int snapshotInterval = config.getSnapshotInterval();
+               JBossCacheManager jbcm = (JBossCacheManager) manager;
+               jbcm.setSnapshotMode(snapshotMode);
+               jbcm.setSnapshotInterval(snapshotInterval);
+            }
+            
+            String name = "//" + ((hostName == null) ? "localhost" : hostName) + ctxPath;
+            manager.init(name, metaData, config.isUseJK(), config.isUseLocalCache());
+            
+            // Don't assign the manager to the context until all config
+            // is done, or else the manager will be started without the config
+            server.setAttribute(objectName, new Attribute("manager", manager));
+            
+            log.debug("Enabled clustering support for ctxPath=" + ctxPath);
+         }
+         catch (ClusteringNotSupportedException e)
+         {
+            // JBAS-3513 Just log a WARN, not an ERROR
+            log.warn("Failed to setup clustering, clustering disabled. ClusteringNotSupportedException: " + 
+                  e.getMessage());
+         }
+         catch (NoClassDefFoundError ncdf)
+         {
+            // JBAS-3513 Just log a WARN, not an ERROR
+            log.debug("Classes needed for clustered webapp unavailable", ncdf);
+            log.warn("Failed to setup clustering, clustering disabled. NoClassDefFoundError: " + 
+                  ncdf.getMessage());            
+         }
+         catch(Throwable t)
+         {
+            // TODO consider letting this through and fail the deployment
+            log.error("Failed to setup clustering, clustering disabled. Exception: ", t);
+         }
+      }
+
+      /* Add security association valve after the authorization
+      valves so that the authenticated user may be associated with the
+      request thread/session.
+      */
+      SecurityAssociationValve valve = new SecurityAssociationValve(metaData,
+         config.getSecurityManagerService());
+      valve.setSubjectAttributeName(config.getSubjectAttributeName());
+      server.invoke(objectName, "addValve",
+         new Object[]{valve},
+         new String[]{"org.apache.catalina.Valve"});
+
+      // Retrieve the state, and throw an exception in case of a failure
+      Integer state = (Integer) server.getAttribute(objectName, "state");
+      if (state.intValue() != 1)
+      {
+         throw new DeploymentException("URL " + warUrl + " deployment failed");
+      }
+
+      appInfo.setAppData(objectName);
+
+      // Create mbeans for the servlets
+      DeploymentInfo di = webAppParser.getDeploymentInfo();
+      di.deployedObject = objectName;
+      ObjectName servletQuery = new ObjectName
+         (config.getCatalinaDomain() + ":j2eeType=Servlet,WebModule="
+         + objectName.getKeyProperty("name") + ",*");
+      Iterator iterator = server.queryMBeans(servletQuery, null).iterator();
+      while (iterator.hasNext())
+      {
+         di.mbeans.add(((ObjectInstance) iterator.next()).getObjectName());
+      }
+
+      log.debug("Initialized: " + appInfo + " " + objectName);
+
+   }
+
+
+   /**
+    * Called as part of the undeploy() method template to ask the
+    * subclass for perform the web container specific undeployment steps.
+    */
+   protected void performUndeploy(String warUrl, WebApplication appInfo)
+      throws Exception
+   {
+      if (appInfo == null)
+      {
+         log.debug("performUndeploy, no WebApplication found for URL "
+            + warUrl);
+         return;
+      }
+
+      log.info("undeploy, ctxPath=" + appInfo.getMetaData().getContextRoot()
+         + ", warUrl=" + shortWarUrlFromServerHome(warUrl));
+
+      WebMetaData metaData = appInfo.getMetaData();
+      String hostName = null;
+      Iterator hostNames = metaData.getVirtualHosts();
+      if (hostNames.hasNext())
+      {
+         hostName = hostNames.next().toString();
+      }
+      performUndeployInternal(hostName, warUrl, appInfo);
+      while (hostNames.hasNext())
+      {
+         String additionalHostName = hostNames.next().toString();
+         performUndeployInternal(additionalHostName, warUrl, appInfo);
+      }
+
+   }
+
+   protected void performUndeployInternal(String hostName, String warUrl,
+      WebApplication appInfo)
+      throws Exception
+   {
+
+      WebMetaData metaData = appInfo.getMetaData();
+      String ctxPath = metaData.getContextRoot();
+
+      // If the server is gone, all apps were stopped already
+      if (server == null)
+         return;
+
+      ObjectName objectName = new ObjectName(config.getCatalinaDomain()
+         + ":j2eeType=WebModule,name=//" +
+         ((hostName == null) ? "localhost" : hostName)
+         + ctxPath + ",J2EEApplication=none,J2EEServer=none");
+
+      if (server.isRegistered(objectName))
+      {
+         // Contexts should be stopped by the host already
+         server.invoke(objectName, "destroy", new Object[]{},
+            new String[]{});
+      }
+
+   }
+
+   /**
+    * Resolve the input virtual host names to the names of the configured Hosts
+    * @param vhostNames Iterator<String> for the jboss-web/virtual-host elements 
+    * @return Iterator<String> of the unique Host names
+    * @throws Exception
+    */ 
+   protected synchronized Iterator mapVirtualHosts(Iterator vhostNames)
+      throws Exception
+   {
+      if( vhostToHostNames.size() == 0 )
+      {
+         // Query the configured Host mbeans
+         String hostQuery = config.getCatalinaDomain() + ":type=Host,*";
+         ObjectName query = new ObjectName(hostQuery);
+         Set hosts = server.queryNames(query, null);
+         Iterator iter = hosts.iterator();
+         while( iter.hasNext() )
+         {
+            ObjectName host = (ObjectName) iter.next();
+            String name = host.getKeyProperty("host");
+            if( name != null )
+            {
+               vhostToHostNames.put(name, name);
+                String[] aliases = (String[])
+                    server.invoke(host, "findAliases", null, null);
+               int count = aliases != null ? aliases.length : 0;
+               for(int n = 0;n < count; n ++)
+               {
+                  vhostToHostNames.put(aliases[n], name);
+               }
+            }
+         }
+      }
+
+      // Map the virtual host names to the hosts
+      HashSet hosts = new HashSet();
+      while( vhostNames.hasNext() )
+      {
+         String vhost = (String) vhostNames.next();
+         String host = (String) vhostToHostNames.get(vhost);
+         if( host == null )
+         {
+            log.warn("Failed to map vhost: "+vhost);
+            // This will cause a new host to be created
+            host = vhost;
+         }
+         hosts.add(host);
+      }
+      return hosts.iterator();
+   }
+
+   private String findConfig(URL warURL) throws IOException
+   {
+      String result = null;
+      // See if the warUrl is a dir or a file
+      File warFile = new File(warURL.getFile());
+      if (warURL.getProtocol().equals("file") && warFile.isDirectory() == true)
+      {
+         File webDD = new File(warFile, CONTEXT_CONFIG_FILE);
+         if (webDD.exists() == true) result = webDD.getAbsolutePath();
+      }
+      else
+      {
+         ZipFile zipFile = new ZipFile(warFile);
+         ZipEntry entry = zipFile.getEntry(CONTEXT_CONFIG_FILE);
+         if (entry != null)
+         {
+            InputStream zipIS = zipFile.getInputStream(entry);
+            byte[] buffer = new byte[512];
+            int bytes;
+            result = warFile.getAbsolutePath() + "-context.xml";
+            FileOutputStream fos = new FileOutputStream(result);
+            while ((bytes = zipIS.read(buffer)) > 0)
+            {
+               fos.write(buffer, 0, bytes);
+            }
+            zipIS.close();
+            fos.close();
+         }
+         zipFile.close();
+      }
+      return result;
+   }
+
+    private String shortWarUrlFromServerHome(String warUrl)
+    {
+        String serverHomeUrl =  System.getProperty(org.jboss.system.server.ServerConfig.SERVER_HOME_URL);
+
+        if (warUrl == null || serverHomeUrl == null)
+            return warUrl;
+
+        if (warUrl.startsWith(serverHomeUrl))
+          return ".../" + warUrl.substring(serverHomeUrl.length());
+        else
+          return warUrl;
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import org.apache.catalina.loader.WebappClassLoader;
+import org.jboss.logging.Logger;
+
+/**
+ * Subclass the tomcat web app class loader to override the filter method
+ * to exclude classes which cannot be override by the web app due to their
+ * use in the tomcat web container/integration.
+ * 
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public class WebAppClassLoader extends WebappClassLoader
+{
+   static Logger log = Logger.getLogger(WebAppClassLoader.class);
+   private String[] filteredPackages = {
+      "org.apache.commons.logging"
+   };
+
+   public WebAppClassLoader()
+   {
+   }
+
+   public WebAppClassLoader(ClassLoader parent)
+   {
+      super(parent);
+   }
+
+   public String[] getFilteredPackages()
+   {
+      return filteredPackages;
+   }
+   public void setFilteredPackages(String[] pkgs)
+   {
+      this.filteredPackages = pkgs;
+   }
+
+   /**
+    * Overriden to filter out classes in the packages listed in the
+    * filteredPackages settings.
+    * 
+    * @param name
+    * @return true if the class should be loaded from the parent class loader,
+    *    false if it can be loaded from this class loader.
+    */ 
+   protected boolean filter(String name)
+   {
+      boolean excludeClass = super.filter(name);
+      if( excludeClass == false )
+      {
+         // Check class against our filtered packages
+         int length = filteredPackages != null ? filteredPackages.length : 0;
+         for(int n = 0; n < length; n ++)
+         {
+            String pkg = filteredPackages[n];
+            if( name.startsWith(pkg) )
+            {
+               excludeClass = true;
+               break;
+            }
+         }
+      }
+      log.trace("filter name="+name+", exclude="+excludeClass);
+      return excludeClass;
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,68 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.LifecycleException;
+
+/**
+ * Override the tomcat WebappLoader to set the default class loader to the
+ * WebAppClassLoader and pass the filtered packages to the WebAppClassLoader.
+ * 
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public class WebAppLoader extends WebappLoader
+{
+   private String[] filteredPackages = {
+      "org.apache.commons.logging"
+   };
+
+   public WebAppLoader()
+   {
+      super();
+      setLoaderClass(WebAppClassLoader.class.getName());
+   }
+
+   public WebAppLoader(ClassLoader parent, String[] filteredPackages)
+   {
+      super(parent);
+      setLoaderClass(WebAppClassLoader.class.getName());
+      this.filteredPackages = filteredPackages;
+   }
+
+   /**
+    * Override to apply the filteredPackages to the jboss WebAppClassLoader
+    * 
+    * @throws LifecycleException
+    */ 
+   public void start() throws LifecycleException
+   {
+      super.start();
+      ClassLoader loader = getClassLoader();
+      if( loader instanceof WebAppClassLoader )
+      {
+         WebAppClassLoader webLoader = (WebAppClassLoader) loader;
+         webLoader.setFilteredPackages(filteredPackages);
+      }
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,320 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+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.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.jboss.mx.loading.RepositoryClassLoader;
+
+/**
+ * Initial version of a JBoss implementation of the Tomcat Loader.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public class WebCtxLoader
+   implements Lifecycle, Loader
+{
+   /**
+    * The ClassLoader used to scope the ENC
+    */
+   protected ClassLoader encLoader;
+   /**
+    * The ClassLoader returned from getClassLoader
+    */
+   protected ENCLoader ctxLoader;
+   /**
+    * The war UCL used to load the war classes
+    */
+   protected RepositoryClassLoader delegate;
+   protected Container webContainer;
+   protected URL warURL;
+
+   /**
+    * The set of repositories associated with this class loader.
+    */
+   private ArrayList repositories = new ArrayList();
+
+   /**
+    * Create a WebCtxLoader given the ENC scoping class loader.
+    *
+    * @param encLoader
+    */
+   WebCtxLoader(ClassLoader encLoader)
+   {
+      this.encLoader = encLoader;
+      this.ctxLoader = new ENCLoader(encLoader);
+      ClassLoader parent = encLoader;
+      while ((parent instanceof RepositoryClassLoader) == false && parent != null)
+         parent = parent.getParent();
+      this.delegate = (RepositoryClassLoader) parent;
+   }
+
+   public void setWarURL(URL warURL) throws MalformedURLException
+   {
+      this.warURL = warURL;
+      String path = warURL.getFile();
+      File classesDir = new File(path, "WEB-INF/classes");
+      if (classesDir.exists())
+      {
+         delegate.addURL(classesDir.toURL());
+         ctxLoader.addURLInternal(classesDir.toURL());
+      }
+      File libDir = new File(path, "WEB-INF/lib");
+      if (libDir.exists())
+      {
+         File[] jars = libDir.listFiles();
+         int length = jars != null ? jars.length : 0;
+         for (int j = 0; j < length; j++)
+         {
+            File jar = jars[j];
+            if(jar.getAbsolutePath().endsWith(".jar"))
+            {
+               delegate.addURL(jar.toURL());
+               ctxLoader.addURLInternal(jar.toURL());
+            } 
+         }
+      }
+   }
+
+   public void addLifecycleListener(LifecycleListener listener)
+   {
+   }
+
+   public LifecycleListener[] findLifecycleListeners()
+   {
+      return new LifecycleListener[0];
+   }
+
+   public void removeLifecycleListener(LifecycleListener listener)
+   {
+   }
+
+   public void start() throws LifecycleException
+   {
+      // ctxLoader is set upon construction and nullified during stop
+      if (this.ctxLoader == null)
+         throw new LifecycleException("WebCtxLoader cannot be restarted");
+      
+      setClassPath();
+      ServletContext servletContext = ((Context) webContainer).getServletContext();
+      if (servletContext == null)
+         return;
+   }
+
+   public void stop() throws LifecycleException
+   {
+      // Remove the ctxLoader mapping kept by the DirContextURLStreamHandler 
+      DirContextURLStreamHandler.unbind(ctxLoader);
+      org.apache.commons.logging.LogFactory.release(ctxLoader);
+      org.apache.commons.logging.LogFactory.release(encLoader);
+      this.encLoader = null;
+      this.ctxLoader = null;
+      this.delegate = null;
+      this.repositories.clear();
+      this.warURL = null;
+      this.webContainer = null;
+   }
+
+   public void backgroundProcess()
+   {
+      //To change body of implemented methods use File | Settings | File Templates.
+   }
+
+   /**
+    * We must pass the wrapped encLoader as tomcat needs to see a unique
+    * class loader that is distinct from the thread context class loader seen
+    * to be in effect when the web app is started. This is due to how it
+    * binds contexts using the DirContextURLStreamHandler class.
+    *
+    * @return The ENC scoping class loader
+    * @see org.apache.naming.resources.DirContextURLStreamHandler
+    */
+   public ClassLoader getClassLoader()
+   {
+      return ctxLoader;
+   }
+
+   public Container getContainer()
+   {
+      return webContainer;
+   }
+
+   public void setContainer(Container container)
+   {
+      webContainer = container;
+
+   }
+
+   public boolean getDelegate()
+   {
+      return false;
+   }
+
+   public void setDelegate(boolean delegate)
+   {
+   }
+
+   public String getInfo()
+   {
+      return null;
+   }
+
+   public boolean getReloadable()
+   {
+      return false;
+   }
+
+   public void setReloadable(boolean reloadable)
+   {
+   }
+
+   public void addPropertyChangeListener(PropertyChangeListener listener)
+   {
+   }
+
+   public void addRepository(String repository)
+   {
+      if (repositories.contains(repository) == true)
+         return;
+      repositories.add(repository);
+      setClassPath();
+   }
+
+   public String[] findRepositories()
+   {
+      String[] tmp = new String[repositories.size()];
+      repositories.toArray(tmp);
+      return tmp;
+   }
+
+   public boolean modified()
+   {
+      return false;
+   }
+
+   public void removePropertyChangeListener(PropertyChangeListener listener)
+   {
+   }
+
+   /**
+    * 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 (!(webContainer instanceof Context))
+         return;
+      ServletContext servletContext = ((Context) webContainer).getServletContext();
+      if (servletContext == null)
+         return;
+
+      try
+      {
+         Method method =
+            webContainer.getClass().getMethod("getCompilerClasspath", null);
+         Object baseClasspath = method.invoke(webContainer, null);
+         if (baseClasspath != null)
+         {
+            servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+               baseClasspath.toString());
+            return;
+         }
+      }
+      catch (Exception e)
+      {
+         // Ignore
+         e.printStackTrace();
+      }
+
+      StringBuffer classpath = new StringBuffer();
+
+      // Assemble the class path information from our repositories
+      for (int i = 0; i < repositories.size(); i++)
+      {
+         String repository = repositories.get(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 (i > 0)
+            classpath.append(File.pathSeparator);
+         classpath.append(repository);
+      }
+
+      // Store the assembled class path as a servlet context attribute
+      servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+         classpath.toString());
+
+   }
+
+   /**
+    * A trival extension of URLClassLoader that uses an empty URL[] as its
+    * classpath so that all work is delegated to its parent.
+    */
+   static class ENCLoader extends URLClassLoader
+   {
+      private URL[] urllist = new URL[0];
+
+      ENCLoader(ClassLoader parent)
+      {
+         super(new URL[0], parent);
+      }
+
+      void addURLInternal(URL url)
+      {
+         URL[] result = new URL[urllist.length + 1];
+         for (int i = 0; i < urllist.length; i++)
+            result[i] = urllist[i];
+         result[urllist.length] = url;
+         urllist = result;
+      }
+
+      public URL[] getURLs()
+      {
+         return urllist;
+      }
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,807 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.jasper;
+
+import java.util.Properties;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.Map;
+import java.io.File;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.apache.jasper.Options;
+import org.apache.jasper.Constants;
+import org.apache.jasper.xmlparser.ParserUtils;
+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.jboss.logging.Logger;
+
+/**
+ * Override the default JspServletOptions to externalize the jsp layer
+ * configuration. This overrides the default TagLibCache to the jboss version.
+ * 
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 41071 $
+ */
+public class JspServletOptions
+   implements Options
+{
+   static Logger log = Logger.getLogger(JspServletOptions.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;
+
+   /**
+    * The compiler class name.
+    */
+   private String compilerClassName = null;
+   
+   /**
+    * Compiler target VM.
+    */
+   private String compilerTargetVM = "1.4";
+
+   /**
+    * The compiler source VM.
+    */
+   private String compilerSourceVM = "1.4";
+
+   /**
+    * Should we include a source fragment in exception messages, which could be displayed
+    * to the developer ?
+    */
+   private boolean displaySourceFragment = true;
+
+   /**
+    * 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;
+   }
+
+   /**
+    * Should we include a source fragment in exception messages, which could be displayed
+    * to the developer ?
+    */
+   public boolean getDisplaySourceFragment() {
+       return displaySourceFragment;
+   }
+
+   /**
+    * 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;
+   }
+
+   /**
+    * Java compiler class to use.
+    */
+   public String getCompilerClassName() {
+       return compilerClassName;
+   }
+
+   /**
+    * @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;
+   }
+
+   /**
+    * Is caching enabled (used for precompilation).
+    */
+   public boolean isCaching()
+   {
+      return false;
+   }
+    
+   /**
+    * The web-application wide cache for the returned TreeNode
+    * by parseXMLDocument in TagLibraryInfoImpl.parseTLD,
+    * if isCaching returns true.
+    * 
+    * @return the Map(String uri, TreeNode tld) instance.
+    */
+   public Map getCache()
+   {
+      return null;
+   }
+
+   /**
+    * Create an EmbeddedServletOptions object using data available from
+    * ServletConfig and ServletContext.
+    */
+   public JspServletOptions(ServletConfig config, ServletContext context)
+   {
+
+      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
+         {
+            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
+         {
+            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
+         {
+            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
+         {
+            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
+         {
+            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
+         {
+            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;
+               log.warn(Localizer.getMessage("jsp.warning.checkInterval"));
+            }
+         }
+         catch (NumberFormatException ex)
+         {
+            log.warn(Localizer.getMessage("jsp.warning.checkInterval"));
+         }
+      }
+
+      String modificationTestInterval = config.getInitParameter("modificationTestInterval");
+      if (modificationTestInterval != null)
+      {
+         try
+         {
+            this.modificationTestInterval = Integer.parseInt(modificationTestInterval);
+         }
+         catch (NumberFormatException ex)
+         {
+            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
+         {
+            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
+         {
+            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
+         {
+            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
+         {
+            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
+         {
+            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 compilerClassName = config.getInitParameter("compilerClassName");
+      if (compilerClassName != null)
+      {
+          this.compilerClassName = compilerClassName;
+      }
+
+      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 displaySourceFragment = config.getInitParameter("displaySourceFragment"); 
+      if (displaySourceFragment != null)
+      {
+          if (displaySourceFragment.equalsIgnoreCase("true"))
+          {
+              this.displaySourceFragment = true;
+          }
+          else if (displaySourceFragment.equalsIgnoreCase("false"))
+          {
+              this.displaySourceFragment = false;
+          }
+          else
+          {
+              log.warn(Localizer.getMessage("jsp.warning.displaySourceFragment"));
+          }
+      }
+      
+      String fork = config.getInitParameter("fork");
+      if (fork != null)
+      {
+         if (fork.equalsIgnoreCase("true"))
+         {
+            this.fork = true;
+         }
+         else if (fork.equalsIgnoreCase("false"))
+         {
+            this.fork = false;
+         }
+         else
+         {
+            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
+         {
+            log.warn(Localizer.getMessage("jsp.warning.xpoweredBy"));
+         }
+      }
+
+      /* Setup the global Tag Libraries location cache for this web app. The
+         tagLibJarN entries define the jars the cache should scan. 
+      */
+      String base = "tagLibJar";
+      ArrayList tldJars = new ArrayList();
+      int count = 0;
+      String jarPath = null;
+      do
+      {
+         String name = base + count;
+         jarPath = config.getInitParameter(name);
+         if( jarPath != null )
+            tldJars.add(jarPath);
+         count ++;
+      } while( jarPath != null );
+      
+      tldLocationsCache = new TagLibCache(context, tldJars);
+
+      // Setup the jsp config info for this web app.
+      jspConfig = new JspConfig(context);
+
+      // Create a Tag plugin instance
+      tagPluginManager = new TagPluginManager(context);
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,446 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.jasper;
+
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.MalformedURLException;
+import javax.servlet.ServletContext;
+import java.util.Map; // SYNC
+
+import org.apache.jasper.compiler.TldLocationsCache;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.Constants;
+import org.apache.jasper.xmlparser.TreeNode;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.jboss.logging.Logger;
+
+/**
+ * A prototype TagLibCache that allows one to obtain shared tlds from the
+ * jbossweb sar conf/tlds directory.
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 41791 $
+ */
+public class TagLibCache extends TldLocationsCache
+{
+   private static final String WEB_XML = "/WEB-INF/web.xml";
+   private static final String JAR_FILE_SUFFIX = ".jar";
+
+   private static Logger log = Logger.getLogger(TagLibCache.class);
+
+   private ServletContext ctx;
+   private HashMap mappings;
+   private ArrayList tagLibJars;
+
+   public TagLibCache(ServletContext ctx, ArrayList tagLibJars)
+   {
+      super(ctx, true);
+      this.ctx = ctx;
+      this.tagLibJars = tagLibJars;
+   }
+
+   /**
+    * 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 (mappings == null)
+         init();
+      String[] locations = (String[]) mappings.get(uri);
+      return locations;
+   }
+
+   private synchronized void init() throws JasperException
+   {
+     if (mappings != null)
+     {
+       return;
+     }
+
+     HashMap tmpMappings = null;
+      try
+      {
+         tmpMappings = new HashMap();
+         processWebDotXml(tmpMappings);
+         loadStandardTlds(tmpMappings);
+         processTldsInFileSystem("/WEB-INF/", tmpMappings);
+      }
+      catch (Exception ex)
+      {
+         String msg = Localizer.getMessage("jsp.error.internal.tldinit", ex.getMessage());
+         throw new JasperException(msg, ex);
+      }
+      finally
+      {
+        mappings = tmpMappings;
+      }
+   }
+
+   /*
+    * Populates taglib map described in web.xml.
+    */
+   protected void processWebDotXml(Map tmpMappings) throws Exception
+   {
+
+      InputStream is = null;
+
+      try
+      {
+         // Acquire input stream to web application deployment descriptor
+         String altDDName = (String) ctx.getAttribute(Constants.ALT_DD_ATTR);
+         if (altDDName != null)
+         {
+            try
+            {
+               is = new FileInputStream(altDDName);
+            }
+            catch (FileNotFoundException e)
+            {
+               log.warn(Localizer.getMessage("jsp.error.internal.filenotfound",
+                  altDDName));
+            }
+         }
+         else
+         {
+            is = ctx.getResourceAsStream(WEB_XML);
+            if (is == null)
+            {
+               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 = ctx.getResource(tagLoc).toString();
+               tagLoc2 = "META-INF/taglib.tld";
+            }
+            tmpMappings.put(tagUri, new String[]{tagLoc, tagLoc2}); // SYNC
+         }
+      }
+      finally
+      {
+         if (is != null)
+         {
+            try
+            {
+               is.close();
+            }
+            catch (Throwable t)
+            {
+            }
+         }
+      }
+   }
+
+   protected void loadStandardTlds(Map tmpMappings) throws MalformedURLException // SYNC
+   {
+      if( tagLibJars.size() == 0 )
+         return;
+
+      // Locate the conf/web.xml
+      ClassLoader loader = Thread.currentThread().getContextClassLoader();
+      URL web = loader.getResource("conf/web.xml");
+      URL sarURL = new URL(web, "..");
+      for(int n = 0; n < tagLibJars.size(); n ++)
+      {
+         String jarPath = (String) tagLibJars.get(n);
+         try
+         {
+            URL url = new URL(sarURL, jarPath);
+            String resourcePath = url.toString();
+            log.debug("Scanning for tlds in: "+resourcePath);
+            URLConnection conn = url.openConnection();
+            conn.setUseCaches(false);
+            scanJar(conn, resourcePath, true, tmpMappings); // SYNC
+         }
+         catch (Exception e)
+         {
+            log.debug("Failed to scan: "+jarPath, e);
+         }
+      }
+   }
+
+   /*
+    * 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.
+    */
+   protected void processTldsInFileSystem(String startPath, Map tmpMappings) // SYNC
+      throws Exception
+   {
+
+      Set dirList = ctx.getResourcePaths(startPath);
+      if (dirList != null)
+      {
+         Iterator it = dirList.iterator();
+         while (it.hasNext())
+         {
+            String path = (String) it.next();
+            if (path.endsWith("/"))
+            {
+               processTldsInFileSystem(path, tmpMappings); // SYNC
+            }
+            if( path.endsWith(".jar") )
+            {
+               URL resURL = ctx.getResource(path);
+               URLConnection conn = resURL.openConnection();
+               conn.setUseCaches(false);
+               this.scanJar(conn, resURL.toString(), false, tmpMappings); // SYNC
+            }
+            else if ( path.endsWith(".tld") == true )
+            {
+               InputStream stream = ctx.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 && tmpMappings.get(uri) == null) // SYNC
+               {
+                  tmpMappings.put(uri, new String[]{path, null}); // SYNC
+               }
+            }
+         }
+      }
+   }
+
+   /**
+    * Scans the given JarInputStream 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 
+    * @param ignore true if any exceptions raised when processing the given JAR
+    * should be ignored, false otherwise
+    */
+   private void scanJar(URLConnection conn, String resourcePath, boolean ignore, Map tmpMappings) // SYNC
+      throws JasperException, IOException
+   {
+      InputStream connIS = conn.getInputStream();
+      JarInputStream jis = new JarInputStream(connIS);
+      try
+      {
+         JarEntry entry = jis.getNextJarEntry();
+         while( entry != null )
+         {
+            String name = entry.getName();
+            if( name.endsWith(".tld") == false )
+            {
+               entry = jis.getNextJarEntry();
+               continue;
+            }
+
+            EntryInputStream eis = new EntryInputStream(jis);
+            String uri = getUriFromTld(resourcePath, eis);
+            // Add implicit map entry only if its uri is not already
+            // present in the map
+            if (uri != null && tmpMappings.get(uri) == null) // SYNC
+            {
+               tmpMappings.put(uri, new String[]{resourcePath, name}); // SYNC
+            }
+            entry = jis.getNextJarEntry();
+         }
+      }
+      catch (Exception ex)
+      {
+         if (!ignore)
+         {
+            throw new JasperException(ex);
+         }
+      }
+      finally
+      {
+         if( jis != null )
+         {
+            try
+            {
+               jis.close();
+            }
+            catch (Throwable t)
+            {
+               // ignore
+            }
+         }
+
+         if (connIS != null)
+         {
+            try
+            {
+               connIS.close();
+            }
+            catch (Throwable t)
+            {
+               // ignore
+            }
+         }
+      }
+   }
+
+   /*
+    * 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;
+   }
+
+   /**
+    * Used to ignore the close on the jar entry input stream since this
+    * closes the jar stream, not just the entry.
+    */ 
+   static class EntryInputStream extends InputStream
+   {
+      private JarInputStream jis;
+      EntryInputStream(JarInputStream jis)
+      {
+         this.jis = jis;
+      }
+
+      public int read() throws IOException
+      {
+         return jis.read();
+      }
+
+      public int available() throws IOException
+      {
+         return jis.available();
+      }
+
+      public void close() throws IOException
+      {
+         
+      }
+
+      public void reset() throws IOException
+      {
+         jis.reset();
+      }
+
+      public boolean markSupported()
+      {
+         return jis.markSupported();
+      }
+
+      public synchronized void mark(int readlimit)
+      {
+         jis.mark(readlimit);
+      }
+
+      public long skip(long n) throws IOException
+      {
+         return jis.skip(n);
+      }
+
+      public int read(byte b[], int off, int len) throws IOException
+      {
+         return jis.read(b, off, len);
+      }
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,264 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.jca;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.resource.ResourceException;
+import javax.servlet.ServletException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.resource.connectionmanager.CachedConnectionManager;
+
+/**
+ * This valve checks for unclosed connections on a servlet request
+ *
+ * @author <a href="mailto:adrian at jboss.com">Adrian Brock</a>
+ * @version $Revision: 57206 $
+ */
+public class CachedConnectionValve extends ValveBase implements Lifecycle
+{
+   /**
+    * The log
+    */
+   private static final Logger log = Logger.getLogger(CachedConnectionValve.class);
+
+   /**
+    * The info string for this Valve
+    */
+   private static final String info = "CachedConnectionValve/1.0";
+
+   /**
+    * Valve-lifecycle helper object
+    */
+   protected LifecycleSupport support = new LifecycleSupport(this);
+
+   /**
+    * The object name of the cached connection manager
+    */
+   protected String ccmName;
+
+   /**
+    * The cached connection manager
+    */
+   protected CachedConnectionManager ccm;
+
+   /**
+    * The object name of the transaction manager service
+    */
+   protected String tmName;
+
+   /**
+    * The transaction manager
+    */
+   protected TransactionManager tm;
+
+   /**
+    * The unshareable resources
+    */
+   protected Set unsharableResources = new HashSet();
+
+   /**
+    * Create a new valve
+    *
+    * @param ccm the cached connection manager for the valve
+    */
+   public CachedConnectionValve()
+   {
+      super();
+   }
+
+   /**
+    * Get information about this Valve.
+    */
+   public String getInfo()
+   {
+      return info;
+   }
+
+   /**
+    * Get the cached connection manager object name
+    */
+   public String getCachedConnectionManagerObjectName()
+   {
+      return ccmName;
+   }
+
+   /**
+    * Set the cached connection manager object name
+    */
+   public void setCachedConnectionManagerObjectName(String ccmName)
+   {
+      this.ccmName = ccmName;
+   }
+
+   /**
+    * Get the transaction manager object name
+    */
+   public String getTransactionManagerObjectName()
+   {
+      return tmName;
+   }
+
+   /**
+    * Set the transaction manager object name
+    */
+   public void setTransactionManagerObjectName(String tmName)
+   {
+      this.tmName = tmName;
+   }
+
+   public void invoke(Request request, Response response) throws IOException, ServletException
+   {
+      if(ccm == null)
+         throw new IllegalStateException("Uncomment the dependency on CachedConnectionManager"
+               + " in META-INF/jboss-service.xml of jbossweb-tomcatxxx.sar");
+      try
+      {
+         ccm.pushMetaAwareObject(this, unsharableResources);
+         try
+         {
+            getNext().invoke(request, response);
+         }
+         finally
+         {
+            try
+            {
+               ccm.popMetaAwareObject(unsharableResources);
+            }
+            finally
+            {
+               checkTransactionComplete(request);
+            }
+         }
+      }
+      catch (ResourceException e)
+      {
+         throw new ServletException("Error invoking cached connection manager", e);
+      }
+   }
+
+   // Lifecycle-interface
+   public void addLifecycleListener(LifecycleListener listener)
+   {
+      support.addLifecycleListener(listener);
+   }
+
+   public void removeLifecycleListener(LifecycleListener listener)
+   {
+      support.removeLifecycleListener(listener);
+   }
+
+   public LifecycleListener[] findLifecycleListeners()
+   {
+      return support.findLifecycleListeners();
+   }
+
+   public void start() throws LifecycleException
+   {
+      try
+      {
+         MBeanServer server = MBeanServerLocator.locateJBoss();
+         ccm = (CachedConnectionManager) server.getAttribute(new ObjectName(ccmName), "Instance");
+         tm = (TransactionManager) server.getAttribute(new ObjectName(tmName), "TransactionManager");
+      }
+      catch (Exception e)
+      {
+         throw new LifecycleException(e);
+      }
+      
+      // TODO unshareable resources
+      support.fireLifecycleEvent(START_EVENT, this);
+   }
+
+   public void stop() throws LifecycleException
+   {
+      support.fireLifecycleEvent(STOP_EVENT, this);
+      unsharableResources.clear();
+   }
+
+   protected void checkTransactionComplete(Request request)
+   {
+      int status = Status.STATUS_NO_TRANSACTION;
+
+      try
+      {
+         status = tm.getStatus();
+      }
+      catch (SystemException ex)
+      {
+         log.error("Failed to get status", ex);
+      }
+
+      switch (status)
+      {
+         case Status.STATUS_ACTIVE:
+         case Status.STATUS_COMMITTING:
+         case Status.STATUS_MARKED_ROLLBACK:
+         case Status.STATUS_PREPARING:
+         case Status.STATUS_ROLLING_BACK:
+            try
+            {
+               tm.rollback();
+            }
+            catch (Exception ex)
+            {
+               log.error("Failed to rollback", ex);
+            }
+            // fall through...
+         case Status.STATUS_PREPARED:
+            String servletName = "<Unknown>";
+            try
+            {
+               Wrapper servlet = request.getWrapper();
+               if (servlet != null)
+               {
+                  servletName = servlet.getName();
+                  if (servlet.getJspFile() != null)
+                     servletName = servlet.getJspFile();
+               }
+            }
+            catch (Throwable ignored)
+            {
+            }
+
+            String msg = "Application error: " + servletName + " did not complete its transaction";
+            log.error(msg);
+      }
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+   <mbean name="CachedConnectionValve"
+      description="Valve checks for unclosed connections on a servlet request"
+      domain="Catalina"
+      group="Valve"
+      type="org.jboss.web.tomcat.service.jca.CachedConnectionValve">
+
+      <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"/>
+
+      <attribute name="containerName"
+         description="Object name of the container"
+         type="javax.management.ObjectName"/>
+
+      <attribute name="cachedConnectionManagerObjectName"
+         description="The cached connection manager"
+         type="java.lang.String"/>
+
+   </mbean>
+
+</mbeans-descriptors>
\ No newline at end of file


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,85 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.Manager;
+import org.jboss.metadata.WebMetaData;
+
+/** Common interface for the http session replication managers.
+ * 
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public interface AbstractJBossManager extends Manager
+{
+   /** Initialize the manager with the web metadata and 
+    * @param name
+    * @param webMetaData
+    * @param useJK
+    * @param useLocalCache
+    * @throws ClusteringNotSupportedException
+    */ 
+   public void init(String name, WebMetaData webMetaData, boolean useJK,
+      boolean useLocalCache)
+      throws ClusteringNotSupportedException;
+
+   /** The session invalidation policy. One of:
+    SESSION_INVALIDATE_ACCESS =0;
+    SESSION_INVALIDATE_SET_AND_GET =1;
+    SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET =2;
+    SESSION_INVALIDATE_SET =3;
+    * @return the invalidation policy constant
+    */ 
+   public int getInvalidateSessionPolicy();
+
+   /**
+    * Retrieve the JvmRoute for the enclosing Engine.
+    *
+    * @return the JvmRoute or null.
+    */
+   public String getJvmRoute();
+
+   /**
+    * Sets a new cookie for the given session id and response
+    *
+    * @param sessionId The session id
+    */
+   public void setNewSessionCookie(String sessionId, HttpServletResponse response);
+
+   /**
+    * Remove the active session locally from the manager without replicating to the cluster. This can be
+    * useful when the session is exipred, for example, where there is not need to propagate the expiration.
+    *
+    * @param session
+    */
+   public void removeLocal(Session session);
+
+   /**
+    * Store the modified session.
+    *
+    * @param session
+    */
+   public boolean storeSession(Session session);
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,31 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+/**
+ * Marker interface for use in aop annotation declaration. For example, can use this in pojo
+ * annotation instead of declaring jboss-aop.xml. For a pojo annotation declaration, just declare
+ * this in the pojo class level, and then there is a corresponding jboss-aop.xml that is needed.
+ * @author Ben Wang
+ */
+public interface AopMarker {
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,278 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+/**
+ * Implementation of a clustered session for the JBossCacheManager. The replication granularity
+ * level is attribute based; that is, we replicate only the dirty attributes.
+ * We use JBossCache for our internal, deplicated data store.
+ * The internal structure is like in JBossCache:
+ * <pre>
+ * /JSESSION
+ *    /hostname
+ *       /web_app_path    (path + session id is unique)
+ *          /id   Map(id, session)
+ *                   (VERSION_KEY, version)  // Used for version tracking. version is an Integer.
+ *             /ATTRIBUTE    Map(attr_key, value)
+ * </pre>
+ * <p/>
+ * Note that the isolation level of the cache dictates the
+ * concurrency behavior. Also note that session and its associated attribtues are stored in different nodes.
+ * This will be ok since cache will take care of concurrency. When replicating, we will need to replicate both
+ * session and its attributes.</p>
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ * 
+ * @version $Revision: 57206 $
+ */
+class AttributeBasedClusteredSession
+   extends JBossCacheClusteredSession
+{
+   static final long serialVersionUID = -5625209785550936713L;
+   /**
+    * Descriptive information describing this Session implementation.
+    */
+   protected static final String info = "AttributeBasedClusteredSession/1.0";
+
+   // Transient map to store attr changes for replication.
+   private transient Map attrModifiedMap_ = new HashMap();
+   // Note that the removed attr is intentionally stored in a map 
+   // instead of a Set so it is faster to lookup and remove.
+   private transient Map attrRemovedMap_ = new HashMap();
+   private static final int REMOVE = 0;   // Used to track attribute changes
+   private static final int MODIFY = 1;
+   // TODO why isn't the superclass field sufficient?
+   private transient Map attributes_ = Collections.synchronizedMap(new HashMap());
+
+   public AttributeBasedClusteredSession(JBossCacheManager manager)
+   {
+      super(manager);
+   }
+
+   // ----------------------------------------------- Overridden Public Methods
+
+
+   /**
+    * Override the superclass to additionally reset this class' fields.
+    * <p>
+    * <strong>NOTE:</strong> It is not anticipated that this method will be
+    * called on a ClusteredSession, but we are overriding the method to be
+    * thorough.
+    * </p>
+    */
+   public void recycle()
+   {
+      super.recycle();
+
+      attributes_.clear();
+      clearAttrChangedMaps();
+   }
+
+   /**
+    * Return a string representation of this object.
+    */
+   public String toString()
+   {
+
+      StringBuffer sb = new StringBuffer();
+      sb.append("AttributeBasedClusteredSession[");
+      sb.append(super.toString());
+      sb.append("]");
+      return (sb.toString());
+
+   }
+
+   /**
+    * Overrides the superclass version to read in the attributes.
+    */
+   public synchronized void processSessionRepl()
+   {
+      // Replicate the metadata first. Note this will be lightweight since many  
+      // of the fields are transient and the attribute map isn't included.
+      if (log.isTraceEnabled())
+      {
+         log.trace("processSessionRepl(): session is dirty. Will increment " +
+                "version from: " + getVersion() + " and replicate.");
+      }
+      this.incrementVersion();
+      proxy_.putSession(realId, this);
+
+      // Go thru the attribute change list
+      
+      if (getSessionAttributesDirty())
+      {
+         // Go thru the modified attr list first
+         int modCount = attrModifiedMap_.size();
+         if (modCount == 1)
+         {
+            for (Iterator it = attrModifiedMap_.entrySet().iterator(); it.hasNext(); )
+            {
+               Map.Entry entry = (Entry) it.next();
+               proxy_.putAttribute(realId, (String) entry.getKey(), entry.getValue());
+            }
+         }
+         else if (modCount > 0)
+         {
+            // It's more efficient to write a map than 2 method calls,
+            // plus it reduces the number of CacheListener notifications
+            proxy_.putAttribute(realId, attrModifiedMap_);
+         }
+         
+         // Go thru the remove attr list
+         if (attrRemovedMap_.size() > 0)
+         {         
+            for (Iterator it = attrRemovedMap_.keySet().iterator(); it.hasNext(); )
+            {
+               proxy_.removeAttribute(realId, (String) it.next());
+            }
+         }
+         
+         clearAttrChangedMaps();
+      }
+      
+      sessionAttributesDirty = false;
+      sessionMetadataDirty = false;
+      
+      updateLastReplicated();
+   }
+
+   public void removeMyself()
+   {
+      // This is a shortcut to remove session and it's child attributes.
+      proxy_.removeSession(realId);
+   }
+
+   public void removeMyselfLocal()
+   {
+      // Need to evict attribute first before session to clean up everything.
+      // BRIAN -- the attributes *are* already evicted, but we leave the
+      // removeAttributesLocal call here in order to evict the ATTRIBUTE node.  
+      // Otherwise empty nodes for the session root and child ATTRIBUTE will 
+      // remain in the tree and screw up our list of session names.
+      proxy_.removeAttributesLocal(realId);
+      proxy_.removeSessionLocal(realId);
+   }
+
+   // ------------------------------------------------ JBoss internal abstract method
+
+   /**
+    * Populate the attributes stored in the distributed store to local transient ones.
+    */
+   protected void populateAttributes()
+   {
+      Map map = proxy_.getAttributes(realId);
+      
+      // Preserve any local attributes that were excluded from replication
+      Map excluded = removeExcludedAttributes(attributes_);
+      if (excluded != null)
+         map.putAll(excluded);
+      
+      attributes_ = Collections.synchronizedMap(map);
+      attrModifiedMap_.clear();
+      attrRemovedMap_.clear();
+   }
+
+   protected Object getJBossInternalAttribute(String name)
+   {
+      Object result = attributes_.get(name);
+
+      // Do dirty check even if result is null, as w/ SET_AND_GET null
+      // still makes us dirty (ensures timely replication w/o using ACCESS)
+      if (isGetDirty(result) && !replicationExcludes.contains(name))
+      {
+         attributeChanged(name, result, MODIFY);
+      }
+
+      return result;
+   }
+
+   protected Object removeJBossInternalAttribute(String name, 
+                                                 boolean localCall, 
+                                                 boolean localOnly)
+   {
+      Object result = attributes_.remove(name);
+      if (localCall && !replicationExcludes.contains(name))
+         attributeChanged(name, result, REMOVE);
+      return result;
+   }
+
+   protected Map getJBossInternalAttributes()
+   {
+      return attributes_;
+   }
+
+   protected Set getJBossInternalKeys()
+   {
+      return attributes_.keySet();
+   }
+
+   /**
+    * Method inherited from Tomcat. Return zero-length based string if not found.
+    */
+   protected String[] keys()
+   {
+      return ((String[]) getJBossInternalKeys().toArray(EMPTY_ARRAY));
+   }
+
+   protected Object setJBossInternalAttribute(String key, Object value)
+   {
+      Object old = attributes_.put(key, value);
+      if (!replicationExcludes.contains(key))
+         attributeChanged(key, value, MODIFY);
+      return old;
+   }
+
+   protected synchronized void attributeChanged(Object key, Object value, int op)
+   {
+      if (op == MODIFY)
+      {
+         if (attrRemovedMap_.containsKey(key))
+         {
+            attrRemovedMap_.remove(key);
+         }
+         attrModifiedMap_.put(key, value);
+      }
+      else if (op == REMOVE)
+      {
+         if (attrModifiedMap_.containsKey(key))
+         {
+            attrModifiedMap_.remove(key);
+         }
+         attrRemovedMap_.put(key, value);
+      }
+      sessionAttributesDirty();
+   }
+
+   protected synchronized void clearAttrChangedMaps()
+   {
+      attrRemovedMap_.clear();
+      attrModifiedMap_.clear();
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,149 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.transaction.TransactionManager;
+
+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.valves.ValveBase;
+import org.jboss.logging.Logger;
+
+/**
+ * This Valve handles batch replication mode. It uses the cache tm to performa batch replication.
+ *
+ * @author Ben Wang
+ * @version $Revision: 57206 $
+ */
+public class BatchReplicationClusteredSessionValve extends ValveBase implements Lifecycle
+{
+   private static Logger log_ = Logger.getLogger(BatchReplicationClusteredSessionValve.class);
+
+   // The info string for this Valve
+   private static final String info = "BatchReplicationClusteredSessionValve/1.0";
+
+   // Valve-lifecycle_ helper object
+   protected LifecycleSupport support = new LifecycleSupport(this);
+
+   protected JBossCacheManager manager_;
+
+   /**
+    * Create a new Valve.
+    *
+    */
+   public BatchReplicationClusteredSessionValve(AbstractJBossManager manager)
+   {
+      super();
+      manager_ = (JBossCacheManager)manager;
+   }
+
+   /**
+    * Get information about this Valve.
+    */
+   public String getInfo()
+   {
+      return info;
+   }
+
+   /**
+    * Valve-chain handler method.
+    * This method gets called when the request goes through the Valve-chain. Our session replication mechanism replicates the
+    * session after request got through the servlet code.
+    *
+    * @param request  The request object associated with this request.
+    * @param response The response object associated with this request.
+    */
+   public void invoke(Request request, Response response) throws IOException, ServletException
+   {
+      // Note: we use specfically the tm in cache.
+      TransactionManager tm = manager_.getCacheService().getTransactionManager();
+      if(tm == null)
+      {
+         throw new RuntimeException("BatchReplicationClusteredSessionValve.invoke(): Obtain null tm");
+      }
+
+      // Before we start a tx, get the session.  If this is a failover
+      // situation, this will cause data gravitation, which will occur 
+      // thus outside of the scope of the tx we are about to start.  
+      // JBossCacheManager will ensure the gravitation is in its own tx
+      request.getSession(false);
+      
+      // Start a new transaction, we need transaction so all the replication are sent in batch.
+      try
+      {
+         tm.begin();
+         
+         // let the servlet invocation go through
+         getNext().invoke(request, response);
+         
+         log_.trace("Ready to commit batch replication for field level granularity");         
+         
+         tm.commit();
+      }
+      catch (Exception e)
+      {
+         try
+         {
+            tm.rollback();
+         }
+         catch (Exception exn)
+         {
+            exn.printStackTrace();
+         }
+         // We will need to alert Tomcat of this exception.
+         throw new RuntimeException("JBossCacheManager.processSessionRepl(): failed to replicate session.", e);
+      }
+   }
+
+   // Lifecylce-interface
+   public void addLifecycleListener(LifecycleListener listener)
+   {
+      support.addLifecycleListener(listener);
+   }
+
+   public void removeLifecycleListener(LifecycleListener listener)
+   {
+      support.removeLifecycleListener(listener);
+   }
+
+   public LifecycleListener[] findLifecycleListeners()
+   {
+      return support.findLifecycleListeners();
+   }
+
+   public void start() throws LifecycleException
+   {
+      support.fireLifecycleEvent(START_EVENT, this);
+   }
+
+   public void stop() throws LifecycleException
+   {
+      support.fireLifecycleEvent(STOP_EVENT, this);
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,273 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.cache.AbstractTreeCacheListener;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+
+
+public class CacheListener extends AbstractTreeCacheListener
+{
+   // Element within an FQN that is JSESSION
+   private static final int JSESSION_FQN_INDEX = 0;
+   // Element within an FQN that is the hostname
+   private static final int HOSTNAME_FQN_INDEX = 1;
+   // ELEMENT within an FQN this is the webapp name
+   private static final int WEBAPP_FQN_INDEX = 2;
+   // Element within an FQN that is the session id
+   private static final int SESSION_ID_FQN_INDEX = 3;
+   // Size of an Fqn that points to the root of a session
+   private static final int SESSION_FQN_SIZE = SESSION_ID_FQN_INDEX + 1;
+   // Element within an FQN that is the root of a Pojo attribute map
+   private static final int POJO_ATTRIBUTE_FQN_INDEX = SESSION_ID_FQN_INDEX + 1;
+   // Element within an FQN that is the root of an individual Pojo attribute
+   private static final int POJO_KEY_FQN_INDEX = POJO_ATTRIBUTE_FQN_INDEX + 1;
+   // Size of an Fqn that points to the root of a session
+   private static final int POJO_KEY_FQN_SIZE = POJO_KEY_FQN_INDEX + 1;
+   // The index of the root of a buddy backup subtree
+   private static final int BUDDY_BACKUP_ROOT_OWNER_INDEX = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN.size();
+   // The size of the root of a buddy backup subtree (including owner)
+   private static final int BUDDY_BACKUP_ROOT_OWNER_SIZE = BUDDY_BACKUP_ROOT_OWNER_INDEX + 1;
+   
+//   private static final String TREE_CACHE_CLASS = "org.jboss.cache.TreeCache";
+//   private static final String DATA_GRAVITATION_CLEANUP = "_dataGravitationCleanup";
+   
+   private static Logger log_ = Logger.getLogger(CacheListener.class);
+   private JBossCacheWrapper cacheWrapper_;
+   private JBossCacheManager manager_;
+   private String webapp_;
+   private String hostname_;
+   private boolean fieldBased_;
+   // When trying to ignore unwanted notifications, do we check for local activity first?
+   private boolean disdainLocalActivity_;
+
+   CacheListener(JBossCacheWrapper wrapper, JBossCacheManager manager, String hostname, String webapp)
+   {
+      cacheWrapper_ = wrapper;
+      manager_ = manager;
+      hostname_ = hostname;
+      webapp_ =  webapp;
+      int granularity = manager_.getReplicationGranularity();
+      fieldBased_ = (granularity == WebMetaData.REPLICATION_GRANULARITY_FIELD);
+      // TODO decide if disdaining local activity is always good for REPL_ASYNC
+      disdainLocalActivity_ = (granularity == WebMetaData.REPLICATION_GRANULARITY_SESSION);; // for now
+   }
+
+   // --------------- TreeCacheListener methods ------------------------------------
+
+   public void nodeRemoved(Fqn fqn)
+   {
+      // Ignore our own activity
+      if (SessionReplicationContext.isLocallyActive())
+         return;
+      
+      boolean isBuddy = isBuddyFqn(fqn);
+      
+      // Potential removal of a Pojo where we need to unregister as an Observer.
+      if (fieldBased_ 
+            && isFqnPojoKeySized(fqn, isBuddy)
+            && isFqnForOurWebapp(fqn, isBuddy))
+      {
+         String sessId = getIdFromFqn(fqn, isBuddy);
+         String attrKey = getPojoKeyFromFqn(fqn, isBuddy);
+         manager_.processRemoteAttributeRemoval(sessId, attrKey);
+      }
+      else if(isFqnSessionRootSized(fqn, isBuddy) 
+                  && isFqnForOurWebapp(fqn, isBuddy)
+//                  && !isDataGravitationCleanup()
+                  )
+      {
+         // A session has been invalidated from another node;
+         // need to inform manager
+         String sessId = getIdFromFqn(fqn, isBuddy);
+         manager_.processRemoteInvalidation(sessId);
+      }
+   }
+
+   public void nodeModified(Fqn fqn)
+   {
+      // If checking for local activity has a higher likelihood of
+      // catching unwanted notifications than checking fqn size, 
+      // do it first
+      if (disdainLocalActivity_)
+      {
+         if (SessionReplicationContext.isLocallyActive())
+            return;         
+      }
+      
+      boolean isBuddy = isBuddyFqn(fqn);      
+      // We only care if there is a chance this is for a session root
+      if (!isFqnSessionRootSized(fqn, isBuddy))
+         return;
+      
+      if (!disdainLocalActivity_)
+      {
+         if (SessionReplicationContext.isLocallyActive())
+            return;
+      }
+      
+      // We only care if this is for our webapp
+      if (!isFqnForOurWebapp(fqn, isBuddy))
+         return;
+
+      // Query if we have version value in the distributed cache. 
+      // If we have a version value, compare the version and invalidate if necessary.
+      Integer version = (Integer)cacheWrapper_.get(fqn, JBossCacheService.VERSION_KEY);
+      if(version != null)
+      {
+         String realId = getIdFromFqn(fqn, isBuddy);
+         
+         ClusteredSession session = manager_.findLocalSession(realId);
+         if (session == null)
+         {
+            String owner = isBuddy ? getBuddyOwner(fqn) : null;
+            // Notify the manager that an unloaded session has been updated
+            manager_.unloadedSessionChanged(realId, owner);
+         }
+         else if (session.isNewData(version.intValue()))
+         {
+            // Need to invalidate the loaded session
+            session.setOutdatedVersion(version.intValue());
+            if(log_.isTraceEnabled())
+            {
+               log_.trace("nodeDirty(): session in-memory data is " +
+                          "invalidated with id: " + realId + " and version: " +
+                          version.intValue());
+            }
+         }
+         else if (isBuddy)
+         {
+            // We have a local session but got a modification for the buddy tree.
+            // This means another node is in the process of taking over the session;
+            // we don't worry about it
+            ;
+         }
+         else 
+         {
+            // This could be an issue but can happen legitimately in unusual 
+            // circumstances, so just log something at INFO, not WARN
+            
+            // Unusual circumstance: create session; don't touch session again
+            // until timeout period expired; fail over to another node after
+            // timeout but before session expiration thread has run. Existing
+            // session will be expired locally on new node and a new session created.
+            // When that session replicates, the version id will match the still
+            // existing cached session on the first node.  Unlikely, but due
+            // to design of a unit test, it happens every testsuite run :-)
+            log_.info("Possible concurrency problem: Replicated version id " + 
+                       version + " matches in-memory version for session " + realId);
+            
+            // Mark the loaded session outdated anyway; in the above mentioned
+            // "unusual circumstance" that's the correct thing to do
+            session.setOutdatedVersion(version.intValue());
+         }
+      }
+      else
+      {
+         log_.warn("No VERSION_KEY attribute found in " + fqn);
+      }
+   }
+
+   private boolean isFqnForOurWebapp(Fqn fqn, boolean isBuddy)
+   {
+      try
+      {
+         if (webapp_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + WEBAPP_FQN_INDEX : WEBAPP_FQN_INDEX))
+               && hostname_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + HOSTNAME_FQN_INDEX : HOSTNAME_FQN_INDEX))
+               && JBossCacheService.SESSION.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + JSESSION_FQN_INDEX : JSESSION_FQN_INDEX)))
+            return true;
+      }
+      catch (IndexOutOfBoundsException e)
+      {
+         // can't be ours; too small; just fall through
+      }
+
+      return false;
+   }
+   
+   private static boolean isFqnSessionRootSized(Fqn fqn, boolean isBuddy)
+   {
+      return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_FQN_SIZE : SESSION_FQN_SIZE);
+   }
+   
+   private static boolean isFqnPojoKeySized(Fqn fqn, boolean isBuddy)
+   {
+      return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_SIZE : POJO_KEY_FQN_SIZE);
+   }
+   
+   private static String getIdFromFqn(Fqn fqn, boolean isBuddy)
+   {
+      return (String)fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_ID_FQN_INDEX : SESSION_ID_FQN_INDEX);
+   }
+   
+   private static String getPojoKeyFromFqn(Fqn fqn, boolean isBuddy)
+   {
+      return (String) fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_INDEX: POJO_KEY_FQN_INDEX);
+   }
+   
+   private static boolean isBuddyFqn(Fqn fqn)
+   {
+      try
+      {
+         return BuddyManager.BUDDY_BACKUP_SUBTREE.equals(fqn.get(0));
+      }
+      catch (IndexOutOfBoundsException e)
+      {
+         // Can only happen if fqn is ROOT, and we shouldn't get
+         // notifications for ROOT.
+         // If it does, just means it's not a buddy
+         return false;
+      }      
+   }
+   
+   /**
+    * Extracts the owner portion of an buddy subtree Fqn.
+    * 
+    * @param fqn An Fqn that is a child of the buddy backup root node.
+    */
+   private static String getBuddyOwner(Fqn fqn)
+   {
+      return (String) fqn.get(BUDDY_BACKUP_ROOT_OWNER_INDEX);     
+   }
+   
+//   /**
+//    * FIXME This is a hack that examines the stack trace looking
+//    * for the TreeCache._dataGravitationCleanup method.
+//    * 
+//    * @return
+//    */
+//   private static boolean isDataGravitationCleanup()
+//   {
+//      StackTraceElement[] trace = new Throwable().getStackTrace();
+//      for (int i = 0; i < trace.length; i++)
+//      {
+//         if (TREE_CACHE_CLASS.equals(trace[i].getClassName())
+//               && DATA_GRAVITATION_CLEANUP.equals(trace[i].getMethodName()))
+//            return true;
+//      }
+//      
+//      return false;
+//   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1455 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.beans.PropertyChangeSupport;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Session;
+import org.apache.catalina.session.StandardSession;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.StringManager;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+
+/**
+ * Abstract base class for session clustering based on StandardSession. Different session
+ * replication strategy can be implemented such as session- or attribute-based ones.
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ * 
+ * @version $Revision: 57206 $
+ */
+abstract class ClusteredSession
+   extends StandardSession 
+   implements Externalizable
+{
+   private static final long serialVersionUID = -758573655613558722L;
+   protected static Logger log = Logger.getLogger(ClusteredSession.class);
+
+   // ----------------------------------------------------- Instance Variables
+   /**
+    * Descriptive information describing this Session implementation.
+    */
+   protected static final String info = "ClusteredSession/1.0";
+
+   /**
+    * Set of attribute names which are not allowed to be replicated/persisted.
+    */
+   protected static final String[] excludedAttributes = {
+       Globals.SUBJECT_ATTR
+   };
+   
+   /**
+    * Set containing all members of {@link #excludedAttributes}.
+    */
+   protected static final Set replicationExcludes;
+   static
+   {
+      HashSet set = new HashSet();
+      for (int i = 0; i < excludedAttributes.length; i++)
+      {
+         set.add(excludedAttributes[i]);
+      }
+      replicationExcludes = Collections.unmodifiableSet(set);
+   }
+   
+   protected int invalidationPolicy;
+
+   /**
+    * If true, means the local in-memory session data contains
+    * changes that have not been published to the distributed cache.
+    * 
+    * @deprecated not used
+    */
+   protected transient boolean isSessionModifiedSinceLastSave;
+   
+   /**
+    * If true, means the local in-memory session data contains metadata
+    * changes that have not been published to the distributed cache. 
+    */
+   protected transient boolean sessionMetadataDirty;
+   
+   /**
+    * If true, means the local in-memory session data contains attribute
+    * changes that have not been published to the distributed cache. 
+    */
+   protected transient boolean sessionAttributesDirty;
+   
+   /**
+    * The last version that was passed to {@link #setOutdatedVersion} or
+    * <code>0</code> if <code>setIsOutdated(false)</code> was subsequently called.
+    */
+   protected transient int outdatedVersion;
+   
+   /**
+    * The last time {@link #setIsOutdated  setIsOutdated(true)} was called or
+    * <code>0</code> if <code>setIsOutdated(false)</code> was subsequently called.
+    */
+   protected transient long outdatedTime;
+
+   /**
+    * Version number to track cache invalidation. If any new version number is 
+    * greater than this one, it means the data it holds is newer than this one.
+    */
+   protected int version;
+
+   /**
+    * The session's id with any jvmRoute removed.
+    */
+   protected transient String realId;
+   
+   /**
+    * Whether JK is being used, in which case our realId will
+    * not match our id
+    */
+   private transient boolean useJK;
+   
+   /**
+    * Timestamp when we were last replicated.
+    */
+   protected transient long lastReplicated;
+
+   /**
+    * Maximum percentage of the inactive interval this session
+    * should be allowed to go unreplicated if access to the
+    * session doesn't mark it as dirty. Drives the calculation
+    * of maxUnreplicatedInterval.
+    */
+   protected transient int maxUnreplicatedFactor = 80;
+   
+   /**
+    * Maximum number of milliseconds this session
+    * should be allowed to go unreplicated if access to the
+    * session doesn't mark it as dirty. 
+    */
+   protected transient long maxUnreplicatedInterval;
+   
+   /**
+    * Whether any of this session's attributes implement
+    * HttpSessionActivationListener.
+    */
+   protected transient Boolean hasActivationListener;
+   
+   /**
+    * Has this session only been accessed once?
+    */
+   protected transient boolean firstAccess;
+
+   /**
+    * The string manager for this package.
+    */
+   protected static StringManager sm =
+      StringManager.getManager(ClusteredSession.class.getPackage().getName());
+
+   /** 
+    * Create a new ClusteredSession.
+    *
+    * @param manager the session's manager
+    *  
+    * @deprecated use {@link ClusteredSession(AbstractJBossManager, boolean)}
+    */
+   protected ClusteredSession(AbstractJBossManager manager)
+   {
+      this(manager, false);
+   }
+   
+   protected ClusteredSession(AbstractJBossManager manager, boolean useJK)
+   {
+      super(manager);
+      invalidationPolicy = manager.getInvalidateSessionPolicy();
+      this.useJK = useJK;
+      this.firstAccess = true;
+      calcMaxUnreplicatedInterval();
+   }
+
+   /**
+    * Check to see if the session data is still valid. Outdated here means 
+    * that the in-memory data is not in sync with one in the data store.
+    * 
+    * @return
+    */
+   public boolean isOutdated()
+   {
+      return thisAccessedTime < outdatedTime;
+   }
+
+   /**
+    * Marks this session as outdated or up-to-date vis-a-vis the distributed
+    * cache.
+    * 
+    * @param outdated
+    * 
+    * @deprecated use {@link #setOutdatedVersion(int)} and {@link #clearOutdated()}
+    */
+   public void setIsOutdated(boolean outdated)
+   {
+      if (outdated)
+         outdatedTime = System.currentTimeMillis();
+      else
+         clearOutdated();
+   }
+   
+   public void setOutdatedVersion(int version)
+   {
+      this.outdatedVersion = version;
+      outdatedTime = System.currentTimeMillis();
+   }
+   
+   public void clearOutdated()
+   {
+      // Only overwrite the access time if access() hasn't been called
+      // since setOutdatedVersion() was called
+      if (outdatedTime > thisAccessedTime)
+      {
+         lastAccessedTime = thisAccessedTime;
+         thisAccessedTime = outdatedTime;
+      }
+      outdatedTime = 0;
+      
+      // Only overwrite the version if the outdated version is greater
+      // Otherwise when we first unmarshal a session that has been
+      // replicated many times, we will reset the version to 0
+      if (outdatedVersion > version)
+         version = outdatedVersion;
+      
+      outdatedVersion = 0;
+   }
+   
+   public void updateAccessTimeFromOutdatedTime()
+   {
+      if (outdatedTime > thisAccessedTime)
+      {
+         lastAccessedTime = thisAccessedTime;
+         thisAccessedTime = outdatedTime;
+      }
+      outdatedTime = 0;
+   }
+
+   /**
+    * Gets the session id with any appended jvmRoute info removed.
+    *
+    * @see #getUseJK()
+    */
+   public String getRealId()
+   {
+      return realId;
+   }
+
+   private void parseRealId(String sessionId)
+   {
+      String newId = null;
+      if (useJK)
+         newId = Util.getRealId(sessionId);
+      else
+         newId = sessionId;
+      
+      // realId is used in a lot of map lookups, so only replace it
+      // if the new id is actually different -- preserve object identity
+      if (!newId.equals(realId))
+         realId = newId;
+   }
+
+   /**
+    * This is called specifically for failover case using mod_jk where the new 
+    * session has this node name in there. As a result, it is safe to just 
+    * replace the id since the backend store is using the "real" id
+    * without the node name.
+    * 
+    * @param id
+    */
+   public void resetIdWithRouteInfo(String id)
+   {
+      this.id = id;
+      parseRealId(id);
+   }
+
+   public boolean getUseJK()
+   {
+      return useJK;
+   }
+
+   /**
+    * Check to see if the input version number is greater than I am. If it is, 
+    * it means we will need to invalidate the in-memory cache.
+    * @param version
+    * @return
+    */
+   public boolean isNewData(int version)
+   {
+      return (this.version < version);
+   }
+
+   public int getVersion()
+   {
+      return version;
+   }
+   
+   public void setVersion(int version)
+   {
+      this.version = version;
+   }
+
+   /**
+    * There are couple ways to generate this version number. 
+    * But we will stick with the simple one of incrementing for now.
+    * 
+    * @return the new version
+    */
+   public int incrementVersion()
+   {
+      return version++;
+   }
+
+   /**
+    * Gets the maximum percentage of the <code>maxInactiveInterval</code>
+    * beyond which a session should be replicated upon access even if it 
+    * isn't dirty.  Used to ensure that even a read-only session gets 
+    * replicated before it expires, so that it isn't removed from other
+    * nodes.
+    * 
+    * @return  an int between 1 and 100, or -1 if replicating on access is
+    *          disabled
+    */
+   public int getMaxUnreplicatedFactor()
+   {
+      return maxUnreplicatedFactor;
+   }
+
+   /**
+    * Sets the maximum percentage of the <code>maxInactiveInterval</code>
+    * beyond which a session should be replicated upon access even if it 
+    * isn't dirty.  Used to ensure that even a read-only session gets 
+    * replicated before it expires, so that it isn't removed from other
+    * nodes.
+    * 
+    * @param maxUnreplicatedFactor  an int between 1 and 100, or -1 to
+    *                               disable replicating on access
+    * 
+    * @throws IllegalArgumentException if the factor isn't -1 or between
+    *                                  1 and 100
+    */
+   public void setMaxUnreplicatedFactor(int factor)
+   {
+      if ((factor != -1 && factor < 1) || factor > 100)
+         throw new IllegalArgumentException("Invalid factor " + factor +
+                                   " -- must be between 1 and 100 or -1");
+      this.maxUnreplicatedFactor = factor;
+      calcMaxUnreplicatedInterval();
+   }
+   
+
+   /**
+    * Overrides the superclass to calculate 
+    * {@link #getMaxUnreplicatedInterval() maxUnreplicatedInterval}.
+    */
+   public void setMaxInactiveInterval(int interval)
+   {
+      super.setMaxInactiveInterval(interval);
+      calcMaxUnreplicatedInterval();
+      sessionMetadataDirty();
+   }
+
+   /**
+    * Gets the time {@link #updateLastReplicated()} was last called, or
+    * <code>0</code> if it has never been called.
+    */
+   public long getLastReplicated()
+   {
+      return lastReplicated;
+   }
+   
+   /**
+    * Sets the {@link #getLastReplicated() lastReplicated} field to
+    * the current time.
+    */
+   public void updateLastReplicated()
+   {
+      lastReplicated = System.currentTimeMillis();
+   }
+
+   public long getMaxUnreplicatedInterval()
+   {
+      return maxUnreplicatedInterval;
+   }
+   
+   public boolean getExceedsMaxUnreplicatedInterval()
+   {
+      boolean result = false;
+      
+      if (maxUnreplicatedInterval > 0) // -1 means ignore; 0 means expire now
+      {
+         result = ((System.currentTimeMillis() - lastReplicated) >= maxUnreplicatedInterval);
+      }      
+      
+      return result;
+   }
+   
+   private void calcMaxUnreplicatedInterval()
+   {
+      if (maxInactiveInterval < 0 || maxUnreplicatedFactor < 0)
+         maxUnreplicatedInterval = -1;
+      else
+         maxUnreplicatedInterval = maxInactiveInterval * maxUnreplicatedFactor / 100;
+   }
+
+   /**
+    * This is called after loading a session to initialize the transient values.
+    *
+    * @param manager
+    */
+   public abstract void initAfterLoad(AbstractJBossManager manager);
+
+   /**
+    * Propogate session to the internal store.
+    */
+   public abstract void processSessionRepl();
+
+   /**
+    * Remove myself from the internal store.
+    */
+   public abstract void removeMyself();
+
+   /**
+    * Remove myself from the <t>local</t> internal store.
+    */
+   public abstract void removeMyselfLocal();
+
+
+   // ----------------------------------------------- Overridden Public Methods
+
+   public void access()
+   {
+      super.access();
+
+      // JBAS-3528. If it's not the first access, make sure
+      // the 'new' flag is correct
+      if (!firstAccess && isNew)
+      {
+         setNew(false);
+      }
+      
+      if (invalidationPolicy == WebMetaData.SESSION_INVALIDATE_ACCESS)
+      {
+         this.sessionMetadataDirty();
+      }
+   }
+   
+   
+   public void endAccess()
+   {
+      super.endAccess();
+      
+      if (firstAccess)
+      {
+         firstAccess = false;
+         // Tomcat marks the session as non new, but that's not really
+         // accurate per SRV.7.2, as the second request hasn't come in yet
+         // So, we fix that
+         isNew = true;
+      }
+   }
+
+   public Object getAttribute(String name)
+   {
+
+      if (!isValid())
+         throw new IllegalStateException
+            (sm.getString("clusteredSession.getAttribute.ise"));
+
+      return getAttributeInternal(name);
+   }
+
+   public Enumeration getAttributeNames()
+   {
+      if (!isValid())
+         throw new IllegalStateException
+            (sm.getString("clusteredSession.getAttributeNames.ise"));
+
+      return (new Enumerator(getAttributesInternal().keySet(), true));
+   }
+
+   public void setAttribute(String name, Object value)
+   {
+      // Name cannot be null
+      if (name == null)
+         throw new IllegalArgumentException
+            (sm.getString("clusteredSession.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("clusteredSession.setAttribute.ise"));
+      if ((manager != null) && manager.getDistributable() &&
+         !(canAttributeBeReplicated(value)))
+         throw new IllegalArgumentException
+            (sm.getString("clusteredSession.setAttribute.iae"));
+
+      // Construct an event with the new value
+      HttpSessionBindingEvent event = null;
+
+      // Call the valueBound() method if necessary
+      if (value instanceof HttpSessionBindingListener)
+      {
+         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 = setInternalAttribute(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);
+         }
+      }
+   }
+
+
+   /**
+    * Returns whether the attribute's type is one that can be replicated.
+    * 
+    * @param attribute  the attribute
+    * @return <code>true</code> if <code>attribute</code> is <code>null</code>,
+    *         <code>Serializable</code> or an array of primitives.
+    */
+   protected boolean canAttributeBeReplicated(Object attribute)
+   {
+      if (attribute instanceof Serializable || attribute == null)
+         return true;
+      Class clazz = attribute.getClass().getComponentType();
+      return (clazz != null && clazz.isPrimitive());
+   }
+
+   /**
+    * Invalidates this session and unbinds any objects bound to it.
+    * Overridden here to remove across the cluster instead of just expiring.
+    *
+    * @exception IllegalStateException if this method is called on
+    *  an invalidated session
+    */
+   public void invalidate()
+   {
+      if (!isValid())
+         throw new IllegalStateException(sm.getString("clusteredSession.invalidate.ise"));
+
+      // Cause this session to expire globally
+      boolean notify = true;
+      boolean localCall = true;
+      boolean localOnly = false;
+      expire(notify, localCall, localOnly);
+   }
+    
+    
+   /**
+    * Overrides the {@link StandardSession#isValid() superclass method}
+    * to call {@ #isValid(boolean) isValid(true)}.
+    */
+   public boolean isValid()
+   {
+      return isValid(true);
+   }
+    
+   /**
+    * Returns whether the current session is still valid, but
+    * only calls {@link #expire(boolean)} for timed-out sessions
+    * if <code>expireIfInvalid</code> is <code>true</code>.
+    * 
+    * @param expireIfInvalid  <code>true</code> if sessions that have
+    *                         been timed out should be expired
+    */
+   public boolean isValid(boolean expireIfInvalid)
+   {
+      if (this.expiring)
+      {
+         return true;
+      }
+
+      if (!this.isValid)
+      {
+         return false;
+      }
+
+      if (ACTIVITY_CHECK && accessCount.get() > 0)
+      {
+          return true;
+      }
+
+      if (maxInactiveInterval >= 0)
+      {
+         long timeNow = System.currentTimeMillis();
+         int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);
+         if (timeIdle >= maxInactiveInterval)
+         {
+            if (expireIfInvalid)
+               expire(true);
+            else
+               return false;
+         }
+      }
+
+      return (this.isValid);
+       
+   }
+
+   /**
+    * Expires the session, but in such a way that other cluster nodes
+    * are unaware of the expiration.
+    *
+    * @param notify
+    */
+   public void expire(boolean notify)
+   {
+      boolean localCall = true;
+      boolean localOnly = true;
+      expire(notify, localCall, localOnly);
+   }
+
+   /**
+    * Expires the session, notifying listeners and possibly the manager.
+    * <p>
+    * <strong>NOTE:</strong> The manager will only be notified of the expiration
+    * if <code>localCall</code> is <code>true</code>; otherwise it is the 
+    * responsibility of the caller to notify the manager that the session is 
+    * expired. (In the case of JBossCacheManager, it is the manager itself
+    * that makes such a call, so it of course is aware).
+    * </p>
+    * 
+    * @param notify    whether servlet spec listeners should be notified
+    * @param localCall <code>true</code> if this call originated due to local 
+    *                  activity (such as a session invalidation in user code
+    *                  or an expiration by the local background processing
+    *                  thread); <code>false</code> if the expiration
+    *                  originated due to some kind of event notification
+    *                  from the cluster.
+    * @param localOnly  <code>true</code> if the expiration should not be
+    *                   announced to the cluster, <code>false</code> if other
+    *                   cluster nodes should be made aware of the expiration.
+    *                   Only meaningful if <code>localCall</code> is
+    *                   <code>true</code>.
+    */
+   public void expire(boolean notify, boolean localCall, boolean localOnly)
+   {
+      if (log.isDebugEnabled())
+      {
+         log.debug("The session has expired with id: " + id + 
+                   " -- is it local? " + localOnly);
+      }
+      
+      // If another thread is already doing this, stop
+      if (expiring)
+         return;
+
+      synchronized (this)
+      {
+         // If we had a race to this sync block, another thread may
+         // have already completed expiration.  If so, don't do it again
+         if (!isValid)
+            return;
+
+         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);
+               }
+            }
+         }
+         if (ACTIVITY_CHECK) {
+             accessCount.set(0);
+         }
+
+         // Notify interested session event listeners. 
+         if (notify)
+         {
+            fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
+         }
+
+         // JBAS-1360 -- Unbind any objects associated with this session
+         String keys[] = keys();
+         for (int i = 0; i < keys.length; i++)
+             removeAttributeInternal(keys[i], localCall, localOnly, notify);
+
+         // Remove this session from our manager's active sessions
+         removeFromManager(localCall, localOnly);
+
+         // We have completed expire of this session
+         setValid(false);
+         expiring = false;
+      }
+
+   }
+   
+   /**
+    * Advise our manager to remove this expired session.
+    * 
+    * @param localCall whether this call originated from local activity
+    *                  or from a remote invalidation.  In this default
+    *                  implementation, this parameter is ignored.
+    * @param localOnly whether the rest of the cluster should be made aware
+    *                  of the removal
+    */
+   protected void removeFromManager(boolean localCall, boolean localOnly)
+   {
+      if(localOnly)
+            {
+                ((AbstractJBossManager) manager).removeLocal(this);
+      } 
+      else
+      {
+         manager.remove(this);
+      }
+   }
+
+   public void passivate()
+   {
+      // Notify interested session event listeners
+      fireSessionEvent(Session.SESSION_PASSIVATED_EVENT, null);
+
+      if (hasActivationListener != Boolean.FALSE)
+      {
+         boolean hasListener = false;
+         
+         // Notify ActivationListeners
+         HttpSessionEvent event = null;
+         String keys[] = keys();
+         Map attrs = getAttributesInternal();
+         for (int i = 0; i < keys.length; i++) 
+         {
+            Object attribute = attrs.get(keys[i]);
+            if (attribute instanceof HttpSessionActivationListener) 
+            {
+               hasListener = true;
+               
+               if (event == null)
+                  event = new HttpSessionEvent(getSession());
+               try 
+               {
+                  ((HttpSessionActivationListener)attribute).sessionWillPassivate(event);
+               }
+               catch (Throwable t) 
+               {
+                  manager.getContainer().getLogger().error
+                         (sm.getString("clusteredSession.attributeEvent"), t);
+               }
+            }
+         }
+         
+         hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+      }
+   }
+
+   public void activate()
+   {
+      // Notify interested session event listeners
+      fireSessionEvent(Session.SESSION_ACTIVATED_EVENT, null);
+
+      if (hasActivationListener != Boolean.FALSE)
+      {
+         // Notify ActivationListeners
+
+         boolean hasListener = false;
+         
+         HttpSessionEvent event = null;
+         String keys[] = keys();
+         Map attrs = getAttributesInternal();
+         for (int i = 0; i < keys.length; i++) 
+         {
+            Object attribute = attrs.get(keys[i]);
+            if (attribute instanceof HttpSessionActivationListener) 
+            {
+               hasListener = true;
+               if (event == null)
+                  event = new HttpSessionEvent(getSession());
+               try 
+               {
+                  ((HttpSessionActivationListener)attribute).sessionDidActivate(event);
+               }
+               catch (Throwable t) 
+               {
+                  manager.getContainer().getLogger().error
+                         (sm.getString("clusteredSession.attributeEvent"), t);
+               }
+            }
+         }
+         
+         hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+      }
+   }
+
+   // TODO uncomment when work on JBAS-1900 is completed
+//   public void removeNote(String name)
+//   {
+//      // FormAuthenticator removes the username and password because
+//      // it assumes they are not needed if the Principal is cached,
+//      // but they are needed if the session fails over, so ignore
+//      // the removal request.
+//      // TODO discuss this on Tomcat dev list to see if a better
+//      // way of handling this can be found
+//      if (Constants.SESS_USERNAME_NOTE.equals(name) 
+//            || Constants.SESS_PASSWORD_NOTE.equals(name))
+//      {
+//         if (log.isDebugEnabled())
+//         {
+//            log.debug("removeNote(): ignoring removal of note " + name);
+//         }
+//      }
+//      else
+//      {
+//         super.removeNote(name);
+//      }
+//      
+//   }
+
+   // TODO uncomment when work on JBAS-1900 is completed
+//   public void setNote(String name, Object value)
+//   {
+//      super.setNote(name, value);
+//      
+//      if (Constants.SESS_USERNAME_NOTE.equals(name) 
+//            || Constants.SESS_PASSWORD_NOTE.equals(name))
+//      {
+//         sessionIsDirty();
+//      }
+//   }
+
+   /**
+    * Override the superclass to additionally reset this class' fields.
+    * <p>
+    * <strong>NOTE:</strong> It is not anticipated that this method will be
+    * called on a ClusteredSession, but we are overriding the method to be
+    * thorough.
+    * </p>
+    */
+   public void recycle()
+   {
+      super.recycle();
+      
+      // Fields that the superclass isn't clearing
+      listeners.clear();
+      support = new PropertyChangeSupport(this);
+      
+      invalidationPolicy = 0;
+      outdatedTime = 0;
+      outdatedVersion = 0;
+      sessionAttributesDirty = false;
+      sessionMetadataDirty = false;
+      realId = null;
+      useJK = false;
+      version = 0;
+      hasActivationListener = null;
+      lastReplicated = 0;
+      maxUnreplicatedFactor = 80;
+      calcMaxUnreplicatedInterval();
+   }
+   
+   /**
+    * 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)
+   {
+      super.setCreationTime(time);
+      sessionMetadataDirty();
+   }
+   
+   /**
+    * Overrides the superclass method to also set the
+    * {@link #getRealId() realId} property.
+    */
+   public void setId(String id)
+   {
+      // Parse the real id first, as super.setId() calls add(),
+      // which depends on having the real id
+      parseRealId(id);
+      super.setId(id);
+   }
+
+   /**
+    * 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);
+
+      if ((oldPrincipal != null && !oldPrincipal.equals(principal)) ||
+         (oldPrincipal == null && principal != null))
+         sessionMetadataDirty();
+
+   }
+   
+   public void setNew(boolean isNew)
+   {
+      super.setNew(isNew);
+      // Don't replicate metadata just 'cause its the second request
+      // The only effect of this is if someone besides a request 
+      // deserializes metadata from the distributed cache, this 
+      // field may be out of date.
+      // If a request accesses the session, the access() call will
+      // set isNew=false, so the request will see the correct value
+      // sessionMetadataDirty();
+   }
+   
+   public void setValid(boolean isValid)
+   {
+      super.setValid(isValid);
+      sessionMetadataDirty();
+   }
+
+   public String toString()
+   {
+      StringBuffer buf = new StringBuffer();
+      buf.append("id: " +id).append(" lastAccessedTime: " +lastAccessedTime).append(
+              " version: " +version).append(" lastOutdated: " + outdatedTime);
+
+      return buf.toString();
+   }
+
+   // ---------------------------------------------------------  Externalizable
+
+   /**
+    * Reads all non-transient state from the ObjectOutput <i>except
+    * the attribute map</i>.  Subclasses that wish the attribute map
+    * to be read should override this method and 
+    * {@link #writeExternal(ObjectOutput) writeExternal()}.
+    * 
+    * <p>
+    * This method is deliberately public so it can be used to reset
+    * the internal state of a session object using serialized
+    * contents replicated from another JVM via JBossCache.
+    * </p>
+    * 
+    * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+    */
+   public void readExternal(ObjectInput in) 
+      throws IOException, ClassNotFoundException
+   {
+      synchronized (this)
+      {
+         //    From StandardSession
+         id                  = in.readUTF();
+         creationTime        = in.readLong();
+         lastAccessedTime    = in.readLong();
+         maxInactiveInterval = in.readInt();
+         isNew               = in.readBoolean();
+         isValid             = in.readBoolean();
+         thisAccessedTime    = in.readLong();
+         
+         // From ClusteredSession
+         invalidationPolicy  = in.readInt();
+         version             = in.readInt();
+   
+         // Get our id without any jvmRoute appended
+         parseRealId(id);
+         
+         // We no longer know if we have an activationListener
+         hasActivationListener = null;
+         
+         // If the session has been replicated, any subsequent
+         // access cannot be the first.
+         this.firstAccess = false;
+         
+         // TODO uncomment when work on JBAS-1900 is completed      
+//         // Session notes -- for FORM auth apps, allow replicated session 
+//         // to be used without requiring a new login
+//         // We use the superclass set/removeNote calls here to bypass
+//         // the custom logic we've added      
+//         String username     = (String) in.readObject();
+//         if (username != null)
+//         {
+//            super.setNote(Constants.SESS_USERNAME_NOTE, username);
+//         }
+//         else
+//         {
+//            super.removeNote(Constants.SESS_USERNAME_NOTE);
+//         }
+//         String password     = (String) in.readObject();
+//         if (password != null)
+//         {
+//            super.setNote(Constants.SESS_PASSWORD_NOTE, password);
+//         }
+//         else
+//         {
+//            super.removeNote(Constants.SESS_PASSWORD_NOTE);
+//         }
+      }
+   }
+
+   
+   /**
+    * Writes all non-transient state to the ObjectOutput <i>except
+    * the attribute map</i>.  Subclasses that wish the attribute map
+    * to be written should override this method and append it.
+    *  
+    * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+    */
+   public void writeExternal(ObjectOutput out) 
+      throws IOException
+   {
+      synchronized (this)
+      {
+         // From StandardSession
+         out.writeUTF(id);
+         out.writeLong(creationTime);
+         out.writeLong(lastAccessedTime);
+         out.writeInt(maxInactiveInterval);
+         out.writeBoolean(isNew);
+         out.writeBoolean(isValid);
+         out.writeLong(thisAccessedTime);
+         
+         // From ClusteredSession
+         out.writeInt(invalidationPolicy);
+         out.writeInt(version);
+         
+         // TODO uncomment when work on JBAS-1900 is completed  
+//         // Session notes -- for FORM auth apps, allow replicated session 
+//         // to be used without requiring a new login 
+//         String username = (String) getNote(Constants. SESS_USERNAME_NOTE);
+//         log.debug(Constants.SESS_USERNAME_NOTE + " = " + username);
+//         out.writeObject(username);
+//         String password = (String) getNote(Constants.SESS_PASSWORD_NOTE);
+//         log.debug(Constants.SESS_PASSWORD_NOTE + " = " + password);
+//         out.writeObject(password);
+      }
+   }
+
+   // ----------------------------------------------------- Protected Methods
+   
+   /**
+    * Removes any attribute whose name is found in {@link #excludedAttributes}
+    * from <code>attributes</code> and returns a Map of all such attributes.
+    * 
+    * @param attributes source map from which excluded attributes are to be
+    *                   removed.
+    *                   
+    * @return Map that contains any attributes removed from 
+    *         <code>attributes</code>, or <code>null</code> if no attributes
+    *         were removed.
+    */
+   protected static Map removeExcludedAttributes(Map attributes)
+   {
+      Map excluded = null;
+      for (int i = 0; i < excludedAttributes.length; i++) {
+         Object attr = attributes.remove(excludedAttributes[i]);
+         if (attr != null)
+         {
+            if (log.isTraceEnabled())
+            {
+               log.trace("Excluding attribute " + excludedAttributes[i] + 
+                         " from replication");
+            }
+            if (excluded == null)
+            {
+               excluded = new HashMap();
+            }
+            excluded.put(excludedAttributes[i], attr);
+         }
+      }
+      
+      return excluded;      
+   }
+   
+   // -------------------------------------- Internal protected method override
+
+   /**
+    * Method inherited from Tomcat. Return zero-length based string if not found.
+    */
+   protected String[] keys()
+   {
+      return ((String[]) getAttributesInternal().keySet().toArray(EMPTY_ARRAY));
+   }
+
+   /**
+    * Called by super.removeAttribute().
+    * 
+    * @param name      the attribute name 
+    * @param notify    <code>true</code> if listeners should be notified
+    */
+   protected void removeAttributeInternal(String name, boolean notify)
+   {
+      boolean localCall = true;
+      boolean localOnly = false;
+      removeAttributeInternal(name, localCall, localOnly, notify);
+   }
+   
+   /**
+    * Remove the attribute from the local cache and possibly the distributed
+    * cache, plus notify any listeners
+    * 
+    * @param name      the attribute name
+    * @param localCall <code>true</code> if this call originated from local 
+    *                  activity (e.g. a removeAttribute() in the webapp or a 
+    *                  local session invalidation/expiration), 
+    *                  <code>false</code> if it originated due to an remote
+    *                  event in the distributed cache. 
+    * @param localOnly <code>true</code> if the removal should not be
+    *                  replicated around the cluster
+    * @param notify    <code>true</code> if listeners should be notified
+    */
+   protected void removeAttributeInternal(String name, 
+                                          boolean localCall, 
+                                          boolean localOnly,
+                                          boolean notify)
+   {
+
+      // Remove this attribute from our collection
+      Object value = removeAttributeInternal(name, localCall, localOnly);
+
+      // 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);
+         }
+      }
+
+   }
+
+   /**
+    * Exists in this class solely to act as an API-compatible bridge to the 
+    * deprecated {@link #removeJBossInternalAttribute(String)}.  
+    * JBossCacheClusteredSession subclasses will override this to call their
+    * own methods that make use of localCall and localOnly
+    * 
+    * @param name
+    * @param localCall
+    * @param localOnly
+    * @return
+    * 
+    * @deprecated will be replaced by removeJBossInternalAttribute(String, boolean, boolean)
+    */
+   protected Object removeAttributeInternal(String name, 
+                                            boolean localCall, 
+                                            boolean localOnly)
+   {
+      return removeJBossInternalAttribute(name);
+   }
+
+   protected Object getAttributeInternal(String name)
+   {
+      return getJBossInternalAttribute(name);
+   }
+
+   protected Map getAttributesInternal()
+   {
+      return getJBossInternalAttributes();
+   }
+
+   protected Object setInternalAttribute(String name, Object value)
+   {
+      if (value instanceof HttpSessionActivationListener)
+         hasActivationListener = Boolean.TRUE;
+      
+      return setJBossInternalAttribute(name, value);
+   }
+
+   // ------------------------------------------ JBoss internal abstract method
+
+   protected abstract Object getJBossInternalAttribute(String name);
+
+   /** @deprecated will be replaced by removeJBossInternalAttribute(String, boolean, boolean) */
+   protected abstract Object removeJBossInternalAttribute(String name);
+
+   protected abstract Map getJBossInternalAttributes();
+
+   protected abstract Object setJBossInternalAttribute(String name, Object value);
+
+   // ------------------------------------------------ Session Package Methods
+
+   protected void sessionAttributesDirty()
+   {
+      sessionAttributesDirty = true;
+   }
+   
+   protected boolean getSessionAttributesDirty()
+   {
+      return sessionAttributesDirty;
+   }
+   
+   protected void sessionMetadataDirty()
+   {
+      sessionMetadataDirty = true;
+   }
+   
+   protected boolean getSessionMetadataDirty()
+   {
+      return sessionMetadataDirty;
+   }
+   
+   /**
+    * Calls {@link #sessionAttributesDirty()} and 
+    * {@link #sessionMetadataDirty()}.
+    *
+    * @deprecated use one of the more fine-grained methods.
+    */
+   protected void sessionDirty()
+   {
+      sessionAttributesDirty();
+      sessionMetadataDirty();
+   }
+
+   public boolean isSessionDirty()
+   {
+      return sessionAttributesDirty || sessionMetadataDirty;
+   }
+   
+   public boolean getReplicateSessionBody()
+   {
+      return sessionMetadataDirty || getExceedsMaxUnreplicatedInterval();
+   }
+
+   protected boolean isGetDirty(Object attribute)
+   {
+      boolean result = false;
+      switch (invalidationPolicy)
+      {
+         case (WebMetaData.SESSION_INVALIDATE_SET_AND_GET):
+            result = true;
+            break;
+         case (WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET):
+            result = isMutable(attribute);
+            break;
+         default:
+            // result is false
+}
+      return result;
+   }
+   
+   protected boolean isMutable(Object attribute)
+   {
+      return attribute != null &&
+                !(attribute instanceof String ||
+                  attribute instanceof Number ||
+                  attribute instanceof Character ||
+                  attribute instanceof Boolean);
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,149 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.*;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+
+/**
+ * This Valve detects all sessions that were used in a request. All sessions are given to a snapshot
+ * manager that handles the distribution of modified sessions.
+ * <p/>
+ * TOMCAT 4.1.12 UPDATE: Added findLifecycleListeners() to comply with the latest
+ * Lifecycle interface.
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public class ClusteredSessionValve extends ValveBase implements Lifecycle
+{
+   // The info string for this Valve
+   private static final String info = "ClusteredSessionValve/1.0";
+
+   // Valve-lifecycle_ helper object
+   protected LifecycleSupport support = new LifecycleSupport(this);
+
+   /**
+    * Create a new Valve.
+    */
+   public ClusteredSessionValve()
+   {
+      super();
+   }
+
+   /**
+    * Get information about this Valve.
+    */
+   public String getInfo()
+   {
+      return info;
+   }
+
+   /**
+    * Valve-chain handler method.
+    * This method gets called when the request goes through the Valve-chain. Our session replication mechanism replicates the
+    * session after request got through the servlet code.
+    *
+    * @param request  The request object associated with this request.
+    * @param response The response object associated with this request.
+    */
+   public void invoke(Request request, Response response) throws IOException, ServletException
+   {
+      // Initialize the context and store the request and response objects 
+      // for any clustering code that has no direct access to these objects
+      SessionReplicationContext.enterWebapp(request, response, true);
+      try
+      {  
+         // let the servlet invocation go through
+         getNext().invoke(request, response);
+      }
+      finally // We replicate no matter what
+      {
+         // --> We are now after the servlet invocation
+         try
+         {
+            SessionReplicationContext ctx = SessionReplicationContext.exitWebapp();
+            
+            if (ctx.getSoleSnapshotManager() != null)
+            {
+               ctx.getSoleSnapshotManager().snapshot(ctx.getSoleSession());
+            }
+            else
+            {
+               // Cross-context request touched multiple sesssions;
+               // need to replicate them all
+               Map sessions = ctx.getCrossContextSessions();
+               if (sessions != null && sessions.size() > 0)
+               {
+                  for (Iterator iter = sessions.entrySet().iterator(); iter.hasNext();)
+                  {
+                     Map.Entry entry = (Map.Entry) iter.next();               
+                     ((SnapshotManager) entry.getValue()).snapshot((ClusteredSession) entry.getKey());
+                  }
+               }
+            }
+         }
+         finally
+         {
+            SessionReplicationContext.finishCacheActivity();
+         }
+         
+      }
+   }
+
+   // Lifecylce-interface
+   public void addLifecycleListener(LifecycleListener listener)
+   {
+      support.addLifecycleListener(listener);
+   }
+
+   public void removeLifecycleListener(LifecycleListener listener)
+   {
+      support.removeLifecycleListener(listener);
+   }
+
+   public LifecycleListener[] findLifecycleListeners()
+   {
+      return support.findLifecycleListeners();
+   }
+
+   public void start() throws LifecycleException
+   {
+      support.fireLifecycleEvent(START_EVENT, this);
+   }
+
+   public void stop() throws LifecycleException
+   {
+      support.fireLifecycleEvent(STOP_EVENT, this);
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+
+/**
+ * This exception is thrown when the clustered HTTPSession-service
+ * is not found
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @version $Revision: 57206 $
+ */
+public class ClusteringNotSupportedException extends Exception
+{
+   /** The serialVersionUID */
+   private static final long serialVersionUID = -2365801349845482947L;
+
+   public ClusteringNotSupportedException(String message)
+   {
+      super(message);
+   }
+   
+   public ClusteringNotSupportedException(String message, Throwable cause)
+   {
+      super(message, cause);
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,364 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.aspects.patterns.observable.Observer;
+import org.jboss.aspects.patterns.observable.Subject;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpSessionActivationListener;
+
+/**
+ * <p>
+ * Implementation of a clustered session for JBossCacheManager. The replication granularity
+ * level is field based; that is, we replicate only the dirty field in a POJO that is part of
+ * a session attribute. E.g., once a user do setAttribute("pojo", pojo), pojo will be monitored
+ * automatically for field changes and accessing. It offers couple of advantages:
+ * <ul>
+ * <li>pojo.setName(), for example, will only replicate the name field in the pojo. And thus is more efficient.</li>
+ * <li>If pojo has a complex object graph, we will handle that automtically providing that the
+ * children object is also aspectized.</li>
+ * </ul>
+ * Note that in current version, all the attributes and its associated childre graph objects are
+ * required to be aspectized. That is, you can't simply declare them as Serializable. This is restricted
+ * because of the marshalling/unmarshalling issue.</p>
+ *
+ * <p>We use JBossCache for our internal, replicated data store.
+ * The internal structure is like in JBossCache:
+ * <pre>
+ * /JSESSION
+ *    /hostname
+ *       /web_app_path    (path + session id is unique)
+ *          /id   Map(id, session)
+ *                   (VERSION, version)
+ *             /ATTRIBUTE    Map(can be empty)
+ *                /pojo      Map(field name, field value) (pojo naming is by field.getName())
+ *
+ * </pre>
+ * <p/>
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ * 
+ * @version $Revision: 57206 $
+ */
+class FieldBasedClusteredSession
+   extends JBossCacheClusteredSession implements Observer
+{
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 8347544395334247623L;
+   
+   /**
+    * Descriptive information describing this Session implementation.
+    */
+   protected static final String info = "FieldBasedClusteredSession/1.0";
+   
+   protected transient Map attributes_ = Collections.synchronizedMap(new HashMap());
+
+   public FieldBasedClusteredSession(JBossCacheManager manager)
+   {
+      super(manager);
+   }
+
+   // ----------------------------------------------- Overridden Public Methods
+
+
+   /**
+    * Override the superclass to additionally reset this class' fields.
+    * <p>
+    * <strong>NOTE:</strong> It is not anticipated that this method will be
+    * called on a ClusteredSession, but we are overriding the method to be
+    * thorough.
+    * </p>
+    */
+   public void recycle()
+   {
+      super.recycle();
+
+      attributes_.clear();
+   }
+
+   /**
+    * Return a string representation of this object.
+    */
+   public String toString()
+   {
+
+      StringBuffer sb = new StringBuffer();
+      sb.append("FieldBasedClusteredSession[");
+      sb.append(super.toString());
+      sb.append("]");
+      return (sb.toString());
+
+   }
+
+   // The superclass version of processSessionRepl is fine; it will remove
+   // the session metadata, and any attribute changes have been picked up
+   // for replication as they were made; no need to do anything here
+//   public synchronized void processSessionRepl()
+//   {
+//      super.processSessionRepl();
+//   }
+
+   public void removeMyself()
+   {
+      // This is a shortcut to remove session and it's child attributes.
+      // Note that there is no need to remove attribute first since caller 
+      // will do that already.
+      proxy_.removeSession(realId);
+   }
+
+   public void removeMyselfLocal()
+   {
+      // Need to evict attribute first before session to clean up everything.
+      // Note that there is no need to remove attributes first since caller 
+      // will do that already.
+      // BRIAN -- the attributes *are* already evicted, but we leave the
+      // removePojosLocal call here in order to evict the ATTRIBUTE node.  
+      // Otherwise empty nodes for the session root and child ATTRIBUTE will 
+      // remain in the tree and screw up our list of session names.
+      proxy_.removePojosLocal(realId);
+      proxy_.removeSessionLocal(realId);
+   }
+
+   // ------------------------------------------------ JBoss internal abstract method
+
+   /**
+    * Populate the attributes stored in the distributed store to the local 
+    * transient map. Add ourself as an Observer to newly found attributes and 
+    * remove ourself as an Observer to existing attributes that are no longer
+    * in the distributed store.
+    */
+   protected void populateAttributes()
+   {
+      // Preserve any local attributes that were excluded from replication
+      Map excluded = removeExcludedAttributes(attributes_);
+      
+      Set keys = proxy_.getPojoKeys(realId);
+      Set oldKeys = new HashSet(attributes_.keySet());
+      
+      // Since we are going to touch each attribute, might as well
+      // check if we have any HttpSessionActivationListener
+      boolean hasListener = false;
+      
+      if (keys != null)
+      {
+         oldKeys.removeAll(keys); // only keys that no longer exist are left
+
+         for (Iterator it = keys.iterator(); it.hasNext(); )
+         {
+            String name = (String) it.next();
+            
+            Object oldAttrib = null;
+            Object newAttrib = proxy_.getPojo(realId, name);
+            if (newAttrib != null)
+            {
+               oldAttrib = attributes_.put(name, newAttrib);
+            
+               if (oldAttrib != newAttrib)
+               {
+                  // Need to observe this pojo as well
+                  // for any modification events.
+                  proxy_.addObserver(this, newAttrib);
+                  
+                  // Stop observing the old pojo
+                  proxy_.removeObserver(this, oldAttrib); // null pojo OK :)
+               }
+               
+               // Check if we have a listener
+               if (newAttrib instanceof HttpSessionActivationListener)
+                  hasListener = true;
+            }
+            else
+            {
+               // This shouldn't happen -- if we had a key, newAttrib s/b not null
+               
+               oldAttrib = attributes_.remove(name);
+               // Stop observing this pojo
+               proxy_.removeObserver(this, oldAttrib); // null pojo OK :)                 
+               
+            }
+         }
+      }
+      
+      hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+      
+      // Cycle through remaining old keys and remove them 
+      // and also remove ourself as Observer
+      for (Iterator it = oldKeys.iterator(); it.hasNext(); )
+      {
+         Object oldAttrib = attributes_.remove(it.next());
+         proxy_.removeObserver(this, oldAttrib); 
+      }
+      
+      // Restore any excluded attributes
+      if (excluded != null)
+         attributes.putAll(excluded);
+   }
+ 
+   protected Object getJBossInternalAttribute(String name)
+   {
+      // Check the local map first.
+      Object result = attributes_.get(name);
+      
+      // NOTE -- we no longer check with the store.  Attributes are only
+      // loaded from store during populateAttributes() call at beginning
+      // of request when we notice we are outdated.
+
+      // Do dirty check even if result is null, as w/ SET_AND_GET null
+      // still makes us dirty (ensures timely replication w/o using ACCESS)
+      if (isGetDirty(result))
+      {
+         sessionAttributesDirty();
+      }
+      
+      return result;
+   }
+
+   /**
+    * Overrides the superclass to treat classes implementing Subject
+    * as "immutable", since as an Observer we will detect any changes
+    * to those types.
+    */
+   protected boolean isMutable(Object attribute)
+   {
+      boolean pojo = (attribute instanceof Subject);
+      boolean mutable = (!pojo && super.isMutable(attribute));
+      return mutable;
+   }
+
+   protected Object removeJBossInternalAttribute(String name, boolean localCall, boolean localOnly)
+   {
+      // Remove it from the underlying store
+      if (localCall && !replicationExcludes.contains(name))
+      { 
+         if (localOnly)         
+            proxy_.removePojoLocal(realId, name);      
+         else
+            proxy_.removePojo(realId, name); 
+         
+         sessionAttributesDirty();
+      }
+      Object result = attributes_.remove(name);
+      if(result == null)
+      {
+         log.warn("removeJBossInternalAttribute(): null value to remove with key: "+ name);
+         return null;
+      }
+      proxy_.removeObserver(this, result);
+         
+      return result;
+   }
+
+   protected Map getJBossInternalAttributes()
+   {
+      return attributes_;
+   }
+
+   protected Set getJBossInternalKeys()
+   {
+      return attributes_.keySet();
+   }
+
+   /**
+    * Method inherited from Tomcat. Return zero-length based string if not found.
+    */
+   protected String[] keys()
+   {
+      return ((String[]) getJBossInternalKeys().toArray(EMPTY_ARRAY));
+   }
+
+   /**
+    * Overrides the superclass to allow instrumented classes and
+    * non-serializable Collections and Maps.
+    */
+   protected boolean canAttributeBeReplicated(Object attribute)
+   {
+      return (Util.checkPojoType(attribute));
+   }
+
+   /**
+    * This is the hook for setAttribute. Note that in this FieldBasedClusteredSession using aop,
+    * user should not call setAttribute call too often since this will re-connect the attribute with the internal
+    * cache (and this is expensive).
+    * @param key
+    * @param value
+    * @return Object
+    */
+   protected Object setJBossInternalAttribute(String key, Object value)
+   {
+      Object oldVal = null;
+      if (!replicationExcludes.contains(key))
+      {   
+         oldVal = proxy_.setPojo(realId, key, value);
+         if(oldVal != null)
+         {  // We are done with the old one.
+            proxy_.removeObserver(this, oldVal);
+         }
+   
+         if(value != null)
+         {
+            // Special case for Collection classes.
+            if( value instanceof Map || value instanceof Collection)
+            {
+               // We need to obtain the proxy first.
+               value = proxy_.getPojo(realId, key);
+            }
+
+            // Need to use obj since it can return as a proxy.
+            proxy_.addObserver(this, value);
+         }
+
+         // Only mark session dirty if we can replicate the attribute
+         sessionAttributesDirty();
+      }
+      
+      // Still need to put it in the map to track locally.
+      oldVal = attributes_.put(key, value);
+      
+      return oldVal;
+   }
+
+   /**
+    * Call back handler for the aop Subject/Observer pattern. 
+    * We subscribe to the event of field write and mark ourself dirty.
+    * 
+    * @param subject  the object we are Observing
+    */
+   public void fireChange(Subject subject)
+   {
+      // Currently we don't care who is modified, we will simply mark session is dirty for replication purpose.
+      if(log.isTraceEnabled())
+      {
+         log.trace("fireChange(): subject has changed: " +subject);
+      }
+      sessionAttributesDirty();
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,31 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+/**
+ * Marker interface for use in aop annotation declaration. For example, can use this in pojo
+ * annotation instead of declaring jboss-aop.xml. For a pojo annotation declaration, just declare
+ * this in the pojo class level, and then there is a corresponding jboss-aop.xml that is needed.
+ * @author Ben Wang
+ */
+public interface InstanceOfAopMarker {
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+
+/**
+ * A concrete implementation of the snapshot manager interface
+ * that does instant replication of a modified session
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public class InstantSnapshotManager extends SnapshotManager
+{
+   public InstantSnapshotManager(AbstractJBossManager manager, String path)
+   {
+      super(manager, path);
+   }
+
+   /**
+    * Instant replication of the modified session
+    */
+   public void snapshot(ClusteredSession session)
+   {
+      if (session != null)
+      {
+         try
+         {
+            getManager().storeSession(session);
+         }
+         catch (Exception e)
+         {
+            getLog().warn("Failed to replicate session " + session.getIdInternal(), e);
+         }
+      }
+   }
+
+   public void start()
+   {
+   }
+
+   public void stop()
+   {
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,200 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.jboss.logging.Logger;
+
+/**
+ * A snapshot manager that collects all modified sessions over a given
+ * period of time and distributes them en bloc.
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public class IntervalSnapshotManager extends SnapshotManager implements Runnable
+{
+   static Logger log = Logger.getLogger(IntervalSnapshotManager.class);
+
+   // the interval in ms
+   protected int interval = 1000;
+
+   // the modified sessions
+   protected Set sessions = new LinkedHashSet();
+
+   // the distribute thread
+   protected Thread thread = null;
+
+   // Is session processing allowed?
+   protected boolean processingAllowed = false;
+   
+   // has the thread finished?
+   protected boolean threadDone = false;
+
+   public IntervalSnapshotManager(AbstractJBossManager manager, String path)
+   {
+      super(manager, path);
+   }
+
+   public IntervalSnapshotManager(AbstractJBossManager manager, String path, int interval)
+   {
+      super(manager, path);
+      this.interval = interval;
+   }
+
+   /**
+    * Store the modified session in a hashmap for the distributor thread
+    */
+   public void snapshot(ClusteredSession session)
+   {
+      try
+      {      
+         // Don't hold a ref to the session for a long time
+         synchronized (sessions)
+         {
+            sessions.add(session);
+         }
+      }
+      catch (Exception e)
+      {
+         log.error("Failed to queue session " + session + " for replication", e);
+      }
+   }
+
+   /**
+    * Distribute all modified sessions
+    */
+   protected void processSessions()
+   {
+      ClusteredSession[] toProcess = null;
+      synchronized (sessions)
+      {
+         toProcess = new ClusteredSession[sessions.size()];
+         toProcess = (ClusteredSession[]) sessions.toArray(toProcess);
+         sessions.clear();
+      }
+
+      AbstractJBossManager mgr = getManager();
+      for (int i = 0; i < toProcess.length; i++)
+      {
+         // Confirm we haven't been stopped
+         if (!processingAllowed)
+            break;
+         
+         try
+         {
+            mgr.storeSession(toProcess[i]);
+         }
+         catch (Exception e)
+         {
+            getLog().error("Caught exception processing session " + toProcess[i].getRealId(), e);
+         }
+      }
+   }
+
+   /**
+    * Start the snapshot manager
+    */
+   public void start()
+   {
+      processingAllowed = true;
+      startThread();
+   }
+
+   /**
+    * Stop the snapshot manager
+    */
+   public void stop()
+   {
+      processingAllowed = false;
+      stopThread();
+      synchronized (sessions)
+      {
+         sessions.clear();
+      }
+   }
+
+   /**
+    * Start the distributor thread
+    */
+   protected void startThread()
+   {
+      if (thread != null)
+      {
+         return;
+      }
+
+      thread = new Thread(this, "ClusteredSessionDistributor[" + getContextPath() + "]");
+      thread.setDaemon(true);
+      thread.setContextClassLoader(getManager().getContainer().getLoader().getClassLoader());
+      threadDone = false;
+      thread.start();
+   }
+
+   /**
+    * Stop the distributor thread
+    */
+   protected void stopThread()
+   {
+      if (thread == null)
+      {
+         return;
+      }
+      threadDone = true;
+      thread.interrupt();
+      try
+      {
+         thread.join();
+      }
+      catch (InterruptedException e)
+      {
+      }
+      thread = null;
+   }
+
+   /**
+    * Thread-loop
+    */
+   public void run()
+   {
+      while (!threadDone)
+      {
+         try
+         {
+            Thread.sleep(interval);
+            processSessions();
+         }
+         catch (InterruptedException ie)
+         {
+            if (!threadDone)
+               getLog().error("Caught exception processing sessions", ie);
+         }
+         catch (Exception e)
+         {
+            getLog().error("Caught exception processing sessions", e);
+         }
+      }
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,959 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+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.ArrayList;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+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.Manager;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.BatchModeTransactionManagerLookup;
+import org.jboss.cache.PropertyConfigurator;
+import org.jboss.cache.aop.PojoCache;
+import org.jboss.cache.aop.PojoCacheMBean;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.web.tomcat.service.JBossWeb;
+
+/**
+ * A Tomcat <code>Cluster</code> implementation that uses a JBoss
+ * <code>TreeCache</code> to support intra-cluster session replication.
+ * <p>
+ * This class registers a <code>TreeCache</code> in JMX, making it 
+ * available to other users who wish to replicate data within the cluster.
+ * </p>
+ *  
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public class JBossCacheCluster
+   implements JBossCacheClusterMBean, Lifecycle 
+{
+   //  -------------------------------------------------------  Static Fields
+   
+   protected static final String info = "JBossCacheCluster/2.1";
+
+   public static Log log = LogFactory.getLog(JBossCacheCluster.class);
+
+   public static final String DEFAULT_CLUSTER_NAME = "Tomcat-Cluster";
+
+   /** TreeCache's isolation level */
+   public static final String DEFAULT_ISOLATION_LEVEL = "REPEATABLE_READ";
+   
+   /** TreeCache's cache mode */
+   public static final String DEFAULT_CACHE_MODE = "REPL_ASYNC";
+   
+   /** TreeCache's lock aquisition timeout */
+   public static final long DEFAULT_LOCK_TIMEOUT = 15000;
+   
+   /** TransactionManagerLookup implementation that the TreeCache should use. */
+   public static final String DEFAULT_TM_LOOKUP = 
+      BatchModeTransactionManagerLookup.class.getName();
+
+   public static final String DEFAULT_CACHE_CONFIG_PATH = "conf/cluster-cache.xml";
+   
+   //  -------------------------------------------------------  Instance Fields
+
+   /** Parent container of this cluster. */
+   private Container container = null;
+   
+   /** Our JMX Server. */
+   private MBeanServer mserver = null;
+   
+   /** Name under which we are registered in JMX */
+   private ObjectName objectName = null;
+   
+   /** Are we started? */
+   private boolean started = false;
+
+   /** The lifecycle event support for this component. */
+   private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+   /** Our tree cache */
+   private PojoCacheMBean treeCache = null;
+   
+   /** Name under which our TreeCache is registered in JMX */
+   private String treeCacheObjectName = JBossWeb.DEFAULT_CACHE_NAME;
+   
+   /** Did we create the tree cache, or was it already registered in JMX? */
+   private boolean treeCacheLocal = false;
+   
+   /** Name of the tree cache's JGroups channel */
+   private String clusterName = null;
+   
+   /** File name, URL or String to use to configure JGroups. */
+   private String cacheConfigPath = null;
+   
+   /**
+    * Implementation of Manager to instantiate when 
+    * createManager() is called.
+    */
+   private String managerClassName = JBossCacheManager.class.getName();
+
+   /** Does the Engine in which we are running use mod_jk? */
+   private boolean useJK = false;
+   
+   /** Whether our Managers should use a local cache. */
+   private boolean useLocalCache = false;
+
+   /**
+    * Default replication trigger to assign to our 
+    * Managers that haven't had this property set.
+    */
+   private String defaultReplicationTrigger = null;
+
+   /**
+    * Default replication granularity to assign to our Managers
+    * that haven't had this property set.
+    */
+   private String defaultReplicationGranularity = null;
+   
+   /**
+    * JBossCacheManager's snapshot mode.
+    */
+   private String snapshotMode = null;
+   
+   /**
+    * JBossCacheManager's snapshot interval.
+    */
+   private int snapshotInterval = 0;
+   
+   /** Whether we use batch mode replication for field level granularity */
+   private boolean replicationFieldBatchMode;
+   
+   //  ----------------------------------------------------------  Constructors
+   
+   /**
+    * Default constructor.
+    */
+   public JBossCacheCluster()
+   {
+      super();
+   }
+
+   //  ------------------------------------------------------------  Properties 
+
+   /**
+    * Gets a String representation of the JMX <code>ObjectName</code> under
+    * which our <code>TreeCache</code> is registered.
+    * <p>
+    * If this property is not explicitly set, the <code>TreeCache</code> will
+    * be registered under 
+    * @{@link Tomcat5.DEFAULT_CACHE_NAME the default name used in 
+    * embedded JBoss/Tomcat}.
+    * </p>
+    * 
+    * @jmx.managed-attribute
+    */
+   public String getCacheObjectName()
+   {
+      return treeCacheObjectName;
+   }
+
+   /**
+    * Sets the JMX <code>ObjectName</code> under which our 
+    * <code>TreeCache</code> is registered, if already created, or under
+    * which it should be registered if this object creates it.
+    * 
+    * @jmx.managed-attribute
+    */
+   public void setCacheObjectName(String objectName)
+   {
+      this.treeCacheObjectName = objectName;
+   }
+
+   /**
+    * Sets the name of the <code>TreeCache</code>'s JGroups channel.
+    * <p>
+    * This property is ignored if a <code>TreeCache</code> is already
+    * registered under the provided 
+    * {@link #setCacheObjectName cache object name}.
+    * </p>
+    * 
+    * @jmx.managed-attribute
+    */
+   public void setClusterName(String clusterName)
+   {
+      this.clusterName = clusterName;
+   }
+
+   /**
+    * Gets the filesystem path, which can either be absolute or a path 
+    * relative to <code>$CATALINA_BASE</code>, where a
+    * a JBossCache configuration file can be found.
+    * 
+    * @return  a path, either absolute or relative to 
+    *          <code>$CATALINA_BASE</code>.  Will return 
+    *          <code>null</code> if no such path was configured.
+    * 
+    * @jmx.managed-attribute
+    */
+   public String getCacheConfigPath()
+   {
+      return cacheConfigPath;
+   }
+
+   /** 
+    * Sets the filesystem path, which can either be absolute or a path 
+    * relative to <code>$CATALINA_BASE</code>, where a
+    * a JBossCache configuration file can be found.
+    * <p>
+    * This property is ignored if a <code>TreeCache</code> is already
+    * registered under the provided 
+    * {@link #setCacheObjectName cache object name}.
+    * </p>
+    * 
+    * @param cacheConfigPath   a path, absolute or relative to 
+    *                          <code>$CATALINA_BASE</code>,
+    *                          pointing to a JBossCache configuration file.
+    *                            
+    * @jmx.managed-attribute
+    */
+   public void setCacheConfigPath(String cacheConfigPath)
+   {
+      this.cacheConfigPath = cacheConfigPath;
+   }
+
+   /**
+    * Gets the name of the implementation of Manager to instantiate when 
+    * createManager() is called.
+    * 
+    * @jmx.managed-attribute
+    */
+   public String getManagerClassName()
+   {
+      return managerClassName;
+   }
+
+   /**
+    * Sets the name of the implementation of Manager to instantiate when 
+    * createManager() is called.
+    * <p>
+    * This should be {@link JBossCacheManager} (the default) or a subclass
+    * of it.
+    * </p>
+    * 
+    * @jmx.managed-attribute
+    */
+   public void setManagerClassName(String managerClassName)
+   {
+      this.managerClassName = managerClassName;
+   }
+
+   public void registerManager(Manager arg0)
+   {
+      // TODO tie this into the managerClassName      
+   }
+
+   public void removeManager(Manager arg0)
+   {
+      // TODO tie this into the managerClassName
+   }
+
+   /**
+    * Gets whether the <code>Engine</code> in which we are running
+    * uses <code>mod_jk</code>.
+    * 
+    * @jmx.managed-attribute
+    */
+   public boolean isUseJK()
+   {
+      return useJK;
+   }
+
+   /**
+    * Sets whether the <code>Engine</code> in which we are running
+    * uses <code>mod_jk</code>.
+    * 
+    * @jmx.managed-attribute
+    */
+   public void setUseJK(boolean useJK)
+   {
+      this.useJK = useJK;
+   }
+
+   /**
+    * Gets the <code>JBossCacheManager</code>'s <code>useLocalCache</code>
+    * property.
+    * 
+    * @jmx.managed-attribute
+    */
+   public boolean isUseLocalCache()
+   {
+      return useLocalCache;
+   }
+
+   /**
+    * Sets the <code>JBossCacheManager</code>'s <code>useLocalCache</code>
+    * property.
+    * 
+    * @jmx.managed-attribute
+    */
+   public void setUseLocalCache(boolean useLocalCache)
+   {
+      this.useLocalCache = useLocalCache;
+   }
+
+   /**
+    * Gets the default granularity of session data replicated across the 
+    * cluster; i.e. whether the entire session should be replicated when 
+    * replication is triggered, or only modified attributes.
+    * <p>
+    * The value of this property is applied to <code>Manager</code> instances
+    * that did not have an equivalent property explicitly set in 
+    * <code>context.xml</code> or <code>server.xml</code>.
+    * </p>
+    * 
+    * @jmx.managed-attribute
+    */
+   public String getDefaultReplicationGranularity()
+   {
+      return defaultReplicationGranularity;
+   }
+
+   /**
+    * Sets the granularity of session data replicated across the cluster.
+    * Valid values are:
+    * <ul>
+    * <li>SESSION</li>
+    * <li>ATTRIBUTE</li>
+    * <li>FIELD</li>
+    * </ul>
+    * @jmx.managed-attribute
+    */
+   public void setDefaultReplicationGranularity(
+         String defaultReplicationGranularity)
+   {
+      this.defaultReplicationGranularity = defaultReplicationGranularity;
+   }
+
+   /**
+    * Gets the type of operations on a <code>HttpSession</code> that 
+    * trigger replication.
+    * <p>
+    * The value of this property is applied to <code>Manager</code> instances
+    * that did not have an equivalent property explicitly set in 
+    * <code>context.xml</code> or <code>server.xml</code>.
+    * </p>
+    *  
+    * @jmx.managed-attribute
+    */
+   public String getDefaultReplicationTrigger()
+   {
+      return defaultReplicationTrigger;
+   }
+
+   /**
+    * Sets the type of operations on a <code>HttpSession</code> that 
+    * trigger replication.  Valid values are:
+    * <ul>
+    * <li>SET_AND_GET</li>
+    * <li>SET_AND_NON_PRIMITIVE_GET</li>
+    * <li>SET</li>
+    * </ul>
+    * 
+    * @jmx.managed-attribute
+    */
+   public void setDefaultReplicationTrigger(String defaultTrigger)
+   {
+      this.defaultReplicationTrigger = defaultTrigger;
+   }
+
+   /**
+    * Gets whether Managers should use batch mode replication.
+    * Only meaningful if replication granularity is set to <code>FIELD</code>.
+    * 
+    * @jmx.managed-attribute
+    */
+   public boolean getDefaultReplicationFieldBatchMode()
+   {
+      return replicationFieldBatchMode;
+   }
+   
+   /**
+    * Sets whether Managers should use batch mode replication.
+    * Only meaningful if replication granularity is set to <code>FIELD</code>.
+    * 
+    * @jmx.managed-attribute
+    */
+   public void setDefaultReplicationFieldBatchMode(boolean replicationFieldBatchMode)
+   {
+      this.replicationFieldBatchMode = replicationFieldBatchMode;
+   }
+
+   /**
+    * Gets when sessions are replicated to the other nodes.
+    * The default value, "instant", synchronously replicates changes
+    * to the other nodes. In this case, the "SnapshotInterval" attribute
+    * is not used.
+    * The "interval" mode, in association with the "SnapshotInterval"
+    * attribute, indicates that Tomcat will only replicate modified
+    * sessions every "SnapshotInterval" miliseconds at most.
+    * 
+    * @see #getSnapshotInterval()
+    * 
+    * @jmx.managed-attribute
+    */
+   public String getSnapshotMode()
+   {
+      return snapshotMode;
+   }
+
+   /**
+    * Sets when sessions are replicated to the other nodes. Valid values are:
+    * <ul>
+    * <li>instant</li>
+    * <li>interval</li> 
+    * </ul>
+    * 
+    * @jmx.managed-attribute
+    */
+   public void setSnapshotMode(String snapshotMode)
+   {
+      this.snapshotMode = snapshotMode;
+   }
+
+   /**
+    * Gets how often session changes should be replicated to other nodes.
+    * Only relevant if property {@link #getSnapshotMode() snapshotMode} is 
+    * set to <code>interval</code>.
+    * 
+    * @return the number of milliseconds between session replications.
+    * 
+    * @jmx.managed-attribute
+    */
+   public int getSnapshotInterval()
+   {
+      return snapshotInterval;
+   }
+
+   /**
+    * Sets how often session changes should be replicated to other nodes.
+    * 
+    * @param snapshotInterval the number of milliseconds between 
+    *                         session replications.
+    * @jmx.managed-attribute
+    */
+   public void setSnapshotInterval(int snapshotInterval)
+   {
+      this.snapshotInterval = snapshotInterval;
+   }
+   
+   // ----------------------------------------------------------------  Cluster
+
+   /**
+    * Gets the name of the <code>TreeCache</code>'s JGroups channel.
+    * 
+    * @see org.apache.catalina.Cluster#getClusterName()
+    */
+   public String getClusterName()
+   {
+      return clusterName;
+   }
+   
+   /* (non-javadoc)
+    * @see org.apache.catalina.Cluster#getContainer()
+    */
+   public Container getContainer()
+   {
+      return container;
+   }
+   
+   /* (non-javadoc)
+    * @see org.apache.catalina.Cluster#setContainer()
+    */
+   public void setContainer(Container container)
+   {
+      this.container = container;
+   }
+
+   /**
+    * @see org.apache.catalina.Cluster#getInfo()
+    * 
+    * @jmx.managed-attribute access="read-only"
+    */
+   public String getInfo()
+   {
+      return info;
+   }
+
+   /**
+    * @see org.apache.catalina.Cluster#createManager(java.lang.String)
+    */
+   public 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 JBossCacheManager();
+      } 
+      finally
+      {
+         manager.setDistributable(true);
+      }
+      
+      if (manager instanceof JBossCacheManager)
+      {
+         configureManager((JBossCacheManager) manager);
+      }
+      
+      return manager;
+   }
+
+   /**
+    * Does nothing; tracking the status of other members of the cluster is
+    * provided by the JGroups layer. 
+    * 
+    * @see org.apache.catalina.Cluster#backgroundProcess()
+    */
+   public void backgroundProcess()
+   {
+      ; // no-op
+   }
+
+   // ---------------------------------------------  Deprecated Cluster Methods
+   
+   /**
+    * Returns <code>null</code>; method is deprecated.
+    * 
+    * @return <code>null</code>, always.
+    * 
+    * @see org.apache.catalina.Cluster#getProtocol()
+    */
+   public String getProtocol()
+   {
+      return null;
+   }
+   
+   /**
+    * Does nothing; method is deprecated.
+    * 
+    * @see org.apache.catalina.Cluster#setProtocol(java.lang.String)
+    */
+   public void setProtocol(String protocol)
+   {
+      ; // no-op
+   }
+   
+   /**
+    * Does nothing; method is deprecated.
+    * 
+    * @see org.apache.catalina.Cluster#startContext(java.lang.String)
+    */
+   public void startContext(String contextPath) throws IOException
+   {
+      ; // no-op
+   }
+   
+   /**
+    * Does nothing; method is deprecated.
+    * 
+    * @see org.apache.catalina.Cluster#installContext(java.lang.String, java.net.URL)
+    */
+   public void installContext(String contextPath, URL war)
+   {
+      ; // no-op
+   }
+   
+   /**
+    * Does nothing; method is deprecated.
+    * 
+    * @see org.apache.catalina.Cluster#stop(java.lang.String)
+    */
+   public void stop(String contextPath) throws IOException 
+   {
+      ; // no-op
+   }
+
+   // ---------------------------------------------------------  Public Methods
+   
+   /**
+    * Sets the cluster-wide properties of a <code>Manager</code> to
+    * match those of this cluster.  Does not override 
+    * <code>Manager</code>-specific properties with cluster-wide defaults 
+    * if the <code>Manager</code>-specfic properties have already been set.
+    */
+   public void configureManager(JBossCacheManager manager)
+   {
+      manager.setSnapshotMode(snapshotMode);
+      manager.setSnapshotInterval(snapshotInterval);
+      manager.setUseJK(useJK);
+      manager.setUseLocalCache(useLocalCache);
+      manager.setCacheObjectNameString(treeCacheObjectName);
+      
+      // Only set replication attributes if they were not
+      // already set via a <Manager> element in an XML config file
+      
+      if (manager.getReplicationGranularityString() == null) 
+      {
+         manager.setReplicationGranularityString(defaultReplicationGranularity);
+      }
+      
+      if (manager.getReplicationTriggerString() == null) 
+      {
+         manager.setReplicationTriggerString(defaultReplicationTrigger);
+      }
+      
+      if (manager.isReplicationFieldBatchMode() == null)
+      {
+         manager.setReplicationFieldBatchMode(replicationFieldBatchMode);
+   }
+   }
+
+   // ---------------------------------------------------------------  Lifecyle
+
+   /**
+    * Finds or creates a {@link TreeCache}; if created, starts the 
+    * cache and registers it with our JMX server.
+    * 
+    * @see org.apache.catalina.Lifecycle#start()
+    */
+   public void start() throws LifecycleException
+   {
+      if (started)
+      {
+         throw new LifecycleException("Cluster already started");
+      }
+
+      // Notify our interested LifecycleListeners
+      lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, this);
+      
+      try
+      {         
+         // Tell the JBoss MBeanServerLocator utility 
+         // that Tomcat's MBean server is 'jboss'
+         MBeanServerLocator.setJBoss(getMBeanServer());
+         
+         // Initialize the tree cache
+         PojoCacheMBean cache = getTreeCache();
+         
+         if (treeCacheLocal)
+         {
+            cache.createService();
+            cache.startService();
+         }
+         
+         registerMBeans();
+
+         started = true;
+         
+         // Notify our interested LifecycleListeners
+         lifecycle.fireLifecycleEvent(AFTER_START_EVENT, this);
+
+      }
+      catch (LifecycleException e)
+      {
+         throw e;
+      }
+      catch (Exception e)
+      {
+         log.error("Unable to start cluster.", e);
+         throw new LifecycleException(e);
+      } 
+   }
+
+   /**
+    * If this object created its own {@link TreeCache}, stops it 
+    * and unregisters it with JMX.
+    * 
+    * @see org.apache.catalina.Lifecycle#stop()
+    */
+   public void stop() throws LifecycleException
+   {
+      if (!started)
+      {
+         throw new IllegalStateException("Cluster not started");
+      }
+      
+      // Notify our interested LifecycleListeners
+      lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+      
+      if (treeCacheLocal)
+      {
+         treeCache.stopService();
+         treeCache.destroyService();
+      }
+
+      started = false;
+      // Notify our interested LifecycleListeners
+      lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+
+      unregisterMBeans();
+   }
+
+   /* (non-javadoc)           
+    * @see org.apache.catalina.Lifecycle#addLifecycleListener()
+    */
+   public void addLifecycleListener(LifecycleListener listener)
+   {
+      lifecycle.addLifecycleListener(listener);
+   }
+
+   /* (non-javadoc)           
+    * @see org.apache.catalina.Lifecycle#findLifecycleListeners()
+    */
+   public LifecycleListener[] findLifecycleListeners()
+   {
+      return lifecycle.findLifecycleListeners();
+   }
+
+   /* (non-javadoc)           
+    * @see org.apache.catalina.Lifecycle#removeLifecycleListener()
+    */
+   public void removeLifecycleListener(LifecycleListener listener)
+   {
+      lifecycle.removeLifecycleListener(listener);
+   }
+   
+   // -------------------------------------------------------- Private Methods
+ 
+   /**
+    * Gets our TreeCache, either from a local reference or the JMX
+    * server.  If one is not found, creates and configures it.
+    */
+   private PojoCacheMBean getTreeCache() throws Exception
+   {
+      if (treeCache == null) {
+         
+         MBeanServer server = getMBeanServer();
+         ObjectName objName = new ObjectName(treeCacheObjectName);
+         if (server.isRegistered(objName))
+         {
+            // Get a proxy to the existing TreeCache
+            treeCache = (PojoCacheMBean) 
+                  MBeanProxyExt.create(PojoCacheMBean.class, objName);
+         }
+         else
+         {
+            // Create our own tree cache
+            treeCache = new PojoCache();
+            
+            // See if there is an XML descriptor file to configure the cache
+            InputStream configIS = getCacheConfigStream();
+            
+            if (configIS != null)
+            {
+               PropertyConfigurator config = new PropertyConfigurator();
+               config.configure(treeCache, configIS);
+               try 
+               {
+                  configIS.close();
+               }
+               catch (IOException io)
+               {
+                  // ignore
+               }
+               
+               if (clusterName != null)
+               {
+                  // Override the XML config with the name provided in
+                  // server.xml.  Method setClusterName is specified in the
+                  // Cluster interface, otherwise we would not do this
+                  treeCache.setClusterName(clusterName);
+               }
+            }
+            else
+            {
+               // User did not try to configure the cache.
+               // Configure it using defaults.  Only exception
+               // is the clusterName, which user can specify in server.xml.
+               String channelName = (clusterName == null) ? DEFAULT_CLUSTER_NAME
+                                                          : clusterName;
+               treeCache.setClusterName(channelName);
+               treeCache.setIsolationLevel(DEFAULT_ISOLATION_LEVEL);
+               treeCache.setCacheMode(DEFAULT_CACHE_MODE);
+               treeCache.setLockAcquisitionTimeout(DEFAULT_LOCK_TIMEOUT);
+               treeCache.setTransactionManagerLookupClass(DEFAULT_TM_LOOKUP);
+            }
+            
+            treeCacheLocal = true;
+         }
+      }
+      return treeCache;
+   }   
+   
+   
+
+   private InputStream getCacheConfigStream() throws FileNotFoundException
+   {
+      boolean useDefault = (this.cacheConfigPath == null);
+      String path = (useDefault) ? DEFAULT_CACHE_CONFIG_PATH : cacheConfigPath;
+      // See if clusterProperties points to a file relative
+      // to $CATALINA_BASE
+      File file = new File(path);
+      if (!file.isAbsolute())
+      {
+         file = new File(System.getProperty("catalina.base"), path);
+      }
+      
+      try
+      {
+         return new FileInputStream(file);
+      }
+      catch (FileNotFoundException fnf)
+      {
+         if (useDefault)
+         {
+            // Not a problem, just means user did not try to
+            // configure the cache.  Return null and let the cache
+            // be configured from defaults.
+            return null;
+         }
+         else
+         {
+            // User provided config was invalid; throw the exception
+            log.error("No tree cache config file found at " + 
+                      file.getAbsolutePath());
+            throw fnf;
+         }
+      }      
+   }
+
+   /**
+    * Registers this object and the tree cache (if we created it) with JMX.
+    */
+   private void registerMBeans()
+   {
+      try
+      {
+         MBeanServer server = getMBeanServer();
+         
+         String domain;
+         if (container instanceof ContainerBase)
+         {
+            domain = ((ContainerBase) container).getDomain();
+         }
+         else
+         {
+            domain = server.getDefaultDomain();
+         }
+         
+         String name = ":type=Cluster";
+         if (container instanceof Host) {
+            name += ",host=" + container.getName();
+         }
+         else if (container instanceof Engine)
+         {            
+            name += ",engine=" + container.getName();
+         }
+         
+         ObjectName clusterName = new ObjectName(domain + name);
+
+         if (server.isRegistered(clusterName))
+         {
+            log.warn("MBean " + clusterName + " already registered");
+         }
+         else
+         {
+            this.objectName = clusterName;
+            server.registerMBean(this, objectName);
+         }
+
+         if (treeCacheLocal)
+         {
+            // Register the treeCache
+            ObjectName treeCacheName = new ObjectName(treeCacheObjectName);
+            server.registerMBean(getTreeCache(), treeCacheName);
+         }
+
+      }
+      catch (Exception ex)
+      {
+         log.error(ex.getMessage(), ex);
+      }
+   }   
+
+   /**
+    * Unregisters this object and the tree cache (if we created it) with JMX.
+    */
+   private void unregisterMBeans()
+   {
+      if (mserver != null)
+      {
+         try
+         {
+            if (objectName != null) {
+               mserver.unregisterMBean(objectName);
+            }
+            if (treeCacheLocal)
+            {
+               mserver.unregisterMBean(new ObjectName(treeCacheObjectName));
+            }
+         }
+         catch (Exception e)
+         {
+            log.error(e);
+         }
+      }
+   }
+
+   /**
+    * Get the current Catalina MBean Server.
+    * 
+    * @return
+    * @throws Exception
+    */
+   private MBeanServer getMBeanServer() throws Exception
+   {
+      if (mserver == null)
+      {
+         ArrayList servers = MBeanServerFactory.findMBeanServer(null);
+         if (servers.size() > 0)
+         {
+            mserver = (MBeanServer) servers.get(0);
+         }
+         else
+         {
+            mserver = MBeanServerFactory.createMBeanServer();
+         }
+      }
+      return mserver;
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.LifecycleException;
+
+/**
+ * The MBean interface for the JBossCacheCluster.
+ * 
+ * @see org.jboss.web.tomcat.tc5.JBossCacheCluster
+ * 
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public interface JBossCacheClusterMBean extends Cluster
+{
+   public abstract boolean isUseJK();
+
+   public abstract void setUseJK(boolean useJK);
+
+   public abstract boolean isUseLocalCache();
+
+   public abstract void setUseLocalCache(boolean useLocalCache);
+
+   public abstract String getManagerClassName();
+
+   public abstract void setManagerClassName(String managerClassName);
+
+   public abstract String getDefaultReplicationGranularity();
+
+   public abstract void setDefaultReplicationGranularity(String defaultGran);
+
+   public abstract String getDefaultReplicationTrigger();
+
+   public abstract void setDefaultReplicationTrigger(String defaultTrigger);
+
+   public boolean getDefaultReplicationFieldBatchMode();
+
+   public void setDefaultReplicationFieldBatchMode(boolean replicationFieldBatchMode);
+
+   public abstract int getSnapshotInterval();
+
+   public abstract void setSnapshotInterval(int snapshotInterval);
+
+   public abstract String getSnapshotMode();
+
+   public abstract void setSnapshotMode(String snapshotMode);
+   
+   public abstract String getCacheObjectName();
+   
+   public abstract void setCacheObjectName(String objectName);
+
+   public abstract void start() throws LifecycleException;
+
+   /**
+    * Does nothing.
+    */
+   public abstract void stop() throws LifecycleException;
+
+}
\ No newline at end of file


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,159 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+/**
+ * Common superclass of ClusteredSession types that use JBossCache
+ * as their distributed cache.
+ * 
+ * @author Brian Stansberry 
+ * 
+ * @version $Revision$
+ */
+public abstract class JBossCacheClusteredSession extends ClusteredSession
+{
+   /**
+    * Our proxy to the cache.
+    */
+   protected transient JBossCacheService proxy_;
+   
+   /**
+    * Create a new JBossCacheClusteredSession.
+    * 
+    * @param manager
+    * @param useJK
+    */
+   public JBossCacheClusteredSession(JBossCacheManager manager)
+   {
+      super(manager, manager.getUseJK());
+      establishProxy();
+   }
+
+   /**
+    * Initialize fields marked as transient after loading this session
+    * from the distributed store
+    *
+    * @param manager the manager for this session
+    */
+   public void initAfterLoad(AbstractJBossManager manager)
+   {
+      // Our manager and proxy may have been lost if we were recycled,
+      // so reestablish them
+      setManager(manager);
+      establishProxy();
+
+      // Since attribute map may be transient, we may need to populate it 
+      // from the underlying store.
+      populateAttributes();
+      
+      // Notify all attributes of type HttpSessionActivationListener (SRV 7.7.2)
+      this.activate();
+      
+      // We are no longer outdated vis a vis distributed cache
+      clearOutdated();
+   }
+   
+   /**
+    * Gets a reference to the JBossCacheService.
+    */
+   protected void establishProxy()
+   {
+      if (proxy_ == null)
+      {
+         proxy_ = ((JBossCacheManager) manager).getCacheService();
+
+         // still null???
+         if (proxy_ == null)
+         {
+            throw new RuntimeException("JBossCacheClusteredSession: Cache service is null.");
+         }
+      }
+   }
+   
+   protected abstract void populateAttributes();
+
+   /**
+    * Override the superclass to additionally reset this class' fields.
+    * <p>
+    * <strong>NOTE:</strong> It is not anticipated that this method will be
+    * called on a ClusteredSession, but we are overriding the method to be
+    * thorough.
+    * </p>
+    */
+   public void recycle()
+   {
+      super.recycle();
+      
+      proxy_ = null;
+   }
+
+   /**
+    * Increment our version and place ourself in the cache.
+    */
+   public synchronized void processSessionRepl()
+   {
+      // Replicate the session.
+      if (log.isTraceEnabled())
+      {
+         log.trace("processSessionRepl(): session is dirty. Will increment " +
+                   "version from: " + getVersion() + " and replicate.");
+      }
+      this.incrementVersion();
+      proxy_.putSession(realId, this);
+      
+      sessionAttributesDirty = false;
+      sessionMetadataDirty = false;
+      
+      updateLastReplicated();
+   }
+
+   /**
+    * Overrides the superclass impl by doing nothing if <code>localCall</code>
+    * is <code>false</code>.  The JBossCacheManager will already be aware of
+    * a remote invalidation and will handle removal itself.
+    */
+   protected void removeFromManager(boolean localCall, boolean localOnly)
+   {
+      if (localCall)
+      {
+         super.removeFromManager(localCall, localOnly);
+      }
+   }
+   
+   
+   protected Object removeAttributeInternal(String name, boolean localCall, boolean localOnly)
+   {
+      return removeJBossInternalAttribute(name, localCall, localOnly);
+   }
+
+   protected Object removeJBossInternalAttribute(String name)
+   {
+      throw new UnsupportedOperationException("removeJBossInternalAttribute(String) " +
+            "is not supported by JBossCacheClusteredSession; use " +
+            "removeJBossInternalAttribute(String, boolean, boolean");
+   }
+
+   protected abstract Object removeJBossInternalAttribute(String name, 
+                                                          boolean localCall, 
+                                                          boolean localOnly);
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1786 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+import javax.transaction.RollbackException;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.ContainerBase;
+import org.jboss.cache.CacheException;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.web.tomcat.service.JBossWeb;
+
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implementation of a clustered session manager for
+ * catalina using JBossCache replication.
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ * 
+ * @version $Revision: 57357 $
+ */
+public class JBossCacheManager
+   extends JBossManager
+   implements JBossCacheManagerMBean
+{
+
+  /**
+    * Informational name for this Catalina component
+    */
+   static final String info_ = "JBossCacheManager/1.0";
+
+   // -- Class attributes ---------------------------------
+
+   /**
+    * The transaction manager.
+    */
+   protected TransactionManager tm;
+
+   /**
+    * Proxy-object for the JBossCacheService
+    */
+   private JBossCacheService proxy_;
+
+   /**
+    * Id/timestamp of sessions in cache that we haven't loaded
+    */
+   private Map unloadedSessions_ = new ConcurrentHashMap();
+
+   /** Our TreeCache's ObjectName */
+   private String cacheObjectNameString_ = JBossWeb.DEFAULT_CACHE_NAME;
+
+   /**
+    * If set to true, will add a JvmRouteFilter to the request.
+    */
+   protected boolean useJK_ = false;
+
+   /** Are we running embedded in JBoss? */
+   private boolean embedded_ = false;
+
+   /** Our JMX Server */
+   private MBeanServer mserver_ = null;
+
+   /** Our ClusteredSessionValve's snapshot mode. */
+   private String snapshotMode_ = null;
+
+   /** Our ClusteredSessionValve's snapshot interval. */
+   private int snapshotInterval_ = 0;
+
+   /** String form of invalidateSessionPolicy_ */
+   private String replTriggerString_ = null;
+
+   /** String form of replGranularityString_ */
+   private String replGranularityString_ = null;
+
+   /**
+    * Whether we use batch mode replication for field level granularity.
+    * We store this in a Boolean rather than a primitive so JBossCacheCluster
+    * can determine if this was set via a <Manager> element.
+    */
+   private Boolean replicationFieldBatchMode_;
+   
+   /** Class loader for this web app. */
+   private ClassLoader tcl_;
+   
+   /**
+    * The snapshot manager we are using.
+    */
+   private SnapshotManager snapshotManager_;
+
+   //  ----------------------------------------------------------  Constructors
+
+   public JBossCacheManager()
+   {
+      super();
+   }
+
+   /**
+    * Initializes this Manager when running in embedded mode.
+    * <p>
+    * <strong>NOTE:</strong> This method should not be called when
+    * running unembedded.
+    * </p>
+    */
+   public void init(String name, WebMetaData webMetaData,
+                    boolean useJK, boolean useLocalCache)
+      throws ClusteringNotSupportedException
+   {
+      super.init(name, webMetaData, useJK, useLocalCache);
+      this.useJK_ = useJK;
+      this.replicationFieldBatchMode_ =
+         webMetaData.getReplicationFieldBatchMode() ? Boolean.TRUE : Boolean.FALSE;
+      
+      proxy_ = new JBossCacheService(cacheObjectNameString_);
+
+      // Confirm our replication granularity is compatible with the cache
+      // Throws ISE if not
+      validateFieldMarshalling();
+
+      embedded_ = true;
+   }
+
+   // -------------------------------------------------------------  Properties
+
+   /**
+    * Gets the <code>JBossCacheService</code> through which we interact
+    * with the <code>TreeCache</code>.
+    */
+   public JBossCacheService getCacheService()
+   {
+      return proxy_;
+   }
+
+   /**
+    * Gets a String representation of the JMX <code>ObjectName</code> under
+    * which our <code>TreeCache</code> is registered.
+    */
+   public String getCacheObjectNameString()
+   {
+      return cacheObjectNameString_;
+   }
+
+   /**
+    * Sets the JMX <code>ObjectName</code> under which our
+    * <code>TreeCache</code> is registered.
+    */
+   public void setCacheObjectNameString(String treeCacheObjectName)
+   {
+      this.cacheObjectNameString_ = treeCacheObjectName;
+   }
+
+   /**
+    * Gets when sessions are replicated to the other nodes.
+    * The default value, "instant", synchronously replicates changes
+    * to the other nodes. In this case, the "SnapshotInterval" attribute
+    * is not used.
+    * The "interval" mode, in association with the "SnapshotInterval"
+    * attribute, indicates that Tomcat will only replicate modified
+    * sessions every "SnapshotInterval" miliseconds at most.
+    *
+    * @see #getSnapshotInterval()
+    */
+   public String getSnapshotMode()
+   {
+      return snapshotMode_;
+   }
+
+   /**
+    * Sets when sessions are replicated to the other nodes. Valid values are:
+    * <ul>
+    * <li>instant</li>
+    * <li>interval</li>
+    * </ul>
+    */
+   public void setSnapshotMode(String snapshotMode)
+   {
+      this.snapshotMode_ = snapshotMode;
+   }
+
+   /**
+    * Gets how often session changes should be replicated to other nodes.
+    * Only relevant if property {@link #getSnapshotMode() snapshotMode} is
+    * set to <code>interval</code>.
+    *
+    * @return the number of milliseconds between session replications.
+    */
+   public int getSnapshotInterval()
+   {
+      return snapshotInterval_;
+   }
+
+   /**
+    * Sets how often session changes should be replicated to other nodes.
+    *
+    * @param snapshotInterval the number of milliseconds between
+    *                         session replications.
+    */
+   public void setSnapshotInterval(int snapshotInterval)
+   {
+      this.snapshotInterval_ = snapshotInterval;
+   }
+
+   /**
+    * Gets whether the <code>Engine</code> in which we are running
+    * uses <code>mod_jk</code>.
+    */
+   public boolean getUseJK()
+   {
+      return useJK_;
+   }
+
+   /**
+    * Sets whether the <code>Engine</code> in which we are running
+    * uses <code>mod_jk</code>.
+    */
+   public void setUseJK(boolean useJK)
+   {
+      this.useJK_ = useJK;
+   }
+
+   /**
+    * Returns the replication granularity expressed as an int.
+    *
+    *  @see WebMetaData#REPLICATION_GRANULARITY_ATTRIBUTE
+    *  @see WebMetaData#REPLICATION_GRANULARITY_FIELD
+    *  @see WebMetaData#REPLICATION_GRANULARITY_SESSION
+    */
+   public int getReplicationGranularity()
+   {
+      return replicationGranularity_;
+   }
+
+   /**
+    * Gets the granularity of session data replicated across the
+    * cluster; i.e. whether the entire session should be replicated when
+    * replication is triggered, only modified attributes, or only
+    * modified fields of attributes.
+    */
+   public String getReplicationGranularityString()
+   {
+      // Only lazy-set this if we are started; 
+      // otherwise screws up standalone TC integration!!
+      if (started_ && this.replGranularityString_ == null)
+      {
+         switch (this.replicationGranularity_)
+         {
+            case WebMetaData.REPLICATION_GRANULARITY_ATTRIBUTE:
+               this.replGranularityString_ = "ATTRIBUTE";
+               break;
+            case WebMetaData.REPLICATION_GRANULARITY_SESSION:
+               this.replGranularityString_ = "SESSION";
+               break;
+            case WebMetaData.REPLICATION_GRANULARITY_FIELD:
+               this.replGranularityString_ = "FIELD";
+         }
+      }
+      return replGranularityString_;
+   }
+
+   /**
+    * Sets the granularity of session data replicated across the cluster.
+    * Valid values are:
+    * <ul>
+    * <li>SESSION</li>
+    * <li>ATTRIBUTE</li>
+    * <li>FIELD</li>
+    * </ul>
+    */
+   public void setReplicationGranularityString(String granularity)
+   {
+      this.replGranularityString_ = granularity;
+   }
+
+   /**
+    * Gets the type of operations on a <code>HttpSession</code> that
+    * trigger replication.
+    */
+   public String getReplicationTriggerString()
+   {
+      // Only lazy-set this if we are started; 
+      // otherwise screws up standalone TC integration!!
+      if (started_ && this.replTriggerString_ == null)
+      {
+         switch (this.invalidateSessionPolicy_)
+         {
+            case WebMetaData.SESSION_INVALIDATE_SET:
+               this.replTriggerString_ = "SET";
+               break;
+            case WebMetaData.SESSION_INVALIDATE_SET_AND_GET:
+               this.replTriggerString_ = "SET_AND_GET";
+               break;
+            case WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET:
+               this.replTriggerString_ = "SET_AND_NON_PRIMITIVE_GET";
+         }
+      }
+      return this.replTriggerString_;
+   }
+
+   /**
+    * Sets the type of operations on a <code>HttpSession</code> that
+    * trigger replication.  Valid values are:
+    * <ul>
+    * <li>SET_AND_GET</li>
+    * <li>SET_AND_NON_PRIMITIVE_GET</li>
+    * <li>SET</li>
+    * </ul>
+    */
+   public void setReplicationTriggerString(String trigger)
+   {
+      this.replTriggerString_ = trigger;
+   }
+
+   /**
+    * Gets whether, if replication granularity is set to <code>FIELD</code>,
+    * replication should be done in batch mode.  Ignored if field-level
+    * granularity is not used.
+    */
+   public Boolean isReplicationFieldBatchMode()
+   {
+      return replicationFieldBatchMode_;
+   }
+
+   /**
+    * Sets whether, if replication granularity is set to <code>FIELD</code>,
+    * replication should be done in batch mode.  Ignored if field-level
+    * granularity is not used.
+    */
+   public void setReplicationFieldBatchMode(boolean replicationFieldBatchMode)
+   {
+      this.replicationFieldBatchMode_ = Boolean.valueOf(replicationFieldBatchMode);
+   }
+
+   public void setUseLocalCache(boolean useLocalCache)
+   {
+      this.useLocalCache_ = useLocalCache;
+   }
+
+   // JBossCacheManagerMBean-methods -------------------------------------
+
+   public void expireSession(String sessionId)
+   {
+      Session session = findSession(sessionId);
+      if (session != null)
+         session.expire();
+   }
+
+   public String getLastAccessedTime(String sessionId)
+   {
+      Session session = findSession(sessionId);
+      if(session == null) {
+         log_.debug("getLastAccessedTime(): Session " + sessionId + 
+                    " not found");
+         return "";
+      }
+     return new Date(session.getLastAccessedTime()).toString();
+   }
+
+   public Object getSessionAttribute(String sessionId, String key)
+   {
+      ClusteredSession session = (ClusteredSession) findSession(sessionId);
+      return (session == null) ? null : session.getAttribute(key);
+   }
+
+   public String getSessionAttributeString(String sessionId, String key)
+   {
+      Object attr = getSessionAttribute(sessionId, key);
+      return (attr == null) ? null : attr.toString();
+   }
+   
+   public String listLocalSessionIds()
+   {
+      return reportSessionIds(sessions_.keySet());
+   }
+   
+   public String listSessionIds()
+   {
+      Set ids = new HashSet(sessions_.keySet());
+      ids.addAll(unloadedSessions_.keySet());
+      return reportSessionIds(ids);
+   }
+   
+   private String reportSessionIds(Set ids)
+   {
+      StringBuffer sb = new StringBuffer();
+      boolean added = false;
+      for (Iterator it = ids.iterator(); it.hasNext(); )
+      {
+         if (added)
+         {
+            sb.append(',');
+         }
+         else
+         {
+            added = true;
+         }
+         
+         sb.append(it.next());
+      }
+      return sb.toString();
+   }
+
+   // Manager-methods -------------------------------------
+
+   /**
+    * Start this Manager
+    *
+    * @throws org.apache.catalina.LifecycleException
+    *
+    */
+   public void start() throws LifecycleException
+   {
+      if (embedded_)
+      {
+         startEmbedded();
+      }
+      else
+      {
+         startUnembedded();
+      }
+   }
+
+   public void stop() throws LifecycleException
+   {
+      if (!started_)
+      {
+         throw new IllegalStateException("Manager not started");
+      }
+      
+      log_.debug("Stopping");
+      
+      resetStats();
+      
+      // Notify our interested LifecycleListeners
+      lifecycle_.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+      
+      clearSessions();
+      
+      // Don't leak the classloader
+      tcl_ = null;
+      
+      proxy_.stop();
+      tm = null;
+      
+      snapshotManager_.stop();
+      
+      started_ = false;
+      
+      // Notify our interested LifecycleListeners
+      lifecycle_.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+      
+      try
+      {
+         unregisterMBeans();
+      }
+      catch (Exception e)
+      {
+         log_.error("Could not unregister ManagerMBean from MBeanServer", e);
+      }
+   }
+
+   /**
+    * Clear the underlying cache store and also pojo that has the observers.
+    */
+   protected void clearSessions()
+   {
+      // First, the sessions we have actively loaded
+      ClusteredSession[] sessions = findLocalSessions();
+      for(int i=0; i < sessions.length; i++)
+      {
+         ClusteredSession ses = sessions[i];
+         if (log_.isDebugEnabled())
+         {
+             log_.debug("clearSessions(): clear session by expiring: " + ses);
+         }
+         boolean notify = true;
+         boolean localCall = true;
+         boolean localOnly = true;
+         try
+         {
+            ses.expire(notify, localCall, localOnly);
+         }
+         catch (Throwable t)
+         {
+            log_.warn("clearSessions(): Caught exception expiring session " +
+                     ses.getIdInternal(), t);
+         }
+         finally
+         {
+            // Guard against leaking memory if anything is holding a
+            // ref to the session by clearing its internal state
+            ses.recycle();
+         }
+      }
+
+      // Next, the local copy of the distributed cache
+      Map unloaded = new HashMap(unloadedSessions_);
+      Set keys = unloaded.keySet();
+      for (Iterator it = keys.iterator(); it.hasNext(); )
+      {
+         String realId = (String) it.next();
+         proxy_.removeSessionLocal(realId);
+         unloadedSessions_.remove(realId);
+      }
+   }
+
+   /**
+    * Create a new session with a generated id.
+    */
+   public Session createSession()
+   {
+      return createSession(null);
+   }
+
+   /**
+    * Create a new session.
+    *
+    * @param sessionId the id to use, or <code>null</code> if we should
+    *                  generate a new id
+    *
+    * @return the session
+    *
+    * @throws IllegalStateException if the current number of active sessions
+    *         exceeds the maximum number allowed
+    */
+   public Session createSession(String sessionId)
+   {
+      // We check here for maxActive instead of in add().  add() gets called
+      // when we load an already existing session from the distributed cache
+      // (e.g. in a failover) and we don't want to fail in that situation.
+
+      // maxActive_ -1 is unlimited
+      if (maxActive_ != -1 && activeCounter_ >= maxActive_)
+      {
+         // Exceeds limit. We need to reject it.
+         rejectedCounter_++;
+         // Catalina api does not specify what happens
+         // but we will throw a runtime exception for now.
+         String msgEnd = (sessionId == null) ? "" : " id " + sessionId;
+         throw new IllegalStateException("JBossCacheManager.add(): number of " +
+                "active sessions exceeds the maximum limit: " +
+                maxActive_ + " when trying to add session" + msgEnd);
+      }
+
+      ClusteredSession session = createEmptyClusteredSession();
+
+      session.setNew(true);
+      session.setCreationTime(System.currentTimeMillis());
+      session.setMaxInactiveInterval(this.maxInactiveInterval_);
+      session.setValid(true);
+
+      if (sessionId == null)
+      {
+          sessionId = this.getNextId();
+
+          // We are using mod_jk for load balancing. Append the JvmRoute.
+          if (useJK_)
+          {
+              if (log_.isDebugEnabled())
+              {
+                  log_.debug("createSession(): useJK is true. Will append JvmRoute: " + this.getJvmRoute());
+              }
+              sessionId += "." + this.getJvmRoute();
+          }
+      }
+
+      session.setId(sessionId); // Setting the id leads to a call to add()
+
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("Created a ClusteredSession with id: " + sessionId);
+      }
+
+      createdCounter_++;
+      
+      // Add this session to the set of those potentially needing replication
+      SessionReplicationContext.bindSession(session, snapshotManager_);
+      
+      return session;
+   }
+
+   public boolean storeSession(Session baseSession)
+   {
+      boolean stored = false;
+      if(baseSession != null && started_)
+      {
+         ClusteredSession session = (ClusteredSession) baseSession;
+
+         synchronized (session)
+         {
+            if (log_.isTraceEnabled())
+            {
+               log_.trace("check to see if needs to store and replicate " +
+                          "session with id " + session.getIdInternal());
+            }
+
+            if (session.isValid() &&
+                  (session.isSessionDirty() || session.getExceedsMaxUnreplicatedInterval()))
+            {
+               String realId = session.getRealId();
+
+               // Notify all session attributes that they get serialized (SRV 7.7.2)
+               long begin = System.currentTimeMillis();
+               session.passivate();
+               long elapsed = System.currentTimeMillis() - begin;
+               stats_.updatePassivationStats(realId, elapsed);
+
+               // Do the actual replication
+               begin = System.currentTimeMillis();
+               processSessionRepl(session);
+               elapsed = System.currentTimeMillis() - begin;
+               stored = true;
+               stats_.updateReplicationStats(realId, elapsed);
+            }
+         }
+      }
+
+      return stored;
+   }
+
+   public void add(Session session)
+   {
+      if (session == null)
+         return;
+
+      if (!(session instanceof ClusteredSession))
+      {
+         throw new IllegalArgumentException("You can only add instances of " +
+               "type ClusteredSession to this Manager. Session class name: " +
+               session.getClass().getName());
+      }
+
+//      add((ClusteredSession) session, true);
+      add((ClusteredSession) session, false);
+   }
+
+   /**
+    * Adds the given session to the collection of those being managed by this
+    * Manager.
+    *
+    * @param session   the session. Cannot be <code>null</code>.
+    * @param replicate whether the session should be replicated
+    *
+    * @throws NullPointerException if <code>session</code> is <code>null</code>.
+    */
+   private void add(ClusteredSession session, boolean replicate)
+   {
+      if (!session.isValid())
+      {
+         log_.error("Cannot add session with id=" + session.getIdInternal() +
+                    " because it is invalid");
+         return;
+      }
+
+      String realId = session.getRealId();
+      Object existing = sessions_.put(realId, session);
+      unloadedSessions_.remove(realId);
+
+      if (!session.equals(existing))
+      {
+         if (replicate)
+         {
+            storeSession(session);
+         }
+
+         activeCounter_++;
+         if (activeCounter_ > maxActiveCounter_)
+            maxActiveCounter_++;
+         
+         if (log_.isDebugEnabled())
+         {
+            log_.debug("Session with id=" + session.getIdInternal() + " added. " +
+                       "Current active sessions " + activeCounter_);
+         }
+      }
+   }
+
+   // Satisfy the Manager interface.  Internally we use
+   // createEmptyClusteredSession to avoid a cast
+   public Session createEmptySession()
+   {
+      return createEmptyClusteredSession();
+   }
+
+   private ClusteredSession createEmptyClusteredSession()
+   {
+      log_.debug("Creating an empty ClusteredSession");
+
+      ClusteredSession session = null;
+      switch (replicationGranularity_)
+      {
+         case (WebMetaData.REPLICATION_GRANULARITY_ATTRIBUTE):
+         session = new AttributeBasedClusteredSession(this);
+            break;
+         case (WebMetaData.REPLICATION_GRANULARITY_FIELD):
+            session = new FieldBasedClusteredSession(this);
+            break;
+         default:
+            session = new SessionBasedClusteredSession(this);
+            break;
+      }
+      return session;
+   }
+
+   /**
+    * Attempts to find the session in the collection of those being managed
+    * locally, and if not found there, in the distributed cache of sessions.
+    * <p>
+    * If a session is found in the distributed cache, it is added to the
+    * collection of those being managed locally.
+    * </p>
+    *
+    * @param id the session id, which may include an appended jvmRoute
+    *
+    * @return the session, or <code>null</code> if no such session could
+    *         be found
+    */
+   public Session findSession(String id)
+   {
+      String realId = getRealId(id);
+      // Find it from the local store first
+      ClusteredSession session = findLocalSession(realId);
+      
+      // If we didn't find it locally, only check the distributed cache
+      // if we haven't previously handled this session id on this request.
+      // If we handled it previously but it's no longer local, that means
+      // it's been invalidated. If we request an invalidated session from
+      // the distributed cache, it will be missing from the local cache but
+      // may still exist on other nodes (i.e. if the invalidation hasn't 
+      // replicated yet because we are running in a tx). With buddy replication,
+      // asking the local cache for the session will cause the out-of-date
+      // session from the other nodes to be gravitated, thus resuscitating
+      // the session.
+      if (session == null 
+            && !SessionReplicationContext.isSessionBoundAndExpired(realId, snapshotManager_))
+      {
+         if (log_.isTraceEnabled())
+            log_.trace("Checking for session " + realId + " in the distributed cache");
+         
+         session = loadSession(realId);
+         if (session != null)
+         {
+            add(session);
+            // TODO should we advise of a new session?
+            //tellNew();
+         }
+      }
+      else if (session != null && session.isOutdated())
+      {
+         if (log_.isTraceEnabled())
+            log_.trace("Updating session " + realId + " from the distributed cache");
+         
+         // Need to update it from the cache
+         loadSession(realId);
+      }
+
+      if (session != null)
+      {
+         // Add this session to the set of those potentially needing replication
+         SessionReplicationContext.bindSession(session, snapshotManager_);
+      }
+
+      return session;
+   }
+
+   /**
+    * Return the sessions. Note that this will return not only the local
+    * in-memory sessions, but also any sessions that are in the distributed
+    * cache but have not previously been accessed on this server.  Invoking
+    * this method will bring all such sessions into local memory and can
+    * potentially be quite expensive.
+    *
+    * <p>
+    * Note also that when sessions are loaded from the distributed cache, no
+    * check is made as to whether the number of local sessions will thereafter
+    * exceed the maximum number allowed on this server.
+    * </p>
+    *
+    * @return an array of all the sessions
+    */
+   public Session[] findSessions()
+   {
+      // Need to load all the unloaded sessions
+      if(unloadedSessions_.size() > 0)
+      {
+         // Make a thread-safe copy of the new id list to work with
+         Set ids = new HashSet(unloadedSessions_.keySet());
+
+         if(log_.isDebugEnabled()) {
+            log_.debug("findSessions: loading sessions from distributed cache: " + ids);
+         }
+
+         for(Iterator it = ids.iterator(); it.hasNext();) {
+            loadSession((String) it.next());
+         }
+      }
+
+      // All sessions are now "local" so just return the local sessions
+      return findLocalSessions();
+   }
+
+   /**
+    * Returns all the sessions that are being actively managed by this manager.
+    * This includes those that were created on this server, those that were
+    * brought into local management by a call to
+    * {@link #findLocalSession(String)} as well as all sessions brought into
+    * local management by a call to {@link #findSessions()}.
+    */
+   public ClusteredSession[] findLocalSessions()
+   {
+      Collection coll = sessions_.values();
+      ClusteredSession[] sess = new ClusteredSession[coll.size()];
+      sess = (ClusteredSession[]) coll.toArray(sess);
+      return sess;
+   }
+
+   /**
+    * Returns the given session if it is being actively managed by this manager.
+    * An actively managed session is on that was either created on this server,
+    * brought into local management by a call to
+    * {@link #findLocalSession(String)} or brought into local management by a
+    * call to {@link #findSessions()}.
+    *
+    * @param realId the session id, with any trailing jvmRoute removed.
+    *
+    * @see #getRealId(String)
+    */
+   public ClusteredSession findLocalSession(String realId)
+   {
+      return (ClusteredSession) sessions_.get(realId);
+   }
+
+   /**
+    * Removes the session from this Manager's collection of actively managed
+    * sessions.  Also removes the session from the distributed cache, both
+    * on this server and on all other server to which this one replicates.
+    */
+   public void remove(Session session)
+   {
+      ClusteredSession clusterSess = (ClusteredSession) session;
+      synchronized (clusterSess)
+      {
+         String realId = clusterSess.getRealId();
+         if (realId == null)
+            return;
+
+         if (log_.isDebugEnabled())
+         {
+            log_.debug("Removing session from store with id: " + realId);
+         }
+
+         try {
+            // Ignore any cache notifications that our own work generates
+            SessionReplicationContext.startCacheActivity();
+            clusterSess.removeMyself();
+         }
+         finally {
+            SessionReplicationContext.finishCacheActivity();
+            
+            // We don't want to replicate this session at the end
+            // of the request; the removal process took care of that
+            SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
+            
+            sessions_.remove(realId);
+            stats_.removeStats(realId);
+            activeCounter_--;
+         }
+      }
+   }
+
+   /**
+    * Removes the session from this Manager's collection of actively managed
+    * sessions.  Also removes the session from this server's copy of the
+    * distributed cache (but does not remove it from other servers'
+    * distributed cache).
+    */
+   public void removeLocal(Session session)
+   {
+      ClusteredSession clusterSess = (ClusteredSession) session;
+      synchronized (clusterSess)
+      {
+         String realId = clusterSess.getRealId();
+         if (realId == null) return;
+
+         if (log_.isDebugEnabled())
+         {
+            log_.debug("Removing session from local store with id: " + realId);
+         }
+
+         try {
+            // Ignore any cache notifications that our own work generates
+            SessionReplicationContext.startCacheActivity();
+            clusterSess.removeMyselfLocal();
+         }
+         finally
+         {
+            SessionReplicationContext.finishCacheActivity();
+            
+            // We don't want to replicate this session at the end
+            // of the request; the removal process took care of that
+            SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
+            
+            sessions_.remove(realId);
+            stats_.removeStats(realId);
+
+            // Update counters.
+            // It's a bit ad-hoc to do it here. But since we currently call
+            // this when session expires ...
+            expiredCounter_++;
+            activeCounter_--;
+         }
+      }
+   }
+
+   /**
+    * Loads a session from the distributed store.  If an existing session with
+    * the id is already under local management, that session's internal state
+    * will be updated from the distributed store.  Otherwise a new session
+    * will be created and added to the collection of those sessions under
+    * local management.
+    *
+    * @param realId  id of the session-id with any jvmRoute removed
+    *
+    * @return the session or <code>null</code> if the session cannot be found
+    *         in the distributed store
+    *         
+    * TODO refactor this into 2 overloaded methods -- one that takes a ClusteredSession
+    * and populates it and one that takes an id, creates the session and calls the first
+    */
+   protected ClusteredSession loadSession(String realId)
+   {
+      if (realId == null)
+      {
+         return null;
+      }
+
+      long begin = System.currentTimeMillis();
+      boolean mustAdd = false;
+      ClusteredSession session = (ClusteredSession) sessions_.get(realId);
+      if (session == null)
+      {
+         // This is either the first time we've seen this session on this
+         // server, or we previously expired it and have since gotten
+         // a replication message from another server
+         mustAdd = true;
+         session = createEmptyClusteredSession();
+      }
+
+      synchronized (session)
+      {
+         boolean doTx = false;      
+         try
+         {
+            // We need transaction so any data gravitation replication 
+            // is sent in batch.
+            // Don't do anything if there is already transaction context
+            // associated with this thread.
+            if(tm.getTransaction() == null)
+               doTx = true;
+
+            if(doTx)
+               tm.begin();
+            
+            // Ignore cache notifications we may generate for this 
+            // session if data gravitation occurs. 
+            SessionReplicationContext.startCacheActivity();
+            
+            session = proxy_.loadSession(realId, session);
+         }
+         catch (Exception ex)
+         {
+            try
+            {
+//                  if(doTx)
+               // Let's set it no matter what.
+               tm.setRollbackOnly();
+            }
+            catch (Exception exn)
+            {
+               exn.printStackTrace();
+            }
+            
+            // We will need to alert Tomcat of this exception.
+            if (ex instanceof RuntimeException)
+               throw (RuntimeException) ex;
+            
+            throw new RuntimeException("loadSession(): failed to load session " +
+                                       realId, ex);
+         }
+         finally
+         {
+            try {
+               if(doTx)
+                  endTransaction(realId);
+            }
+            finally {
+               SessionReplicationContext.finishCacheActivity();
+            }
+         }
+
+         if (session != null)
+         {
+            // Need to initialize.
+            session.initAfterLoad(this);
+            if (mustAdd)
+               add(session, false); // don't replicate
+            long elapsed = System.currentTimeMillis() - begin;
+            stats_.updateLoadStats(realId, elapsed);
+
+            if (log_.isDebugEnabled())
+            {
+               log_.debug("loadSession(): id= " + realId + ", session=" + session);
+            }
+         }
+         else if (log_.isDebugEnabled())
+         {
+            log_.debug("loadSession(): session " + realId +
+                       " not found in distributed cache");
+         }
+      }
+
+      return session;
+   }
+
+   /**
+    * Places the current session contents in the distributed cache and
+    * replicates them to the cluster
+    *
+    * @param session  the session.  Cannot be <code>null</code>.
+    */
+   protected void processSessionRepl(ClusteredSession session)
+   {
+      // If we are using SESSION granularity, we don't want to initiate a TX
+      // for a single put
+      boolean notSession = (replicationGranularity_ != WebMetaData.REPLICATION_GRANULARITY_SESSION);
+      boolean doTx = false;
+      try
+      {
+         // We need transaction so all the replication are sent in batch.
+         // Don't do anything if there is already transaction context
+         // associated with this thread.
+         if(notSession && tm.getTransaction() == null)
+            doTx = true;
+
+         if(doTx)
+            tm.begin();
+
+         // Tell the proxy to ignore cache notifications we are about
+         // to generate for this session. We have to do this
+         // at this level because we don't want to resume handling
+         // notifications until any compensating changes resulting
+         // from a tx rollback are done.
+         SessionReplicationContext.startCacheActivity();
+
+         session.processSessionRepl();
+      }
+      catch (Exception ex)
+      {
+         if (log_.isDebugEnabled())
+            log_.debug("processSessionRepl(): failed with exception", ex);
+         
+         try
+         {
+//            if(doTx)
+               // Let's set it no matter what.
+               tm.setRollbackOnly();
+         }
+         catch (Exception exn)
+         {
+            exn.printStackTrace();
+         }
+         // We will need to alert Tomcat of this exception.
+         if (ex instanceof RuntimeException)
+            throw (RuntimeException) ex;
+         
+         throw new RuntimeException("JBossCacheManager.processSessionRepl(): " +
+                                    "failed to replicate session.", ex);
+      }
+      finally
+      {
+         try {
+            if(doTx)
+               endTransaction(session.getId());
+         }
+         finally {
+            SessionReplicationContext.finishCacheActivity();
+         }
+      }
+   }
+
+   protected void endTransaction(String id)
+   {
+      if (tm == null)
+      {
+         log_.warn("JBossCacheManager.endTransaction(): tm is null for id: " +id);
+         return;
+      }
+
+
+      try
+      {
+         if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
+         {
+            tm.commit();
+         }
+         else
+         {
+            log_.info("JBossCacheManager.endTransaction(): rolling back tx for id: " +id);
+            tm.rollback();
+         }
+      }
+      catch (RollbackException re)
+      {
+         // Do nothing here since cache may rollback automatically.
+         log_.warn("JBossCacheManager.endTransaction(): rolling back transaction with exception: " +re);
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("JBossCacheManager.endTransaction(): Exception for id: " +id, e);
+      }
+   }
+   
+   /**
+    * Gets the classloader of the webapp we are managing.
+    */
+   protected ClassLoader getWebappClassLoader()
+   {
+      return tcl_;
+   }
+
+   /**
+    * Goes through all sessions and look if they have expired.
+    * Note this overrides the method in JBossManager.
+    */
+   protected void processExpires()
+   {
+      if (maxInactiveInterval_ < 0)
+      {
+         return;
+      }
+
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("Looking for sessions that have expired ...");
+      }
+
+      try
+      {
+         // First, handle the sessions we are actively managing
+         Session sessions[] = findLocalSessions();
+         for (int i = 0; i < sessions.length; ++i)
+         {
+            try
+            {
+               ClusteredSession session = (ClusteredSession) sessions[i];
+               if(session == null)
+               {
+                  log_.warn("processExpires(): processing null session at index " +i);
+                  continue;
+               }
+
+               // JBAS-2403. Check for outdated sessions where we think
+               // the local copy has timed out.  If found, refresh the
+               // session from the cache in case that might change the timeout
+               if (session.isOutdated() && !(session.isValid(false)))
+               {
+                  // JBAS-2792 don't assign the result of loadSession to session
+                  // just update the object from the cache or fall through if
+                  // the session has been removed from the cache
+                  loadSession(session.getRealId());
+               }
+
+               // Do a normal invalidation check that will expire any
+               // sessions that have timed out
+               // DON'T SYNCHRONIZE on session here -- isValid() and
+               // expire() are meant to be multi-threaded and synchronize
+               // properly internally; synchronizing externally can lead
+               // to deadlocks!!
+               if (!session.isValid()) continue;
+            }
+            catch (Exception ex)
+            {
+               log_.error("processExpires(): failed expiring " + 
+                          sessions[i].getIdInternal() + " with exception: " + 
+                          ex, ex);
+            }
+         }
+
+         // Next, handle any unloaded sessions that are stale
+
+         long now = System.currentTimeMillis();
+         Map unloaded = new HashMap(unloadedSessions_);
+         Set entries = unloaded.entrySet();
+         for (Iterator it = entries.iterator(); it.hasNext(); )
+         {
+            Map.Entry entry = (Map.Entry) it.next();
+            OwnedSessionUpdate osu = (OwnedSessionUpdate) entry.getValue();
+            int elapsed = (int) ((now - osu.updateTime) / 1000L);
+            if (elapsed >= maxInactiveInterval_)
+            {
+               String realId = (String) entry.getKey();
+               try
+               {
+                  proxy_.removeSessionLocal(realId, osu.owner);
+                  unloadedSessions_.remove(realId);
+               }
+               catch (Exception ex)
+               {
+                  log_.error("processExpire(): failed removing unloaded session " + 
+                          realId + " with exception: " + 
+                          ex, ex);
+               }
+            }
+         }
+      }
+      catch (Exception ex)
+      {
+         log_.error("processExpires: failed with exception: " + ex, ex);
+      }
+   }
+   
+   public void processRemoteAttributeRemoval(String realId, String attrKey)
+   {
+      
+      ClusteredSession session = findLocalSession(realId);
+      if (session != null)
+      {
+         boolean localCall = false; // call is due to remote event
+         boolean localOnly = true;  // don't call back into cache
+         boolean notify = false;    // SRV.10.7 gives us leeway
+                                    // not to notify listeners, 
+                                    // which is safer
+         
+         // Ensure the correct TCL is in place
+         ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
+         try
+         {
+            Thread.currentThread().setContextClassLoader(tcl_);
+            synchronized (session)
+            {
+               session.removeAttributeInternal(attrKey, localCall, localOnly, notify);
+            }
+            if (log_.isTraceEnabled())
+               log_.trace("processRemoteAttributeRemoval: removed attribute " + 
+                          attrKey + " from " + realId);
+         }
+         finally
+         {
+            Thread.currentThread().setContextClassLoader(prevTcl);
+         }
+      }      
+   }
+
+   public void processRemoteInvalidation(String realId)
+   {
+      // Remove the session from our local map
+      ClusteredSession session = (ClusteredSession) sessions_.remove(realId);
+      if (session == null)
+      {
+         // We weren't managing the session anyway.  But remove it
+         // from the list of cached sessions we haven't loaded
+         if (unloadedSessions_.remove(realId) != null)
+         {
+            if (log_.isTraceEnabled())
+               log_.trace("Removed entry for session " + realId + " from unloaded session map");
+         }
+      }
+      else
+      {
+         // Expire the session
+         // DON'T SYNCHRONIZE ON SESSION HERE -- isValid() and
+         // expire() are meant to be multi-threaded and synchronize
+         // properly internally; synchronizing externally can lead
+         // to deadlocks!!
+         boolean notify = false; // Don't notify listeners. SRV.10.7
+                                 // allows this, and sending notifications
+                                 // leads to all sorts of issues; e.g.
+                                 // circular calls with ClusteredSSO
+         boolean localCall = false; // this call originated from the cache;
+                                    // we have already removed session
+         boolean localOnly = true; // Don't pass attr removals to cache
+         
+         // Ensure the correct TCL is in place
+         ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();         
+         try
+         {
+            Thread.currentThread().setContextClassLoader(tcl_);
+            session.expire(notify, localCall, localOnly);
+         }
+         finally
+         {
+            Thread.currentThread().setContextClassLoader(prevTcl);
+         }
+
+         // Remove any stats for this session
+         stats_.removeStats(realId);
+         
+         // Update counter.
+         activeCounter_--;
+      }
+   }
+
+   /**
+    * Gets the session id with any jvmRoute removed.
+    * 
+    * @param id a session id with or without an appended jvmRoute.
+    *           Cannot be <code>null</code>.
+    */
+   protected String getRealId(String id)
+   {
+      return (useJK_ ? Util.getRealId(id) : id);
+   }
+   
+   /**
+    * Callback from the CacheListener to notify us that a session
+    * we haven't loaded has been changed.
+    * 
+    * @param realId the session id, without any trailing jvmRoute
+    * @param dataOwner  the owner of the session.  Can be <code>null</code> if
+    *                   the owner is unknown.
+    */
+   protected void unloadedSessionChanged(String realId, String dataOwner)
+   {
+      Object obj = unloadedSessions_.put(realId, 
+            new OwnedSessionUpdate(dataOwner, System.currentTimeMillis()));
+      if (log_.isTraceEnabled())
+      {
+         if (obj == null)
+         {
+            log_.trace("New session " + realId + " added to unloaded session map");
+         }
+         else
+         {
+            log_.trace("Updated timestamp for unloaded session " + realId);
+         }
+      }
+   }
+
+   // ----------------------------------------------------  Lifecyle Unembedded
+   
+   /**
+    * Start this Manager when running embedded in JBoss AS.
+    *
+    * @throws org.apache.catalina.LifecycleException
+    */
+   private void startEmbedded() throws LifecycleException
+   {
+      super.start();
+      
+      // Start the JBossCacheService
+      // Will need to pass the classloader that is associated with this 
+      //web app so de-serialization will work correctly.
+      tcl_ = super.getContainer().getLoader().getClassLoader();
+
+      proxy_.start(tcl_, this);
+
+      tm = proxy_.getTransactionManager();
+      if(tm == null)
+      {
+         throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
+      }
+      
+      try
+      {
+         initializeUnloadedSessions();
+         
+         // Setup our SnapshotManager
+         initSnapshotManager();
+         
+         // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
+         installValves();
+
+         log_.debug("start(): JBossCacheService started");         
+      }
+      catch (Exception e)
+      {
+         log_.error("Unable to start manager.", e);
+         throw new LifecycleException(e);
+      }
+   }
+   
+   // -----------------------------------------------  Lifecyle When Unembedded
+
+   /**
+    * Start this Manager when running in standalone Tomcat.
+    */
+   private void startUnembedded() throws LifecycleException
+   {
+      if (started_)
+      {
+         return;
+      }
+      
+      if (log_.isInfoEnabled())
+      {
+         log_.info("Manager is about to start");
+      }
+
+      // Notify our interested LifecycleListeners
+      lifecycle_.fireLifecycleEvent(BEFORE_START_EVENT, this);
+      
+      if (snapshotMode_ == null) 
+      {
+         // We were not instantiated by a JBossCacheCluster, so we need to
+         // find one and let it configure our cluster-wide properties
+         try 
+         {
+            JBossCacheCluster cluster = (JBossCacheCluster) container_.getCluster();
+            cluster.configureManager(this);
+         }
+         catch (ClassCastException e)
+         {
+            String msg = "Cluster is not an instance of JBossCacheCluster";
+            log_.error(msg, e);
+            throw new LifecycleException(msg, e);
+         }
+      }
+      
+      // Validate attributes
+      
+      if ("SET_AND_GET".equalsIgnoreCase(replTriggerString_))         
+         this.invalidateSessionPolicy_ = WebMetaData.SESSION_INVALIDATE_SET_AND_GET;
+      else if ("SET_AND_NON_PRIMITIVE_GET".equalsIgnoreCase(replTriggerString_))
+         this.invalidateSessionPolicy_ = WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET;
+      else if ("SET".equalsIgnoreCase(replTriggerString_))
+         this.invalidateSessionPolicy_ = WebMetaData.SESSION_INVALIDATE_SET;
+      else
+         throw new LifecycleException("replication-trigger value set to a " +
+                                       "non-valid value: '" + 
+                                       replTriggerString_ + 
+                                       "' (should be ['SET_AND_GET', " +
+                                       "'SET_AND_NON_PRIMITIVE_GET', 'SET'])");
+      
+      if ("SESSION".equalsIgnoreCase(replGranularityString_))
+         this.replicationGranularity_ = WebMetaData.REPLICATION_GRANULARITY_SESSION;
+      else if ("ATTRIBUTE".equalsIgnoreCase(replGranularityString_))
+         this.replicationGranularity_ = WebMetaData.REPLICATION_GRANULARITY_ATTRIBUTE;
+      else if ("FIELD".equalsIgnoreCase(replGranularityString_))
+         this.replicationGranularity_ = WebMetaData.REPLICATION_GRANULARITY_FIELD;
+      else
+         throw new LifecycleException("replication-granularity value set to " +
+                                       "a non-valid value: '" + 
+                                       replGranularityString_ +
+                                       "' (should be ['SESSION', " +
+                                       "'ATTRIBUTE' or 'FIELD'])");      
+      
+      // Create the JBossCacheService
+      try
+      {
+         proxy_ = new JBossCacheService(cacheObjectNameString_);
+         
+         // Confirm our replication granularity is compatible with the cache
+         // Throws ISE if not
+         validateFieldMarshalling();
+         
+         // We need to pass the classloader that is associated with this 
+         // web app so de-serialization will work correctly.
+         tcl_ = container_.getLoader().getClassLoader();
+         proxy_.start(tcl_, this);
+      }
+      catch (Throwable t)
+      {
+         String str = "Problem starting JBossCacheService for Tomcat clustering";
+         log_.error(str, t);
+         throw new LifecycleException(str, t);
+      }
+      
+      tm = proxy_.getTransactionManager();
+      if(tm == null)
+      {
+         throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
+      }
+      
+      try
+      {
+         initializeUnloadedSessions();
+
+         // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
+         installValves();
+
+         started_ = true;
+         
+         // Notify our interested LifecycleListeners
+         lifecycle_.fireLifecycleEvent(AFTER_START_EVENT, this);
+         
+         if (log_.isDebugEnabled())
+         {
+            log_.debug("start(): JBossCacheService started");
+         }
+      } 
+      catch (Exception e)
+      {
+         log_.error("Unable to start manager.", e);
+         throw new LifecycleException(e);
+      }
+      
+      try
+      {
+         registerMBeans();
+      }
+      catch (Exception e)
+      {
+         log_.error("Could not register ManagerMBean with MBeanServer", e);
+      }
+   }
+
+   /**
+    * Register this Manager with JMX.
+    */
+   private void registerMBeans() 
+   {
+      try
+      {
+         MBeanServer server = getMBeanServer();
+
+         String domain;
+         if (container_ instanceof ContainerBase)
+         {
+            domain = ((ContainerBase) container_).getDomain();
+         }
+         else
+         {
+            domain = server.getDefaultDomain();
+         }
+         String hostName = ((Host) container_.getParent()).getName();
+         hostName = (hostName == null) ? "localhost" : hostName;
+         ObjectName clusterName = new ObjectName(domain
+               + ":service=ClusterManager,WebModule=//" + hostName
+               + ((Context) container_).getPath());
+
+         if (server.isRegistered(clusterName))
+         {
+            log_.warn("MBean " + clusterName + " already registered");
+            return;
+         }
+
+         objectName_ = clusterName;
+         server.registerMBean(this, clusterName);
+
+      }
+      catch (Exception ex)
+      {
+         log_.error(ex.getMessage(), ex);
+      }
+   }
+
+   /**
+    * Unregister this Manager from the JMX server.
+    */
+   private void unregisterMBeans()
+   {
+      if (mserver_ != null)
+      {
+         try
+         {
+            mserver_.unregisterMBean(objectName_);
+         }
+         catch (Exception e)
+         {
+            log_.error(e);
+         }
+      }
+   }
+
+   /**
+    * Get the current MBean Server.
+    * 
+    * @return
+    * @throws Exception
+    */
+   private MBeanServer getMBeanServer() throws Exception
+   {
+      if (mserver_ == null)
+      {
+         mserver_ = MBeanServerLocator.locateJBoss();
+      }
+      return (mserver_);
+   }
+   
+   /**
+    * Gets the ids of all sessions in the distributed cache and adds
+    * them to the unloaded sessions map, with the current time as the
+    * last replication time.  This means these sessions may not be
+    * evicted from the cache for a period well beyond when they would
+    * normally expire, but this is a necessary tradeoff to avoid
+    * deserializing them all to check their lastAccessedTime.
+    */
+   private void initializeUnloadedSessions() throws CacheException
+   {      
+      Map sessions = proxy_.getSessionIds();
+      if (sessions != null)
+      {
+         long now = System.currentTimeMillis();
+         for (Iterator it = sessions.entrySet().iterator(); it.hasNext(); )
+         {
+            Map.Entry entry = (Entry) it.next();
+            unloadedSessions_.put(entry.getKey(), 
+                  new OwnedSessionUpdate((String) entry.getValue(), now));
+         }
+      }
+   }
+
+   /**
+    * Instantiate a SnapshotManager and ClusteredSessionValve and add 
+    * the valve to our parent Context's pipeline.  
+    * Add a JvmRouteValve and BatchReplicationClusteredSessionValve if needed.
+    * 
+    */
+   private void installValves()
+   {  
+      if (useJK_)
+      {
+         log_.info("We are using mod_jk(2) for load-balancing. " +
+                   "Will add JvmRouteValve.");
+         
+         installContextValve(new JvmRouteValve(this));         
+      }
+         
+      // Add batch replication valve if needed.
+      // TODO -- should we add this even if not FIELD in case a cross-context
+      // call traverses a field-based webapp?
+      if (replicationGranularity_ == WebMetaData.REPLICATION_GRANULARITY_FIELD &&
+          Boolean.TRUE.equals(replicationFieldBatchMode_))
+      {
+         Valve batchValve = new BatchReplicationClusteredSessionValve(this);
+         log_.debug("Adding BatchReplicationClusteredSessionValve for batch replication.");
+         installContextValve(batchValve);
+      }
+
+      // Add clustered session valve
+      ClusteredSessionValve valve = new ClusteredSessionValve();
+      installContextValve(valve);
+   }
+
+   /**
+    * Create and start a snapshot manager.
+    */
+   private void initSnapshotManager()
+   {
+      String ctxPath = ((Context) container_).getPath();
+      if ("instant".equals(snapshotMode_) || replicationGranularity_
+            == WebMetaData.REPLICATION_GRANULARITY_FIELD)
+      {
+         snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
+      }
+      else if ("interval".equals(snapshotMode_))
+      {
+         snapshotManager_ = new IntervalSnapshotManager(this, ctxPath, snapshotInterval_);
+      }
+      else
+      {
+         log_.error("Snapshot mode must be 'instant' or 'interval' - " +
+                    "using 'instant'");
+         snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
+      }
+      
+      snapshotManager_.start();
+   }
+   
+   private void installContextValve(Valve valve)
+   {
+      boolean installed = false;
+      
+      // In embedded mode, install the valve via JMX to be consistent
+      // with the way the overall context is created in TomcatDeployer.
+      // We can't do this in unembedded mode because we are called
+      // before our Context is registered with the MBean server
+      if (embedded_ && getContextObjectName() != null) {
+         try
+         {
+            getMBeanServer().invoke(getContextObjectName(), "addValve",
+                                    new Object[]{valve},
+                                    new String[]{"org.apache.catalina.Valve"});
+            installed = true;
+         }
+         catch (Exception e)
+         {
+            // JBAS-2422.  If the context is restarted via JMX, the above
+            // JMX call will fail as the context will not be registered
+            // when it's made.  So we catch the exception and fall back
+            // to adding the valve directly.
+            // TODO consider skipping adding via JMX and just do it directly
+            log_.debug("Caught exception installing valve to Context", e);
+         }
+      }
+      
+      if (!installed)
+      {
+         // If possible install via the ContainerBase.addValve() API.
+         if (container_ instanceof ContainerBase)
+         {            
+            ((ContainerBase) container_).addValve(valve);
+         }
+         else
+         {
+            // No choice; have to add it to the context's pipeline
+            container_.getPipeline().addValve(valve);
+         }
+      }
+   }
+
+   /**
+    * If we are using FIELD granularity, checks that the TreeCache
+    * supports marshalling.
+    * 
+    * @throws IllegalStateException if not
+    */
+   private void validateFieldMarshalling()
+   {
+      if (replicationGranularity_ == WebMetaData.REPLICATION_GRANULARITY_FIELD
+            && !proxy_.isMarshallingAvailable())
+      {
+         // BES 16/8/2006 -- throw ISE, not ClusteringNotSupportedException, as a
+         // misconfig should be treated differently from the absence of clustering 
+         // services         
+         throw new IllegalStateException("replication-granularity value is set to " +
+               "'FIELD' but is not supported by the cache service configuration. " +
+               "Must set 'UseRegionBasedMarshalling' to 'true' in the tc5-cluster.sar jboss-service.xml");
+      }
+   }
+   
+   private ObjectName getContextObjectName()
+   {
+      String oname = container_.getObjectName();
+      try
+      {         
+         return (oname == null) ? null : new ObjectName(oname);
+      }
+      catch (MalformedObjectNameException e)
+      {
+         log_.warn("Error creating object name from string " + oname, e);
+         return null;
+      }
+   }
+
+   
+   private class OwnedSessionUpdate
+   {
+      String owner;
+      long updateTime;
+      
+      OwnedSessionUpdate(String owner, long updateTime)
+      {
+         this.owner = owner;
+         this.updateTime = updateTime;
+      }
+   }
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,128 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+public interface JBossCacheManagerMBean extends JBossManagerMBean
+{
+   /**
+    * Gets the value of the attribute with the given key from the given
+    * session.  If the session is in the distributed store but hasn't been
+    * loaded on this node, invoking this method will cause it to be loaded.
+    * 
+    * @param sessionId the id of the session
+    * @param key       the attribute key
+    * @return          the value, or <code>null</code> if the session or
+    *                  key does not exist.
+    */
+   Object getSessionAttribute(String sessionId, String key);
+   
+   /**
+    * Same as <code>getSessionAttribute(sessionId, key).toString()</code>.
+    * 
+    */
+   String getSessionAttributeString(String sessionId, String key);
+   
+   /**
+    * Expires the given session. If the session is in the distributed store 
+    * but hasn't been loaded on this node, invoking this method will cause it 
+    * to be loaded.
+    * 
+    * @param sessionId the id of the session
+    */
+   void expireSession(String sessionId);
+   
+   /**
+    * Gets the last time the given session was accessed on this node.
+    * Information about sessions stored in the distributed store but never
+    * accessed on this node will not be made available.
+    * 
+    * @param sessionId
+    * @return the last accessed time, or <code>null</code> if the session
+    *         has expired or has never been accessed on this node.
+    */
+   String getLastAccessedTime(String sessionId);
+   
+   /**
+    * Gets the JMX ObjectName of the distributed session cache as a string.
+    */
+   String getCacheObjectNameString();
+   
+   /**
+    * Gets the replication granularity.
+    * 
+    * @return SESSION, ATTRIBUTE or FIELD, or <code>null</code> if this
+    *         has not yet been configured.
+    */
+   String getReplicationGranularityString();
+
+   /**
+    * Gets the replication trigger.
+    * 
+    * @return SET, SET_AND_GET, SET_AND_NON_PRIMITIVE_GET or <code>null</code> 
+    *         if this has not yet been configured.
+    */
+   String getReplicationTriggerString();
+   
+   /**
+    * Gets whether batching of field granularity changes will be done.  Only
+    * relevant with replication granularity FIELD.
+    * 
+    * @return <code>true</code> if per-request batching will be done, 
+    *         <code>false</code> if not, <code>null</code> if not configured
+    */
+   Boolean isReplicationFieldBatchMode();
+   
+   /**
+    * Gets whether JK is being used and special handling of a jvmRoute
+    * portion of session ids is needed.
+    */
+   boolean getUseJK();
+   
+   /**
+    * Gets the snapshot mode.
+    * 
+    * @return "instant" or "interval"
+    */
+   String getSnapshotMode();
+   
+   /**
+    * Gets the number of milliseconds between replications if "interval" mode
+    * is used.
+    */
+   int getSnapshotInterval();
+   
+   /**
+    * Lists all session ids known to this manager, including those in the 
+    * distributed store that have not been accessed on this node.
+    * 
+    * @return a comma-separated list of session ids
+    */
+   String listSessionIds();
+   
+   /**
+    * Lists all session ids known to this manager, excluding those in the 
+    * distributed store that have not been accessed on this node.
+    * 
+    * @return a comma-separated list of session ids
+    */
+   String listLocalSessionIds();
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1043 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.WeakHashMap;
+
+import javax.management.ObjectName;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.Context;
+import org.jboss.aspects.patterns.observable.Observer;
+import org.jboss.aspects.patterns.observable.Subject;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.aop.PojoCacheMBean;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.transaction.BatchModeTransactionManager;
+import org.jboss.invocation.MarshalledValue;
+import org.jboss.invocation.MarshalledValueInputStream;
+import org.jboss.invocation.MarshalledValueOutputStream;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanProxyExt;
+
+/**
+ * A wrapper class to JBossCache. This is currently needed to handle various operations such as
+ * <ul>
+ * <li>Using MarshalledValue to replace Serializable used inside different web app class loader context.</li>
+ * <li>Stripping out any id string after ".". This is to handle the JK failover properly with
+ * Tomcat JvmRoute.</li>
+ * <li>Cache exception retry.</li>
+ * <li>Helper APIS.</li>
+ * </ul>
+ */
+public class JBossCacheService
+{   
+   protected static Logger log_ = Logger.getLogger(JBossCacheService.class);
+   public static final String BUDDY_BACKUP = BuddyManager.BUDDY_BACKUP_SUBTREE;
+   public static final Fqn BUDDY_BACKUP_FQN = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN;
+   public static final String SESSION = "JSESSION";
+   public static final String ATTRIBUTE = "ATTRIBUTE";
+   // Needed for cache invalidation
+   static final String VERSION_KEY = "VERSION";
+   static final String FQN_DELIMITER = "/";
+   
+   private PojoCacheMBean proxy_;
+   private ObjectName cacheServiceName_;
+   
+   // name of webapp's virtual host(JBAS-2194). 
+   // Idea is host_name + web_app_path + session id is a unique combo.
+   private String hostName_;
+   // web app path (JBAS-1367 and JBAS-2194). 
+   // Idea is host_name + web_app_path + session id is a unique combo.
+   private String webAppPath_;
+   private TransactionManager tm;
+
+   private JBossCacheManager manager_;
+   private CacheListener cacheListener_;
+   private JBossCacheWrapper cacheWrapper_;
+   
+   // Do we have to marshall attributes ourself or can we let 
+   // the TreeCache do it?
+   private boolean useTreeCacheMarshalling_ = false;
+   
+   private WeakHashMap typeMap = new WeakHashMap();
+   
+   public JBossCacheService(String treeCacheObjectName) throws ClusteringNotSupportedException
+   {
+      // Find JBossCacheService
+      try
+      {
+         cacheServiceName_ = new ObjectName(treeCacheObjectName);
+         // Create Proxy-Object for this service
+         proxy_ = (PojoCacheMBean) MBeanProxyExt.create(PojoCacheMBean.class,
+                                                        cacheServiceName_);
+      }
+      catch (Throwable t)
+      {
+
+         String str = "Could not access TreeCache service " + 
+                     (cacheServiceName_ == null ? "<null>" : cacheServiceName_.toString()) + 
+                     " for Tomcat clustering";
+         log_.debug(str);
+         throw new ClusteringNotSupportedException(str, t);
+      }
+      
+      if (proxy_ == null)
+      {
+         String str = "Could not access TreeCache service " + 
+                     (cacheServiceName_ == null ? "<null>" : cacheServiceName_.toString()) + 
+                     " for Tomcat clustering";
+         log_.debug(str);
+         throw new ClusteringNotSupportedException(str);
+      }
+
+      cacheWrapper_ = new JBossCacheWrapper(proxy_);
+      
+      useTreeCacheMarshalling_ = proxy_.getUseRegionBasedMarshalling();
+   }
+
+   public void start(ClassLoader tcl, JBossCacheManager manager)
+   {
+      manager_ = manager;
+      
+      Context webapp = (Context) manager_.getContainer();
+      String path = webapp.getName();
+      if( path.length() == 0 || path.equals("/")) {
+         // If this is root.
+         webAppPath_ = "ROOT";
+      } else if ( path.startsWith("/") ) {
+         webAppPath_ = path.substring(1);
+      } else {
+         webAppPath_ = path;
+      }
+      log_.debug("Old and new web app path are: " +path + ", " +webAppPath_);
+      
+      String host = webapp.getParent().getName();
+      if( host == null || host.length() == 0) {
+         hostName_ = "localhost";
+      }else {
+         hostName_ = host;
+      }
+      log_.debug("Old and new virtual host name are: " + host + ", " + hostName_);
+      
+
+      // Listen for cache changes
+      cacheListener_ = new CacheListener(cacheWrapper_, manager_, hostName_, webAppPath_);
+      proxy_.addTreeCacheListener(cacheListener_);
+
+      // register the tcl and bring over the state for the webapp
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+      Fqn pathFqn = new Fqn( objs );
+      String fqnStr = pathFqn.toString();
+      try {
+         if(useTreeCacheMarshalling_)
+         {
+            log_.debug("UseMarshalling is true. We will register the fqn: " +
+                        fqnStr + " with class loader" +tcl +
+                        " and activate the webapp's Region");
+            proxy_.registerClassLoader(fqnStr, tcl);
+            proxy_.activateRegion(fqnStr);
+         }
+      } catch (Exception ex)
+      {
+         throw new RuntimeException("Can't register class loader", ex);
+      }
+
+      // We require the cache tm to be BatchModeTransactionManager now.
+      tm = proxy_.getTransactionManager();
+      if( ! (tm instanceof BatchModeTransactionManager) )
+      {
+         throw new RuntimeException("JBossCacheService.start(): JBossCacheAop transaction manager is not type BatchModeTransactionManager." +
+                 " Please check the tc5-cluster-service.xml TransactionManagerClassLookup field.");
+      }
+   }
+
+   public void stop()
+   {
+      proxy_.removeTreeCacheListener(cacheListener_);
+
+      // Construct the fqn
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+      Fqn pathFqn = new Fqn( objs );
+
+      String fqnStr = pathFqn.toString();
+      if(useTreeCacheMarshalling_)
+      {
+            log_.debug("UseMarshalling is true. We will inactivate the fqn: " +
+                       fqnStr + " and un-register its classloader");
+            
+         try {
+            proxy_.inactivateRegion(fqnStr);
+            proxy_.unregisterClassLoader(fqnStr);          
+         }
+         catch (Exception e) 
+         {
+            log_.error("Exception during inactivation of webapp region " + fqnStr + 
+                        " or un-registration of its class loader", e);
+         }
+      }
+
+      // remove session data
+      cacheWrapper_.evictSubtree(pathFqn);
+   }
+
+   /**
+    * Get specfically the BatchModeTransactionManager.
+    */
+   public TransactionManager getTransactionManager()
+   {
+      return tm;
+   }
+   
+   
+   /**
+    * Gets whether TreeCache-based marshalling is available
+    */
+   public boolean isMarshallingAvailable()
+   {
+      return useTreeCacheMarshalling_;
+   }
+
+   /**
+    * Loads any serialized data in the cache into the given session
+    * using its <code>readExternal</code> method.
+    *
+    * @return the session passed as <code>toLoad</code>, or
+    *         <code>null</code> if the cache had no data stored
+    *         under the given session id.
+    */
+   public ClusteredSession loadSession(String realId, ClusteredSession toLoad)
+   {
+      Fqn fqn = getSessionFqn(realId);
+   
+      
+      Object sessionData = cacheWrapper_.get(fqn, realId, true);
+      
+      if (sessionData == null) {
+         // Requested session is no longer in the cache; return null
+         return null;
+      }
+      
+      boolean firstLoad = (toLoad.getVersion() == 0);
+      
+//      if (useTreeCacheMarshalling_)
+//      {
+//         toLoad.update((ClusteredSession) sessionData);
+//      }
+//      else
+//      {
+         byte[] sessionBytes = (byte[]) sessionData;
+         
+         // Swap in/out the webapp classloader so we can deserialize
+         // attributes whose classes are only available to the webapp
+         ClassLoader prevTCL = Thread.currentThread().getContextClassLoader();
+         Thread.currentThread().setContextClassLoader(manager_.getWebappClassLoader());
+         try
+         {
+            ByteArrayInputStream bais = new ByteArrayInputStream(sessionBytes);
+            // Use MarshalledValueInputStream instead of superclass ObjectInputStream
+            // or else there are problems finding classes with scoped loaders
+            MarshalledValueInputStream input = new MarshalledValueInputStream(bais);
+            toLoad.readExternal(input);
+            input.close();
+         }
+         catch (Exception e)
+         {
+            log_.error("loadSession(): id: " + realId + "exception occurred during serialization: " +e);
+            return null;
+         }
+         finally {
+            Thread.currentThread().setContextClassLoader(prevTCL);
+         }
+//      }
+      
+      // The internal version of the serialized session may be less than the
+      // real one due to not replicating metadata.  If our listener hasn't 
+      // been keeping the outdatedVersion of the session up to date because
+      // the session has never been loaded into the JBCManager cache, we 
+      // need to fix the version
+      if (firstLoad)
+      {         
+         Integer ver = (Integer) cacheWrapper_.get(fqn, VERSION_KEY);
+         if (ver != null)
+            toLoad.setVersion(ver.intValue());
+      }
+      
+      return toLoad;
+   }
+
+   public void putSession(String realId, ClusteredSession session)
+   {
+      Fqn fqn = getSessionFqn(realId);
+      
+      if (session.getReplicateSessionBody())
+      {
+         Map map = new HashMap();
+//         if (useTreeCacheMarshalling_)
+//            map.put(realId, session);
+//         else
+            map.put(realId, externalizeSession(session));
+         // Put in (VERSION_KEY, version) after the real put for cache invalidation
+         map.put(VERSION_KEY, new Integer(session.getVersion()));
+         cacheWrapper_.put(fqn, map);
+      }
+      else
+      {
+         // Invalidate the remote caches
+         cacheWrapper_.put(fqn, VERSION_KEY, new Integer(session.getVersion()));
+      }
+   }
+
+   public void removeSession(String realId)
+   {
+      Fqn fqn = getSessionFqn(realId);
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("Remove session from distributed store. Fqn: " + fqn);
+      }
+      //Object obj = getUnMarshalledValue(cacheWrapper_.remove(fqn, realId));
+      cacheWrapper_.remove(fqn, realId); 
+      // This needs to go after object removal to support correct cache invalidation.
+//      _remove(fqn, VERSION_KEY);
+      // Let just remove the whole thing (including the fqn)
+      cacheWrapper_.remove(fqn);
+      //return obj;
+   }
+
+   public void removeSessionLocal(String realId)
+   {
+      Fqn fqn = getSessionFqn(realId);
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("Remove session from my own distributed store only. Fqn: " + fqn);
+      }
+      cacheWrapper_.evictSubtree(fqn);
+   }
+
+   public void removeSessionLocal(String realId, String dataOwner)
+   {
+      if (dataOwner == null)
+      {
+         removeSessionLocal(realId);
+      }
+      else
+      {         
+         Fqn fqn = getSessionFqn(realId, dataOwner);
+         if (log_.isDebugEnabled())
+         {
+            log_.debug("Remove session from my own distributed store only. Fqn: " + fqn);
+         }
+         cacheWrapper_.evictSubtree(fqn);
+      }
+   }
+
+   public boolean exists(String realId)
+   {
+      Fqn fqn = getSessionFqn(realId);
+      return proxy_.exists(fqn);
+   }
+
+   public Object getAttribute(String realId, String key)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      return getUnMarshalledValue(cacheWrapper_.get(fqn, key));
+   }
+
+   public void putAttribute(String realId, String key, Object value)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      cacheWrapper_.put(fqn, key, getMarshalledValue(value));
+   }
+
+   public void putAttribute(String realId, Map map)
+   {
+      // Duplicate the map with marshalled values
+      Map marshalled = new HashMap(map.size());
+      Set entries = map.entrySet();
+      for (Iterator it = entries.iterator(); it.hasNext(); )
+      {
+         Map.Entry entry = (Map.Entry) it.next();
+         marshalled.put(entry.getKey(), getMarshalledValue(entry.getValue()));
+      }
+      
+      Fqn fqn = getAttributeFqn(realId);
+      cacheWrapper_.put(fqn, marshalled);
+      
+   }
+
+   public void removeAttributes(String realId)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      cacheWrapper_.remove(fqn);
+   }
+
+   public Object removeAttribute(String realId, String key)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      if (log_.isTraceEnabled())
+      {
+         log_.trace("Remove attribute from distributed store. Fqn: " + fqn + " key: " + key);
+      }
+      return getUnMarshalledValue(cacheWrapper_.remove(fqn, key));
+   }
+
+   public void removeAttributesLocal(String realId)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("Remove attributes from my own distributed store only. Fqn: " + fqn);
+      }
+      cacheWrapper_.evict(fqn);
+   }
+
+   /**
+    * Obtain the keys associated with this fqn. Note that it is not the fqn children.
+    *
+    */
+   public Set getAttributeKeys(String realId)
+   {
+      Set keys = null;
+      Fqn fqn = getAttributeFqn(realId);
+      try
+      {
+         keys = proxy_.getKeys(fqn);
+      }
+      catch (CacheException e)
+      {
+         log_.error("getAttributeKeys(): Exception getting keys for session " + realId, e);
+      }
+      
+      return keys;
+   }
+
+   /**
+    * Return all attributes associated with this session id.
+    *
+    * @param realId the session id with any jvmRoute removed
+    * @return the attributes, or any empty Map if none are found.
+    */
+   public Map getAttributes(String realId)
+   {
+      if (realId == null || realId.length() == 0) return new HashMap();
+      
+      Map map = new HashMap();
+      Set set = getAttributeKeys(realId);
+      if(set != null)
+      {
+         for (Iterator it = set.iterator(); it.hasNext();)
+         {
+            String key = (String) it.next();
+            Object value = getAttribute(realId, key);
+            map.put(key, value);
+         }
+      }
+      return map;
+   }
+
+   /**
+    * Gets the ids of all sessions in the underlying cache.
+    *
+    * @return Set containing all of the session ids of sessions in the cache
+    *         (with any jvmRoute removed) or <code>null</code> if there
+    *         are no sessions in the cache.
+    */
+   public Map getSessionIds() throws CacheException
+   {
+      Map result = new HashMap();
+      Set owners = proxy_.getChildrenNames(BUDDY_BACKUP_FQN);
+      if (owners != null)
+      {
+         for (Iterator it = owners.iterator(); it.hasNext();)
+         {
+            Object owner = it.next();
+            Set ids = proxy_.getChildrenNames(getWebappFqn(owner));
+            storeSessionOwners(ids, owner, result);
+         }
+      }
+      storeSessionOwners(proxy_.getChildrenNames(getWebappFqn()), null, result);
+
+      return result;
+   }
+
+   private void storeSessionOwners(Set ids, Object owner, Map map)
+   {
+      if (ids != null)
+      {
+         for (Iterator it = ids.iterator(); it.hasNext();)
+         {
+            map.put(it.next(), owner);
+         }
+      }
+   }
+
+   /**
+    * store the pojo instance in the cache. Note that this is for the aop cache.
+    * THe pojo needs to be "aspectized".
+    * 
+    * @param realId the session id with any jvmRoute removed
+    * @param key    the attribute key
+    * @param pojo
+    */
+   public Object setPojo(String realId, String key, Object pojo)
+   {
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("setPojo(): session id: " + realId + " key: " + key + 
+                    " object: " + pojo.toString());
+      }
+      // Construct the fqn.
+      Fqn fqn = getFieldFqn(realId, key);
+      try {
+         // Ignore any cache notifications that our own work generates 
+         SessionReplicationContext.startCacheActivity();
+         return proxy_.putObject(fqn, pojo);
+      } catch (CacheException e) {
+         throw new RuntimeException("JBossCacheService: exception occurred in cache setPojo ... ", e);
+      }
+      finally {
+         SessionReplicationContext.finishCacheActivity();
+      }
+   }
+
+   /**
+    * Remove pojo from the underlying cache store.
+    * @param realId the session id with any jvmRoute removed
+    * @param key    the attribute key
+    * @return pojo that just removed. Null if there none.
+    */
+   public Object removePojo(String realId, String key)
+   {
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("removePojo(): session id: " +realId + " key: " +key);
+      }
+      // Construct the fqn.
+      Fqn fqn = getFieldFqn(realId, key);
+      try {
+         // Ignore any cache notifications that our own work generates 
+         SessionReplicationContext.startCacheActivity();
+         return proxy_.removeObject(fqn);
+      } catch (CacheException e) {
+         throw new RuntimeException("JBossCacheService: exception occurred in cache removePojo ... ", e);
+      }
+      finally {
+         SessionReplicationContext.finishCacheActivity();
+      }
+   }
+
+   /**
+    * Remove all the pojos from the underlying cache store locally 
+    * without replication.
+    * 
+    * @param realId the session id with any jvmRoute removed
+    */
+   public void removePojosLocal(String realId)
+   {
+      if(log_.isDebugEnabled())
+      {
+         log_.debug("removePojoLocal(): session id: " +realId);
+      }
+      // Construct the fqn.
+      Fqn fqn = getAttributeFqn(realId);
+      try {
+         // Ignore any cache notifications that our own work generates 
+         SessionReplicationContext.startCacheActivity();
+         cacheWrapper_.evictSubtree(fqn);
+      }
+      finally {
+         SessionReplicationContext.finishCacheActivity();
+      }
+   }
+
+   /**
+    * Remove all the pojos from the underlying cache store locally 
+    * without replication.
+    * 
+    * @param realId the session id with any jvmRoute removed
+    */
+   public void removePojoLocal(String realId, String key)
+   {
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("removePojoLocal(): session id: " + realId + " key: " +key);
+      }
+      // Construct the fqn.
+      Fqn fqn = getFieldFqn(realId, key);
+      try {
+         // Ignore any cache notifications that our own work generates 
+         SessionReplicationContext.startCacheActivity();
+         cacheWrapper_.evictSubtree(fqn);
+      }
+      finally {
+         SessionReplicationContext.finishCacheActivity();
+      }
+   }
+   
+   public Set getPojoKeys(String realId)
+   {
+      Set keys = null;
+      Fqn fqn = getAttributeFqn(realId);
+      try
+      {
+         keys = proxy_.getChildrenNames(fqn);
+      }
+      catch (CacheException e)
+      {
+         log_.error("getPojoKeys(): Exception getting keys for session " + realId, e);
+      }
+      
+      return keys;      
+   }
+   
+
+   /**
+    *
+    * @param realId the session id with any jvmRoute removed
+    * @param key    the attribute key
+    * @return Pojo that is associated with the attribute
+    */
+   public Object getPojo(String realId, String key)
+   {
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("getPojo(): session id: " +realId + " key: " +key);
+      }
+      // Construct the fqn.
+      Fqn fqn = getFieldFqn(realId, key);
+      
+      try 
+      {
+         return proxy_.getObject(fqn);
+      } 
+      catch (CacheException e) 
+      {
+         throw new RuntimeException("JBossCacheService: exception occurred in cache getPojo ... ", e);
+      }
+   }
+
+   /**
+    * Recursively adds session as observer to the pojo graph. Assumes the 
+    * whole object graph has Subject "introduction" declared. If a portion
+    * of the graph isn't a Subject, the recursion does not continue below
+    * that part of the graph.
+    *  
+    * @param session  the session
+    * @param pojo     the pojo.  Can be <code>null</code>.
+    */
+   public void addObserver(Observer session, Object pojo)
+   {
+      addObserver(session, pojo, new HashSet());
+   }
+   
+   private void addObserver(Observer session, Object pojo, Set processed)
+   {
+      if ( pojo instanceof Collection )
+      {
+         Collection col = (Collection)pojo;
+         for (Iterator i = col.iterator(); i.hasNext();) {
+            // If not a managed pojo, will return anyway
+            addObserver(session, i.next(), processed);
+         }
+
+         return;
+      } 
+      else if (pojo instanceof Map)
+      {
+         for (Iterator i = ((Map)pojo).entrySet().iterator(); i.hasNext();) 
+         {
+            Map.Entry entry = (Map.Entry) i.next();
+
+            // Walk thru key and value
+            addObserver(session, entry.getKey(), processed);
+            addObserver(session, entry.getValue(), processed);
+         }
+
+         return;
+      }
+
+      if(! (pojo instanceof Subject) )
+      {
+         return;  // No need to add observer since it is primitive.
+      }
+
+      Subject subject = (Subject)pojo;
+      subject.addObserver(session);
+      
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("addObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
+      }
+      
+      // Examine each field of the type and its superclasses to see if
+      // we need to add the observer to the pojo held by that field
+      // Traverse recursively
+      
+      // First identify and cache the names of all the class' 
+      // non-immediate fields 
+      Class type = pojo.getClass();      
+      Set complexFields = (Set) typeMap.get(type);
+      if (complexFields == null)
+      {
+         complexFields = Util.parseComplexFields(type);
+         typeMap.put(type, complexFields);
+      }
+      
+      if (complexFields.size() == 0)
+         return;
+
+      // Store a ref to the pojo to avoid cyclic additions
+      processed.add(pojo);
+      
+      for (Iterator iter = complexFields.iterator(); iter.hasNext();)
+      {
+         String fieldName = (String) iter.next();
+         Class curType = type;
+         while (curType != null)
+         {
+            try
+            {
+               Field field = curType.getDeclaredField(fieldName);
+               boolean accessible = field.isAccessible();
+               Object value = null;
+               try 
+               {
+                  field.setAccessible(true);
+                  
+                  value=field.get(pojo);
+                  // Continue recursively unless we've already handled this value
+                  if (value != null && !processed.contains(value))
+                     addObserver(session, value, processed);
+                  break;
+               }
+               catch(IllegalAccessException e) 
+               {
+                  throw new RuntimeException("field access failed", e);
+               }
+               finally
+               {
+                  field.setAccessible(accessible);
+               }
+            }
+            catch (NoSuchFieldException e)
+            {
+               // Check if the field is declared in a superclass
+               curType = curType.getSuperclass();
+               if (curType == null)
+                  throw new RuntimeException("Field "  + fieldName + 
+                        " does not exist", e);
+            }
+         }
+      }
+   }
+
+   /**
+    * Recursively removes session as observer to the pojo graph. Assumes the 
+    * whole object graph has Subject "introduction" declared. If a portion
+    * of the graph isn't a Subject, the recursion does not continue below
+    * that part of the graph.
+    *  
+    * @param session  the session
+    * @param pojo the pojo to stop observing.  Can be <code>null</code>.
+    */
+   public void removeObserver(Observer session, Object pojo)
+   {
+      removeObserver(session, pojo, new HashSet());
+   }
+   
+   private void removeObserver(Observer session, Object pojo, Set stack)
+   {
+      if ( pojo instanceof Collection )
+      {
+         Collection col = (Collection)pojo;
+         for (Iterator i = col.iterator(); i.hasNext();) {
+            Object obj = i.next();
+            // If not a managed pojo, will return anyway
+            removeObserver(session, obj, stack);
+         }
+   
+         return;
+      } 
+      else if (pojo instanceof Map)
+      {
+         Map map = (Map)pojo;
+         for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+            Object key = i.next();
+            Object value = map.get(key);
+   
+            // Walk thru key and value
+            removeObserver(session, key, stack);
+            removeObserver(session, value, stack);
+         }
+   
+         return;
+      }
+      // BRIAN 3/14 changed this from checking Advised to checking Subject
+      // since that is what we cast to below
+      if(! (pojo instanceof Subject) )
+      {
+         return;  // No need to add observer since it is primitive.
+      }
+
+      Subject subject = (Subject)pojo;
+      subject.removeObserver(session);
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("removeObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
+      }
+      
+      // Examine each field of the type and its superclasses to see if
+      // we need to remove the observer from the pojo held by that field
+      // Traverse recursively
+      
+      // First identify and cache the names of all the class' 
+      // non-immediate fields 
+      Class type = pojo.getClass();
+      Set complexFields = (Set) typeMap.get(type);
+      if (complexFields == null)
+      {
+         complexFields = Util.parseComplexFields(type);
+         typeMap.put(type, complexFields);
+      }
+
+      if (complexFields.size() == 0)
+         return;
+      
+      // Store a ref to the pojo to avoid cyclic removals
+      stack.add(pojo);
+      
+      for (Iterator iter = complexFields.iterator(); iter.hasNext();)
+      {
+         String fieldName = (String) iter.next();
+         Class curType = type;
+         while (curType != null)
+         {
+            try
+            {
+               Field field = curType.getDeclaredField(fieldName);
+               boolean accessible = field.isAccessible();
+               Object value = null;
+               try 
+               {
+                  field.setAccessible(true);
+                  
+                  value=field.get(pojo);
+                  // Continue recursively unless we've already handled this value
+                  if (value != null && !stack.contains(value))
+                     removeObserver(session, value, stack);
+                  break;
+               }
+               catch(IllegalAccessException e) 
+               {
+                  throw new RuntimeException("field access failed", e);
+               }
+               finally
+               {
+                  field.setAccessible(accessible);
+               }
+            }
+            catch (NoSuchFieldException e)
+            {
+               // Check if the field is declared in a superclass
+               curType = curType.getSuperclass();
+               if (curType == null)
+                  throw new RuntimeException("Field "  + fieldName + 
+                        " does not exist", e);
+            }
+         }
+      }
+   }
+
+   private Fqn getFieldFqn(String id, String key)
+   {
+      // /SESSION/id/ATTR/key
+      // Guard against string with delimiter.
+      List list = new ArrayList(6);
+      list.add(SESSION);
+      list.add(hostName_);
+      list.add(webAppPath_);
+      list.add(id);
+      list.add(ATTRIBUTE);
+      breakKeys(key, list);
+      return new Fqn(list);
+   }
+
+   private void breakKeys(String key, List list)
+   {
+      StringTokenizer token = new StringTokenizer(key, FQN_DELIMITER);
+      while(token.hasMoreTokens())
+      {
+         list.add(token.nextToken());
+      }
+   }
+
+   private Fqn getWebappFqn()
+   {
+      // /SESSION/hostname/webAppPath
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+      return new Fqn(objs);
+   }
+   
+   private Fqn getWebappFqn(Object dataOwner)
+   {
+      if (dataOwner == null)
+         return getWebappFqn();
+      
+      // /SESSION/hostname/webAppPath
+      Object[] objs = new Object[]{BUDDY_BACKUP, dataOwner, SESSION, hostName_, webAppPath_};
+      return new Fqn(objs);
+   }
+   
+   private Fqn getSessionFqn(String id)
+   {
+      // /SESSION/hostname/webAppPath/id
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_, id};
+      return new Fqn(objs);
+   }
+
+   private Fqn getSessionFqn(String id, String dataOwner)
+   {
+      // /_BUDDY_BACKUP_/dataOwner/SESSION/hostname/webAppPath/id
+      Object[] objs = new Object[]{BUDDY_BACKUP, dataOwner, SESSION, hostName_, webAppPath_, id};
+      return new Fqn(objs);
+   }
+
+   private Fqn getAttributeFqn(String id)
+   {
+      // /SESSION/hostName/webAppPath/id/ATTR
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_, id, ATTRIBUTE};
+      return new Fqn(objs);
+   }
+
+   private Object getMarshalledValue(Object value)
+   {
+      // JBAS-2920.  For now, continue using MarshalledValue, as 
+      // it allows lazy deserialization of the attribute on remote nodes
+      // For Branch_4_0 this is what we have to do anyway for backwards
+      // compatibility. For HEAD we'll follow suit for now.
+      // TODO consider only using MV for complex objects (i.e. not primitives)
+      // and Strings longer than X.
+      
+//      if (useTreeCacheMarshalling_)
+//      {
+//         return value;
+//      }
+//      else
+//      {
+         try
+         {
+            MarshalledValue mv = new MarshalledValue(value);
+            if (log_.isTraceEnabled())
+            {
+               log_.trace("marshalled object to size " + mv.size() + " bytes");
+            }
+            return mv;
+         }
+         catch (IOException e)
+         {
+            log_.error("IOException occurred marshalling value ", e);
+            return null;
+         }
+//      }
+      }
+
+   private Object getUnMarshalledValue(Object mv)
+   {
+      // JBAS-2920.  For now, continue using MarshalledValue, as 
+      // it allows lazy deserialization of the attribute on remote nodes
+      // For Branch_4_0 this is what we have to do anyway for backwards
+      // compatibility. For HEAD we'll follow suit for now.
+//      if (useTreeCacheMarshalling_)
+//      {
+//         return mv;
+//      }
+//      else
+//      {
+         if (mv == null) return null;
+         // Swap in/out the tcl for this web app. Needed only for un marshalling.
+         ClassLoader prevTCL = Thread.currentThread().getContextClassLoader();
+         Thread.currentThread().setContextClassLoader(manager_.getWebappClassLoader());
+         try
+         {
+            return ((MarshalledValue) mv).get();
+         }
+         catch (IOException e)
+         {
+            log_.error("IOException occurred unmarshalling value ", e);
+            return null;
+         }
+         catch (ClassNotFoundException e)
+         {
+            log_.error("ClassNotFoundException occurred unmarshalling value ", e);
+            return null;
+         }
+         finally
+         {
+            Thread.currentThread().setContextClassLoader(prevTCL);
+         }
+//      }
+      }
+
+   private byte[] externalizeSession(ClusteredSession session)
+   {      
+      try
+      {
+         // Write the contents of session to a byte array and store that
+         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+         // Use MarshalledValueOutputStream instead of superclass ObjectOutputStream
+         // or else there are problems finding classes with scoped loaders
+         MarshalledValueOutputStream oos = new MarshalledValueOutputStream(baos);
+         session.writeExternal(oos);
+         oos.close(); // flushes bytes to baos
+         
+         byte[] bytes = baos.toByteArray();
+         
+         if (log_.isTraceEnabled())
+         {
+            log_.trace("marshalled object to size " + bytes.length + " bytes");
+         }
+
+         return bytes;
+      }
+      catch (Exception e)
+      {
+         log_.error("externalizeSession(): exception occurred externalizing session " + session, e);
+         return null;
+      }
+      
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,280 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.aop.PojoCacheMBean;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.lock.TimeoutException;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class JBossCacheWrapper 
+{
+   static final Option GRAVITATE_OPTION = new Option();
+
+   static
+   {
+      GRAVITATE_OPTION.setForceDataGravitation(true);
+   }
+         
+   private static final int RETRY = 3;
+   private PojoCacheMBean proxy_;
+
+   JBossCacheWrapper(PojoCacheMBean cache)
+   {
+      proxy_ = cache;
+   }
+
+   /**
+    * Wrapper to embed retyr logic.
+    *
+    * @param fqn
+    * @param id
+    * @return
+    */
+   Object get(Fqn fqn, String id)
+   {
+      return get(fqn, id, false);
+   }
+
+   /**
+    * Wrapper to embed retyr logic.
+    *
+    * @param fqn
+    * @param id
+    * @return
+    */
+   Object get(Fqn fqn, String id, boolean gravitate)
+   {
+      Exception ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            Object value = null;
+            if (gravitate)
+               value = proxy_.get(fqn, id, GRAVITATE_OPTION);
+            else
+               value = proxy_.get(fqn, id);
+            return value;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+         catch (Exception e)
+         {
+            if (e instanceof RuntimeException)
+               throw (RuntimeException) e;
+            throw new RuntimeException("JBossCacheService: exception occurred in cache get ... ", e);
+         }
+      }
+      throw new RuntimeException("JBossCacheService: exception occurred in cache get after retry ... ", ex);
+   }
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    * @param id
+    * @param value
+    * @return
+    */
+   void put(Fqn fqn, String id, Object value)
+   {
+      Exception ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            proxy_.put(fqn, id, value);
+            return;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException("JBossCacheService: exception occurred in cache put ... ", e);
+         }
+      }
+      throw new RuntimeException("JBossCacheService: exception occurred in cache put after retry ... ", ex);
+   }
+
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    * @param map
+    */
+   void put(Fqn fqn, Map map)
+   {
+      Exception ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            proxy_.put(fqn, map);
+            return;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException("JBossCacheService: exception occurred in cache put ... ", e);
+         }
+      }
+      throw new RuntimeException("JBossCacheService: exception occurred in cache put after retry ... ", ex);
+   }
+
+   /**
+    * Wrapper to embed retyr logic.
+    *
+    * @param fqn
+    * @param id
+    * @return
+    */
+   Object remove(Fqn fqn, String id)
+   {
+      Exception ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            return proxy_.remove(fqn, id);
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException("JBossCacheService: exception occurred in cache remove ... ", e);
+         }
+      }
+      throw new RuntimeException("JBossCacheService: exception occurred in cache remove after retry ... ", ex);
+   }
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    */
+   void remove(Fqn fqn)
+   {
+      Exception ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            proxy_.remove(fqn);
+            return;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException("JBossCacheService: exception occurred in cache remove ... ", e);
+         }
+      }
+      throw new RuntimeException("JBossCacheService: exception occurred in cache remove after retry ... ", ex);
+   }
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    */
+   void evict(Fqn fqn)
+   {
+      Exception ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            proxy_.evict(fqn);
+            return;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException("JBossCacheService: exception occurred in cache evict ... ", e);
+         }
+      }
+      throw new RuntimeException("JBossCacheService: exception occurred in cache evict after retry ... ", ex);
+   }
+   
+   void evictSubtree(Fqn fqn)
+   {
+      
+      Exception ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            // Evict the node itself first, since if it stores a Pojo
+            // that will do everything
+            proxy_.evict(fqn);
+            
+            // next do a depth first removal; this ensure all nodes
+            // are removed, not just their data map
+            Set children = proxy_.getChildrenNames(fqn);
+            if (children != null)
+            {
+               for (Iterator it = children.iterator(); it.hasNext(); )
+               {
+                  Fqn child = new Fqn(fqn, it.next());
+                  proxy_.evict(child);
+               }
+               
+               proxy_.evict(fqn);
+            }
+            return;
+            
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException("JBossCacheService: exception occurred in cache evict ... ", e);
+         }
+      }
+      throw new RuntimeException("JBossCacheService: exception occurred in cache evictSubtree after retry ... ", ex);
+      
+      
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,899 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+import org.apache.catalina.*;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.web.tomcat.statistics.ReplicationStatistics;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Base abstract implementation of Tomcat manager without the concept of
+ * session operations, e.g., add, remove, etc.
+ *
+ * @author Ben Wang
+ * @version $Revision: 57206 $
+ */
+public abstract class JBossManager
+   implements AbstractJBossManager, Lifecycle,
+      JBossManagerMBean, PropertyChangeListener
+{
+
+   // -- Constants ----------------------------------------
+   /**
+    * Informational name for this Catalina component
+    */
+   private static final String info_ = "JBossManager/1.0";
+
+   // -- Class attributes ---------------------------------
+   protected ReplicationStatistics stats_ = new ReplicationStatistics();
+
+   /**
+    * Policy to determine if a session is dirty
+    */
+   protected int invalidateSessionPolicy_ = WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET;
+   /**
+    * Replication granulairty.
+    */
+   protected int replicationGranularity_ = WebMetaData.REPLICATION_GRANULARITY_SESSION;
+   /**
+    * The lifecycle_ event support for this component.
+    */
+   protected LifecycleSupport lifecycle_ = new LifecycleSupport(this);
+   /**
+    * Has this component been started_ yet?
+    */
+   protected boolean started_ = false;
+   /**
+    * The objectname this Manager is associated with
+    */
+   protected ObjectName objectName_;
+   /**
+    * The Log-object for this class
+    */
+   protected Logger log_ = Logger.getLogger(this.getClass().getName());
+   /**
+    * The Container with which this Manager is associated.
+    */
+   protected Container container_;
+  /**
+   /**
+    * 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_ = true;
+   /**
+    * 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;
+
+   // Maximum of ative sessions allowed. -1 is unlimited.
+   protected int maxActive_ = -1;
+
+   // Number of sessions created by this manager
+   protected int createdCounter_ = 0;
+
+   // number of rejected sessions because the number active sessions exceeds maxActive
+   protected int rejectedCounter_ = 0;
+
+   // Number of active sessions
+   protected int activeCounter_ = 0;
+
+   // Maximum number of active sessions seen so far
+   protected int maxActiveCounter_ = 0;
+
+   // number of expired session ids. Not sure what exactly does it mean in our clustered case.
+   protected int expiredCounter_ = 0;
+
+   protected long timeSinceLastReset_ = 0;
+
+   // Cumulative time spent in backgroundProcess
+   protected long processingTime_ = 0;
+   
+   /**
+    * Map<String,ClusteredSession>. Store the local sessions.
+    */
+   protected final Map sessions_ = new ConcurrentHashMap();
+
+   /**
+    * If set to true, it will not replicate the access time stamp unless attributes are dirty.
+    */
+   protected boolean useLocalCache_ = false;
+
+   /**
+    * The property change support for this component.
+    */
+   protected PropertyChangeSupport support_ = new PropertyChangeSupport(this);
+
+   protected SessionIDGenerator sessionIDGenerator_;
+
+   protected String jvmRoute_;
+
+   // TODO Need a string manager to handle exception localization
+
+   public JBossManager()
+   {
+      sessionIDGenerator_ = SessionIDGenerator.getInstance();
+
+   }
+
+   public void init(String name, WebMetaData webMetaData, boolean useJK, boolean useLocalCache)
+           throws ClusteringNotSupportedException
+   {
+      replicationGranularity_ = webMetaData.getReplicationGranularity();
+      invalidateSessionPolicy_ = webMetaData.getInvalidateSessionPolicy();
+      useLocalCache_ = useLocalCache;
+      log_.info("init(): replicationGranularity_ is " + replicationGranularity_ +
+         " and invaldateSessionPolicy is " + invalidateSessionPolicy_);
+
+      try
+      {
+         // Give this manager a name
+         objectName_ = new ObjectName("jboss.web:service=ClusterManager,WebModule=" + name);
+      }
+      catch (Throwable e)
+      {
+         log_.error("Could not create ObjectName", e);
+         throw new ClusteringNotSupportedException(e.toString());
+      }
+   }
+
+   public int getInvalidateSessionPolicy()
+   {
+      return this.invalidateSessionPolicy_;
+   }
+
+   /**
+    * 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()
+   {
+      if (jvmRoute_ == null)
+      {
+         Engine e = getEngine();
+         jvmRoute_= (e == null ? null : e.getJvmRoute());
+      }
+      return jvmRoute_;
+   }
+
+   /**
+    * Get a new session-id from the distributed store
+    *
+    * @return new session-id
+    */
+   protected String getNextId()
+   {
+      return sessionIDGenerator_.getSessionId();
+   }
+
+   /**
+    * Gets the JMX <code>ObjectName</code> under
+    * which our <code>TreeCache</code> is registered. 
+    */
+   public ObjectName getObjectName()
+   {
+      return objectName_;
+   }
+
+   public boolean isUseLocalCache()
+   {
+      return useLocalCache_;
+   }
+
+   /**
+    * Sets a new cookie for the given session id and response
+    *
+    * @param sessionId The session id
+    */
+   public void setSessionCookie(String sessionId)
+   {
+      HttpServletResponse response = SessionReplicationContext.getOriginalResponse();
+      setNewSessionCookie(sessionId, response);
+   }
+
+   public void setNewSessionCookie(String sessionId, HttpServletResponse response)
+   {
+      if (response != null)
+      {
+         Context context = (Context) container_;
+         Connector connector = ((Response)response).getConnector();
+         if (context.getCookies())
+         {
+            // set a new session cookie
+            Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME, sessionId);
+            if (log_.isDebugEnabled())
+            {
+               log_.debug("Setting cookie with session id:" + sessionId + " & name:" + Globals.SESSION_COOKIE_NAME);
+            }
+
+            String contextPath = null;
+            if (!connector.getEmptySessionPath() && (context != null)) {
+                contextPath = context.getEncodedPath();
+            }
+
+            if ((contextPath != null) && (contextPath.length() > 0)) {
+                newCookie.setPath(contextPath);
+            } else {
+                newCookie.setPath("/");
+            }
+
+            if (connector.getSecure()) {
+                newCookie.setSecure(true);
+            }
+
+            response.addCookie(newCookie);
+         }
+      }
+   }
+
+   // JBossManagerMBean-methods -------------------------------------
+
+   // A better property name for the MBean API
+   public int getMaxActiveAllowed()
+   {
+      return getMaxActive();
+   }
+   
+   // A better property name for the MBean API
+   public void setMaxActiveAllowed(int maxActive)
+   {
+      setMaxActive(maxActive);
+   }
+   
+   public long getMaxActiveSessionCount()
+   {
+      return this.maxActiveCounter_;
+   }
+
+   public ReplicationStatistics getReplicationStatistics()
+   {
+      return stats_;
+   }
+
+   public void resetStats()
+   {
+      stats_.resetStats();
+      activeCounter_ = 0;
+      maxActiveCounter_ = 0;
+      rejectedCounter_ = 0;
+      createdCounter_ = 0;
+      expiredCounter_ = 0;
+      processingTime_ = 0;
+      timeSinceLastReset_ = System.currentTimeMillis();
+   }
+
+   public long timeInSecondsSinceLastReset()
+   {
+      return (System.currentTimeMillis() - timeSinceLastReset_) / (1000L);
+   }
+
+   public long getActiveSessionCount()
+   {
+      return getActiveSessions();
+   }
+
+   public long getCreatedSessionCount()
+   {
+      return createdCounter_;
+   }
+
+   public long getExpiredSessionCount()
+   {
+      return expiredCounter_;
+   }
+
+   public long getRejectedSessionCount()
+   {
+      return rejectedCounter_;
+   }
+
+   public int getSessionMaxAliveTime()
+   {
+       return 0;
+   }
+
+   public void setSessionMaxAliveTime(int sessionMaxAliveTime)
+   {
+   }
+
+   public int getSessionAverageAliveTime()
+   {
+       return 0;
+   }
+
+   public void setSessionAverageAliveTime(int sessionAverageAliveTime)
+   {
+   }
+
+   public String reportReplicationStatistics()
+   {
+      StringBuffer tmp = new StringBuffer();
+      HashMap copy = new HashMap(stats_.getStats());
+      Iterator iter = copy.entrySet().iterator();
+      tmp.append("<table><tr>");
+      tmp.append("<th>sessionID</th>");
+      tmp.append("<th>replicationCount</th>");
+      tmp.append("<th>minPassivationTime</th>");
+      tmp.append("<th>maxPassivationTime</th>");
+      tmp.append("<th>totalPassivationTime</th>");
+      tmp.append("<th>minReplicationTime</th>");
+      tmp.append("<th>maxReplicationTime</th>");
+      tmp.append("<th>totalReplicationlTime</th>");
+      tmp.append("<th>loadCount</th>");
+      tmp.append("<th>minLoadTime</th>");
+      tmp.append("<th>maxLoadTime</th>");
+      tmp.append("<th>totalLoadTime</th>");
+      while (iter.hasNext())
+      {
+         Map.Entry entry = (Map.Entry) iter.next();
+         ReplicationStatistics.TimeStatistic stat = (ReplicationStatistics.TimeStatistic) entry.getValue();
+         if (stat != null)
+         {
+            tmp.append("<tr><td>");
+            tmp.append(entry.getKey());
+            tmp.append("</td><td>");
+            tmp.append(stat.replicationCount);
+            tmp.append("</td><td>");
+            tmp.append(stat.minPassivationTime);
+            tmp.append("</td><td>");
+            tmp.append(stat.maxPassivationTime);
+            tmp.append("</td><td>");
+            tmp.append(stat.totalPassivationTime);
+            tmp.append("</td><td>");
+            tmp.append(stat.minReplicationTime);
+            tmp.append("</td><td>");
+            tmp.append(stat.maxReplicationTime);
+            tmp.append("</td><td>");
+            tmp.append(stat.totalReplicationlTime);
+            tmp.append("</td><td>");
+            tmp.append(stat.loadCount);
+            tmp.append("</td><td>");
+            tmp.append(stat.minLoadTime);
+            tmp.append("</td><td>");
+            tmp.append(stat.maxLoadTime);
+            tmp.append("</td><td>");
+            tmp.append(stat.totalLoadlTime);
+            tmp.append("</td></tr>");
+         }
+      }
+      tmp.append("</table>");
+      copy.clear();
+      return tmp.toString();
+
+   }
+   
+   public String reportReplicationStatisticsCSV()
+   {
+      StringBuffer tmp = createCSVHeader();
+      HashMap copy = new HashMap(stats_.getStats());
+      Iterator iter = copy.entrySet().iterator();
+      while (iter.hasNext())
+      {
+         Map.Entry entry = (Map.Entry) iter.next();
+         ReplicationStatistics.TimeStatistic stat = (ReplicationStatistics.TimeStatistic) entry.getValue();
+         if (stat != null)
+         {
+            tmp.append("\n");
+            tmp.append(entry.getKey());
+            tmp.append(",");
+            tmp.append(stat.replicationCount);
+            tmp.append(",");
+            tmp.append(stat.minPassivationTime);
+            tmp.append(",");
+            tmp.append(stat.maxPassivationTime);
+            tmp.append(",");
+            tmp.append(stat.totalPassivationTime);
+            tmp.append(",");
+            tmp.append(stat.minReplicationTime);
+            tmp.append(",");
+            tmp.append(stat.maxReplicationTime);
+            tmp.append(",");
+            tmp.append(stat.totalReplicationlTime);
+            tmp.append(",");
+            tmp.append(stat.loadCount);
+            tmp.append(",");
+            tmp.append(stat.minLoadTime);
+            tmp.append(",");
+            tmp.append(stat.maxLoadTime);
+            tmp.append(",");
+            tmp.append(stat.totalLoadlTime);
+         }
+      }
+      copy.clear();
+      return tmp.toString();
+
+   }
+   
+   public String reportReplicationStatisticsCSV(String sessionId)
+   {
+      StringBuffer tmp = createCSVHeader();
+      Map stats = stats_.getStats();
+      ReplicationStatistics.TimeStatistic stat = 
+         (ReplicationStatistics.TimeStatistic) stats.get(sessionId);
+      if (stat != null)
+      {
+         tmp.append("\n");
+         tmp.append(sessionId);
+         tmp.append(",");
+         tmp.append(stat.replicationCount);
+         tmp.append(",");
+         tmp.append(stat.minPassivationTime);
+         tmp.append(",");
+         tmp.append(stat.maxPassivationTime);
+         tmp.append(",");
+         tmp.append(stat.totalPassivationTime);
+         tmp.append(",");
+         tmp.append(stat.minReplicationTime);
+         tmp.append(",");
+         tmp.append(stat.maxReplicationTime);
+         tmp.append(",");
+         tmp.append(stat.totalReplicationlTime);
+         tmp.append(",");
+         tmp.append(stat.loadCount);
+         tmp.append(",");
+         tmp.append(stat.minLoadTime);
+         tmp.append(",");
+         tmp.append(stat.maxLoadTime);
+         tmp.append(",");
+         tmp.append(stat.totalLoadlTime);
+      }
+      return tmp.toString();
+   }
+   
+   private StringBuffer createCSVHeader()
+   {
+      StringBuffer tmp = new StringBuffer();
+      tmp.append("sessionID,");
+      tmp.append("replicationCount,");
+      tmp.append("minPassivationTime,");
+      tmp.append("maxPassivationTime,");
+      tmp.append("totalPassivationTime,");
+      tmp.append("minReplicationTime,");
+      tmp.append("maxReplicationTime,");
+      tmp.append("totalReplicationlTime,");
+      tmp.append("loadCount,");
+      tmp.append("minLoadTime,");
+      tmp.append("maxLoadTime,");
+      tmp.append("totalLoadTime");
+      
+      return tmp;
+   }
+
+   // Lifecycle-methods -------------------------------------
+
+   public void addLifecycleListener(LifecycleListener listener)
+   {
+      lifecycle_.addLifecycleListener(listener);
+   }
+
+   public LifecycleListener[] findLifecycleListeners()
+   {
+      return lifecycle_.findLifecycleListeners();
+   }
+
+   public void removeLifecycleListener(LifecycleListener listener)
+   {
+      lifecycle_.removeLifecycleListener(listener);
+   }
+
+   /**
+    * Start this Manager
+    *
+    * @throws org.apache.catalina.LifecycleException
+    *
+    */
+   public void start() throws LifecycleException
+   {
+      startManager();
+   }
+
+   /**
+    * Stop this Manager
+    *
+    * @throws org.apache.catalina.LifecycleException
+    *
+    */
+   public void stop() throws LifecycleException
+   {
+      resetStats();
+      stopManager();
+   }
+
+   /**
+    * 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.
+    *
+    * @throws IllegalStateException if this component has already been
+    *                               started_
+    * @throws org.apache.catalina.LifecycleException
+    *                               if this component detects a fatal error
+    *                               that prevents this component from being used
+    */
+   protected void startManager() throws LifecycleException
+   {
+      log_.info("Starting JBossManager");
+
+      // Validate and update our current component state
+      if (started_)
+         throw new LifecycleException
+            ("JBossManager alreadyStarted");
+      lifecycle_.fireLifecycleEvent(START_EVENT, null);
+      started_ = true;
+
+      // register ClusterManagerMBean to the MBeanServer
+      try
+      {
+         MBeanServer server = MBeanServerLocator.locateJBoss();
+         server.registerMBean(this, objectName_);
+      }
+      catch (Exception e)
+      {
+         log_.error("Could not register ClusterManagerMBean to MBeanServer", 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.
+    *
+    * @throws IllegalStateException if this component has not been started_
+    * @throws org.apache.catalina.LifecycleException
+    *                               if this component detects a fatal error
+    *                               that needs to be reported
+    */
+   protected void stopManager() throws LifecycleException
+   {
+      log_.info("Stopping JBossManager");
+
+      // Validate and update our current component state
+      if (!started_)
+         throw new LifecycleException
+            ("JBossManager notStarted");
+      lifecycle_.fireLifecycleEvent(STOP_EVENT, null);
+      started_ = false;
+
+      // unregister ClusterManagerMBean from the MBeanServer
+      try
+      {
+         MBeanServer server = MBeanServerLocator.locateJBoss();
+         server.unregisterMBean(objectName_);
+      }
+      catch (Exception e)
+      {
+         log_.error("Could not unregister ClusterManagerMBean from MBeanServer", e);
+      }
+   }
+
+   // Manager-methods -------------------------------------
+   public Container getContainer()
+   {
+      return container_;
+   }
+
+   public void setContainer(Container container)
+   {
+
+      // De-register from the old Container (if any)
+      if ((this.container_ != null) && (this.container_ instanceof Context))
+         this.container_.removePropertyChangeListener(this);
+
+      // Default processing provided by our superclass
+      this.container_ = container;
+
+      // Register with the new Container (if any)
+      if ((this.container_ != null) && (this.container_ instanceof Context))
+      {
+         setMaxInactiveInterval
+            (((Context) this.container_).getSessionTimeout() * 60);
+         this.container_.addPropertyChangeListener(this);
+      }
+   }
+
+   public boolean getDistributable()
+   {
+      return distributable_;
+   }
+
+   public void setDistributable(boolean distributable)
+   {
+      this.distributable_ = distributable;
+   }
+
+   public String getInfo()
+   {
+      return info_;
+   }
+
+   public int getMaxInactiveInterval()
+   {
+      return maxInactiveInterval_;
+   }
+
+   public void setMaxInactiveInterval(int interval)
+   {
+      this.maxInactiveInterval_ = interval;
+   }
+
+   public int getSessionIdLength()
+   {
+      return sessionIdLength_;
+   }
+
+   public void setSessionIdLength(int idLength)
+   {
+      this.sessionIdLength_ = idLength;
+   }
+
+   public int getSessionCounter()
+   {
+      return createdCounter_;
+   }
+
+   public void setSessionCounter(int sessionCounter)
+   {
+      this.createdCounter_ = sessionCounter;
+   }
+   
+   public int getMaxActive()
+   {
+      return maxActive_;
+   }
+
+   public void setMaxActive(int maxActive)
+   {
+      this.maxActive_ = maxActive;
+   }
+
+   public int getExpiredSessions()
+   {
+      return expiredCounter_;
+   }
+
+   public void setExpiredSessions(int expiredSessions)
+   {
+      this.expiredCounter_ = expiredSessions;
+   }
+
+   public int getRejectedSessions()
+   {
+      return rejectedCounter_;
+   }
+
+   public void setRejectedSessions(int rejectedSessions)
+   {
+      this.rejectedCounter_ = rejectedSessions;
+   }
+   
+   public long getProcessingTime()
+   {
+      return this.processingTime_;
+   }
+
+   public void addPropertyChangeListener(PropertyChangeListener listener)
+   {
+      support_.addPropertyChangeListener(listener);
+   }
+
+   /**
+    * Remove the active session locally from the manager without replicating to the cluster. This can be
+    * useful when the session is exipred, for example, where there is not need to propagate the expiration.
+    *
+    * @param session
+    */
+   public abstract void removeLocal(Session session);
+
+   /**
+    * Store the modified session.
+    *
+    * @param session
+    */
+   public abstract boolean storeSession(Session session);
+
+   public int getActiveSessions()
+   {
+      return activeCounter_;
+   }
+
+/*
+   public void add(Session session)
+   {
+      //To change body of implemented methods use File | Settings | File Templates.
+   }
+
+   public Session createEmptySession()
+   {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+   }
+
+   public Session createSession()
+   {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+   }
+
+   public Session findSession(String id) throws IOException
+   {
+      return null;  //To change body of implemented methods use File | Settings | File Templates.
+   }
+
+   public Session[] findSessions()
+   {
+      return new Session[0];  //To change body of implemented methods use File | Settings | File Templates.
+   }
+
+   public void remove(Session session)
+   {
+      //To change body of implemented methods use File | Settings | File Templates.
+   }
+*/
+
+   public void load() throws ClassNotFoundException, IOException
+   {
+      // TODO. Implement persistence layer.
+      throw new RuntimeException("JBossManager.load(): Method not implemented.");
+   }
+
+   public void removePropertyChangeListener(PropertyChangeListener listener)
+   {
+      support_.removePropertyChangeListener(listener);
+   }
+
+   public void unload() throws IOException
+   {
+      // TODO. Implement persistence layer.
+      throw new RuntimeException("JBossManager.load(): Method not implemented.");
+   }
+
+
+   public void backgroundProcess()
+   {
+      // Called from Catalina StandardEngine for every 60 seconds.
+      
+      long start = System.currentTimeMillis();
+      
+      processExpires();
+      
+      long elapsed = System.currentTimeMillis() - start;
+      
+      processingTime_ += elapsed;
+   }
+
+   /**
+    * Go through all sessions and look if they have expired
+    */
+   protected void processExpires()
+   {
+      // What's the time?
+//      long timeNow = System.currentTimeMillis();
+
+      // Get all sessions
+      Session sessions[] = findSessions();
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("Looking for sessions that have expired ...");
+      }
+
+      for (int i = 0; i < sessions.length; ++i)
+      {
+         ClusteredSession session = (ClusteredSession) sessions[i];
+
+         // We only look at valid sessions. This will remove session if not valid already.
+         if (!session.isValid())
+         {
+            continue;
+         }
+
+         /* I don't think it is right to check idle time based on lastAccessedTime since it may
+         // remove some request that is currently in progress!!!
+         // How long are they allowed to be idle?
+         int maxInactiveInterval = session.getMaxInactiveInterval();
+
+         // Negative values = never expire
+         if( maxInactiveInterval < 0 )
+         {
+            continue;
+         }
+
+         // How long has this session been idle?
+         int timeIdle =
+            (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+
+         // Too long?
+         if( timeIdle >= maxInactiveInterval )
+         {
+            try
+            {
+               log_.debug("Session with id = " + session.getId() + " has expired on local node");
+               remove(session);
+            }
+            catch(Throwable t)
+            {
+               log_.error("Problems while expiring session with id = " + session.getId(), t);
+            }
+         }
+         */
+      }
+   }
+
+   public void propertyChange(PropertyChangeEvent evt)
+   {
+      // TODO Need to handle it here.
+   }
+
+   /**
+    * Find in-memory sessions, if any.
+    * @return local session found. Sessions of size 0, if not found.
+    */
+   abstract public ClusteredSession[] findLocalSessions();
+
+   /**
+    * Find in-memory sessions, if any.
+    * @param realId the Session id without JvmRoute tag.
+    * @return local session found. Null if not found.
+    */
+   abstract public ClusteredSession findLocalSession(String realId);
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,159 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.web.tomcat.statistics.ReplicationStatistics;
+
+/**
+ * The MBean-interface for the JBossManager
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * 
+ * @version $Revision: 57206 $
+ */
+public interface JBossManagerMBean
+{
+   /**
+    * Gets the replication statistics for the sessions managed
+    * by this manager.
+    * 
+    * @return the statistics
+    */
+   ReplicationStatistics getReplicationStatistics();
+
+   /**
+    * Resets all statistics.
+    */
+   void resetStats();
+
+   /**
+    * Gets the elapsed time since this manager was instantiated or the 
+    * last call to resetStats()
+    */
+   long timeInSecondsSinceLastReset();
+
+   /**
+    * Gets the number of sessions active on this node.  Does not include
+    * replicated sessions that have not been accessed on this node.
+    */
+   long getActiveSessionCount();
+
+   /**
+    * Gets the number of times session creation has failed because the
+    * number of active sessions exceeds 
+    * {@link #getMaxActiveAllowed() maxActiveAllowed}
+    */
+   long getRejectedSessionCount();
+
+   /**
+    * Gets the number of sessions created on this node. Does not include
+    * sessions initially created on other nodes, even if those sessions
+    * were accessed on this node.
+    */
+   long getCreatedSessionCount();
+
+   /**
+    * Gets the number of sessions that have been expired on this node.
+    */
+   long getExpiredSessionCount();
+   
+   /**
+    * Gets the highest number of sessions concurrently active on this node.   
+    * Does not include replicated sessions that have not been accessed on 
+    * this node.
+    */
+   long getMaxActiveSessionCount();
+   
+   /**
+    * Gets the maximum number of active sessions that will concurrently be
+    * allowed on this node.  Does not include replicated sessions that have 
+    * not been accessed on this node.
+    */
+   int getMaxActiveAllowed();
+   
+   /**
+    * Sets the maximum number of active sessions that will concurrently be
+    * allowed on this node.  Does not include replicated sessions that have 
+    * not been accessed on this node.
+    * 
+    * <p>
+    * Note that if sessions fail over to this node from other nodes, the max
+    * number of active sessions may exceed this value.
+    * </p>
+    * 
+    * @param max the max number of sessions, or <code>-1</code> if there is
+    *            no limit.
+    */
+   void setMaxActiveAllowed(int max);
+   
+   /**
+    * Gets the maximum time interval, in seconds, between client requests
+    * after which sessions created by this manager should be expired.  A 
+    * negative time indicates that the session should never time out.
+    */
+   int getMaxInactiveInterval();
+   
+   /**
+    * Sets the maximum time interval, in seconds, between client requests
+    * after which sessions created by this manager should be expired.  A 
+    * negative time indicates that the session should never time out.
+    *
+    * @param interval The new maximum interval
+    */
+   void setMaxInactiveInterval(int minutes);
+   
+   /**
+    * Gets whether this manager's sessions are distributable.
+    */
+   boolean getDistributable();
+   
+   /**
+    * Gets the cumulative number of milliseconds spent in the 
+    * <code>Manager.backgroundProcess()</code> method.
+    */
+   long getProcessingTime();
+   
+   /**
+    * Outputs the replication statistics as an HTML table, with one row
+    * per session.
+    */
+   String reportReplicationStatistics();
+   
+   /**
+    * Outputs the replication statistics as a comma-separated-values, with one 
+    * row per session.  First row is a header listing field names.
+    */
+   String reportReplicationStatisticsCSV();
+
+   /**
+    * Outputs the replication statistics for the given session as a set of 
+    * comma-separated-values.  First row is a header listing field names.
+    */
+   String reportReplicationStatisticsCSV(String sessionId);
+   
+   /**
+    * Gets the number of characters used in creating a session id.  Excludes
+    * any jvmRoute.
+    */
+   int getSessionIdLength();
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,237 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.*;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+import org.jboss.logging.Logger;
+
+/**
+ * Web request valve to specifically handle Tomcat jvmRoute using mod_jk(2)
+ * module. We assume that the session is set by cookie only for now, i.e., no
+ * support of that from URL. Furthermore, the session id has a format of
+ * id.jvmRoute where jvmRoute is used by JK module to determine sticky session
+ * during load balancing.
+ *
+ * @author Ben Wang
+ * @version $Revision: 59035 $
+ */
+public class JvmRouteValve extends ValveBase implements Lifecycle
+{
+   // The info string for this Valve
+   private static final String info = "JvmRouteValve/1.0";
+
+   protected static Logger log_ = Logger.getLogger(JvmRouteValve.class);
+
+   // Valve-lifecycle_ helper object
+   protected LifecycleSupport support = new LifecycleSupport(this);
+
+   protected AbstractJBossManager manager_;
+
+   /**
+    * Create a new Valve.
+    *
+    */
+   public JvmRouteValve(AbstractJBossManager manager)
+   {
+      super();
+      manager_ = manager;
+   }
+
+   /**
+    * Get information about this Valve.
+    */
+   public String getInfo()
+   {
+      return info;
+   }
+
+   public void invoke(Request request, Response response) throws IOException, ServletException
+   {
+
+      // Need to check it before let it through. This is ok because this 
+      // valve is inserted only when mod_jk option is configured.
+      checkJvmRoute(request, response);
+
+      // let the servlet invokation go through
+      getNext().invoke(request, response);
+   }
+
+   public void checkJvmRoute(Request req, Response res)
+      throws IOException, ServletException
+   {
+      String oldsessionId = req.getRequestedSessionId();
+      HttpSession session = req.getSession(false);
+      if (session != null)
+      {
+         String sessionId = session.getId();
+
+         // Obtain JvmRoute
+         String jvmRoute = manager_.getJvmRoute();
+         if (log_.isDebugEnabled())
+         {
+            log_.debug("checkJvmRoute(): check if need to re-route based on JvmRoute. Session id: " +
+               sessionId + " jvmRoute: " + jvmRoute);
+         }
+
+         if (jvmRoute == null)
+         {
+            throw new RuntimeException("JvmRouteValve.checkJvmRoute(): Tomcat JvmRoute is null. " +
+               "Need to assign a value in Tomcat server.xml for load balancing.");
+         }
+
+         // Check if incoming session id has JvmRoute appended. If not, append it.
+         boolean setCookie = !req.isRequestedSessionIdFromURL();
+         handleJvmRoute(oldsessionId, sessionId, jvmRoute, res, setCookie);
+      }
+   }
+
+   protected void handleJvmRoute(String oldsessionId,
+                                 String sessionId, 
+                                 String jvmRoute, 
+                                 HttpServletResponse response,
+                                 boolean setCookie)
+   {
+      // Get requested jvmRoute.
+      // TODO. The current format is assumed to be id.jvmRoute. Can be generalized later.
+      String receivedJvmRoute = null;
+      int index = oldsessionId.lastIndexOf(".");
+      if (index > 0)
+      {
+         receivedJvmRoute = oldsessionId.substring(index + 1, sessionId.length());
+      }
+
+      String requestedJvmRoute = null;
+      index = sessionId.lastIndexOf(".");
+      if (index > 0)
+      {
+         requestedJvmRoute = sessionId.substring(index + 1, sessionId.length());
+      }
+
+      String newId = null;
+      if (!jvmRoute.equals(requestedJvmRoute))
+      {
+         if (requestedJvmRoute == null)
+         {
+            // If this valve is turned on, we assume we have an appendix of jvmRoute. 
+            // So this request is new.
+            newId = sessionId + "." + jvmRoute;
+         }         
+         else 
+         {
+            // We just had a failover since jvmRoute does not match. 
+            // We will replace the old one with the new one.         
+            if (log_.isDebugEnabled())
+            {
+               log_.debug("handleJvmRoute(): We have detected a failover with different jvmRoute." +
+                  " old one: " + requestedJvmRoute + " new one: " + jvmRoute + ". Will reset the session id.");
+            }
+            
+            String base = sessionId.substring(0, index);
+            newId = base + "." + jvmRoute;
+         }
+         
+         resetSessionId(sessionId, newId);
+         
+      }
+      /* Also check the jvmRoute received (via req.getRequestedSessionId()) */
+      if (!jvmRoute.equals(receivedJvmRoute))
+      {
+            if (log_.isDebugEnabled())
+            {
+               log_.debug("handleJvmRoute(): We have detected a failover with different jvmRoute." +
+                  " received one: " + receivedJvmRoute + " new one: " + jvmRoute + ". Will resent the session id.");
+            }
+            String base = sessionId.substring(0, index);
+            newId = base + "." + jvmRoute;
+      }
+
+      /* Change the sessionid cookie if needed */
+      if (setCookie && newId != null)
+         manager_.setNewSessionCookie(newId, response);
+   }
+   
+   private void resetSessionId(String oldId, String newId)
+   {
+      try
+      {
+         ClusteredSession session = (ClusteredSession)manager_.findSession(oldId);
+         // change session id with the new one using local jvmRoute.
+         if( session != null )
+         {
+            // Note this will trigger a session remove from the super Tomcat class.
+            session.resetIdWithRouteInfo(newId);
+            if (log_.isDebugEnabled())
+            {
+               log_.debug("resetSessionId(): changed catalina session to= [" + newId + "] old one= [" + oldId + "]");
+            }
+         }
+         else if (log_.isDebugEnabled())
+         {
+            log_.debug("resetSessionId(): no session with id " + newId + " found");
+         }
+      }
+      catch (IOException e)
+      {
+         if (log_.isDebugEnabled())
+         {
+            log_.debug("resetSessionId(): manager_.findSession() unable to find session= [" + oldId + "]", e);
+         }
+         throw new RuntimeException("JvmRouteValve.resetSessionId(): cannot find session [" + oldId + "]", e);
+      }
+   }
+
+   // Lifecycle Interface
+   public void addLifecycleListener(LifecycleListener listener)
+   {
+      support.addLifecycleListener(listener);
+   }
+
+   public void removeLifecycleListener(LifecycleListener listener)
+   {
+      support.removeLifecycleListener(listener);
+   }
+
+   public LifecycleListener[] findLifecycleListeners()
+   {
+      return support.findLifecycleListeners();
+   }
+
+   public void start() throws LifecycleException
+   {
+      support.fireLifecycleEvent(START_EVENT, this);
+   }
+
+   public void stop() throws LifecycleException
+   {
+      support.fireLifecycleEvent(STOP_EVENT, this);
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,15 @@
+clusteredSession.attributeEvent=Session attribute event listener threw exception
+clusteredSession.invalidate.ise=invalidate: Session already invalidated
+clusteredSession.isNew.ise=isNew: Session already invalidated
+clusteredSession.getAttribute.ise=getAttribute: Session already invalidated
+clusteredSession.getAttributeNames.ise=getAttributeNames: Session already invalidated
+clusteredSession.getCreationTime.ise=getCreationTime: Session already invalidated
+clusteredSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: Session already invalidated
+clusteredSession.getValueNames.ise=getAttributeNames: Session already invalidated
+clusteredSession.notSerializable=Cannot serialize session attribute {0} for session {1}
+clusteredSession.removeAttribute.ise=removeAttribute: Session already invalidated
+clusteredSession.sessionEvent=Session event listener threw exception
+clusteredSession.setAttribute.iae=setAttribute: Non-serializable attribute
+clusteredSession.setAttribute.ise=setAttribute: Session already invalidated
+clusteredSession.setAttribute.namenull=setAttribute: name parameter cannot be null
+clusteredSession.sessionCreated=Created Session id = {0}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,15 @@
+clusteredSession.attributeEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u5c5e\u6027\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+clusteredSession.invalidate.ise=invalidate: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.isNew.ise=isNew: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getAttribute.ise=getAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getAttributeNames.ise=getAttributeNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getCreationTime.ise=getCreationTime: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getValueNames.ise=getAttributeNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.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
+clusteredSession.removeAttribute.ise=removeAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.sessionEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+clusteredSession.setAttribute.iae=setAttribute: \u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3067\u304d\u306a\u3044\u5c5e\u6027\u3067\u3059
+clusteredSession.setAttribute.ise=setAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.setAttribute.namenull=setAttribute: name\u30d1\u30e9\u30e1\u30bf\u306fnull\u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+clusteredSession.sessionCreated=\u30bb\u30c3\u30b7\u30e7\u30f3ID = {0} \u3092\u751f\u6210\u3057\u307e\u3057\u305f


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,194 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Map;
+
+
+/**
+ * Implementation of a clustered session for the JBossCacheManager. The replication granularity
+ * level is session based; that is, we replicate per whole session object.
+ * We use JBossCache for our internal replicated data store.
+ * The internal structure in JBossCache is as follows:
+ * <pre>
+ * /JSESSION
+ *    /hostname
+ *       /web_app_path    (path + session id is unique)
+ *          /id    Map(id, session)
+ *                    (VERSION_KEY, version)  // Used for version tracking. version is an Integer.
+ * </pre>
+ * <p/>
+ * Note that the isolation level of the cache dictates the
+ * concurrency behavior.</p>
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ * 
+ * @version $Revision: 57206 $
+ */
+class SessionBasedClusteredSession
+   extends JBossCacheClusteredSession
+{
+   static final long serialVersionUID = 3200976125245487256L;
+   /**
+    * Descriptive information describing this Session implementation.
+    */
+   protected static final String info = "SessionBasedClusteredSession/1.0";
+
+   public SessionBasedClusteredSession(JBossCacheManager manager)
+   {
+      super(manager);
+   }
+
+   // ---------------------------------------------- Overridden Public Methods
+
+   /**
+    * Return a string representation of this object.
+    */
+   public String toString()
+   {
+      StringBuffer sb = new StringBuffer();
+      sb.append("SessionBasedClusteredSession[");
+      sb.append(super.toString());
+      sb.append("]");
+      return (sb.toString());
+
+   }
+
+   public void removeMyself()
+   {
+      proxy_.removeSession(realId);
+   }
+
+   public void removeMyselfLocal()
+   {
+      proxy_.removeSessionLocal(realId);
+   }
+
+   // ----------------------------------------------HttpSession Public Methods
+   
+   /**
+    * Does nothing -- all attributes are populated already
+    */
+   protected void populateAttributes()
+   {
+      // no-op
+   }
+
+   protected Object getJBossInternalAttribute(String name)
+   {
+      Object result = attributes.get(name);
+
+      // Do dirty check even if result is null, as w/ SET_AND_GET null
+      // still makes us dirty (ensures timely replication w/o using ACCESS)
+      if (isGetDirty(result))
+      {
+         sessionAttributesDirty();
+      }
+
+      return result;
+
+   }
+
+   protected Object removeJBossInternalAttribute(String name, 
+                                                 boolean localCall, 
+                                                 boolean localOnly)
+   {
+      if (localCall)
+         sessionAttributesDirty();
+      return attributes.remove(name);
+   }
+
+   protected Map getJBossInternalAttributes()
+   {
+      return attributes;
+   }
+
+   protected Object setJBossInternalAttribute(String name, Object value)
+   {
+      sessionAttributesDirty();
+      return attributes.put(name, value);
+   }
+
+   /**
+    * Overrides the superclass version by additionally reading the 
+    * attributes map.
+    * 
+    * <p>
+    * This method is deliberately public so it can be used to reset
+    * the internal state of a session object using serialized
+    * contents replicated from another JVM via JBossCache.
+    * </p>
+    * 
+    * @see org.jboss.web.tomcat.tc5.session.ClusteredSession#readExternal(java.io.ObjectInput)
+    */
+   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+   {
+      synchronized (this)
+      {
+         // Let superclass read in everything but the attribute map
+         super.readExternal(in);
+      
+         attributes = (Map) in.readObject();      
+      }
+   }
+
+   /**
+    * Overrides the superclass version by appending the attributes map. Does
+    * not write any attributes whose names are found in 
+    * {@link ClusteredSession#excludedAttributes}.
+    * 
+    * @see org.jboss.web.tomcat.tc5.session.ClusteredSession#writeExternal(java.io.ObjectOutput)
+    */
+   public void writeExternal(ObjectOutput out) throws IOException
+   {
+      synchronized (this)
+      {
+         // Let superclass write out everything but the attribute map
+         super.writeExternal(out);
+         
+         // Don't replicate any excluded attributes
+         Map excluded = removeExcludedAttributes(attributes);
+        
+         out.writeObject(attributes);
+        
+         // Restore any excluded attributes
+         if (excluded != null)
+            attributes.putAll(excluded);
+      }   
+   
+   }
+   
+
+   /**
+    * Overrides the superclass version to return <code>true</code> if either
+    * the metadata or the attributes are dirty.
+    */
+   public boolean getReplicateSessionBody()
+   {
+      return isSessionDirty() || getExceedsMaxUnreplicatedInterval();
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,239 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Random;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import org.jboss.logging.Logger;
+
+/**
+ * Unique session id generator
+ *
+ * @author Ben Wang
+ */
+public class SessionIDGenerator
+{
+   protected final static int SESSION_ID_BYTES = 16; // We want 16 Bytes for the session-id
+   protected final static String SESSION_ID_HASH_ALGORITHM = "MD5";
+   protected final static String SESSION_ID_RANDOM_ALGORITHM = "SHA1PRNG";
+   protected final static String SESSION_ID_RANDOM_ALGORITHM_ALT = "IBMSecureRandom";
+   protected Logger log = Logger.getLogger(SessionIDGenerator.class);
+
+   protected MessageDigest digest = null;
+   protected Random random = null;
+   protected static SessionIDGenerator s_;
+   
+   protected String sessionIdAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*";
+
+   public static SessionIDGenerator getInstance()
+   {
+      if (s_ == null) s_ = new SessionIDGenerator();
+      return s_;
+   }
+
+   /**
+    * The SessionIdAlphabet is the set of characters used to create a session Id
+    */
+   public void setSessionIdAlphabet(String sessionIdAlphabet) 
+   {
+      if (sessionIdAlphabet.length() != 65) {
+         throw new IllegalArgumentException("SessionIdAlphabet must be exactly 65 characters long");
+      }
+
+      checkDuplicateChars(sessionIdAlphabet);
+
+      this.sessionIdAlphabet = sessionIdAlphabet;
+   }
+
+   protected void checkDuplicateChars(String sessionIdAlphabet) {
+      char[] alphabet = sessionIdAlphabet.toCharArray();
+      for (int i=0; i < alphabet.length; i++) {
+          if (!uniqueChar(alphabet[i], sessionIdAlphabet)) {
+              throw new IllegalArgumentException("All chars in SessionIdAlphabet must be unique");
+          }
+      }
+   }
+      
+   // does a character appear in the String once and only once?
+   protected boolean uniqueChar(char c, String s) {
+       int firstIndex = s.indexOf(c);
+       if (firstIndex == -1) return false;
+       return s.indexOf(c, firstIndex + 1) == -1;
+   }
+
+   /**
+    * The SessionIdAlphabet is the set of characters used to create a session Id
+    */
+   public String getSessionIdAlphabet() {
+      return this.sessionIdAlphabet;
+   }
+   
+   public synchronized String getSessionId()
+   {
+      String id = generateSessionId();
+      if (log.isDebugEnabled())
+         log.debug("getSessionId called: " + id);
+      return id;
+   }
+
+
+   /**
+    * Generate a session-id that is not guessable
+    *
+    * @return generated session-id
+    */
+   protected synchronized String generateSessionId()
+   {
+      if (this.digest == null)
+      {
+         this.digest = getDigest();
+      }
+
+      if (this.random == null)
+      {
+         this.random = getRandom();
+      }
+
+      byte[] bytes = new byte[SESSION_ID_BYTES];
+
+      // get random bytes
+      this.random.nextBytes(bytes);
+
+      // Hash the random bytes
+      bytes = this.digest.digest(bytes);
+
+      // Render the result as a String of hexadecimal digits
+      return encode(bytes);
+   }
+
+   /**
+    * Encode the bytes into a String with a slightly modified Base64-algorithm
+    * This code was written by Kevin Kelley <kelley at ruralnet.net>
+    * and adapted by Thomas Peuss <jboss at peuss.de>
+    *
+    * @param data The bytes you want to encode
+    * @return the encoded String
+    */
+   protected String encode(byte[] data)
+   {
+      char[] out = new char[((data.length + 2) / 3) * 4];
+      char[] alphabet = this.sessionIdAlphabet.toCharArray();
+
+      //
+      // 3 bytes encode to 4 chars.  Output is always an even
+      // multiple of 4 characters.
+      //
+      for (int i = 0, index = 0; i < data.length; i += 3, index += 4)
+      {
+         boolean quad = false;
+         boolean trip = false;
+
+         int val = (0xFF & (int) data[i]);
+         val <<= 8;
+         if ((i + 1) < data.length)
+         {
+            val |= (0xFF & (int) data[i + 1]);
+            trip = true;
+         }
+         val <<= 8;
+         if ((i + 2) < data.length)
+         {
+            val |= (0xFF & (int) data[i + 2]);
+            quad = true;
+         }
+         out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
+         val >>= 6;
+         out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
+         val >>= 6;
+         out[index + 1] = alphabet[val & 0x3F];
+         val >>= 6;
+         out[index + 0] = alphabet[val & 0x3F];
+      }
+      return new String(out);
+   }
+
+   /**
+    * get a random-number generator
+    *
+    * @return a random-number generator
+    */
+   protected synchronized Random getRandom()
+   {
+      long seed;
+      Random random = null;
+
+      // Mix up the seed a bit
+      seed = System.currentTimeMillis();
+      seed ^= Runtime.getRuntime().freeMemory();
+
+      try
+      {
+         random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM);
+      }
+      catch (NoSuchAlgorithmException e)
+      {
+         try
+         {
+            random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM_ALT);
+         }
+         catch (NoSuchAlgorithmException e_alt)
+         {
+            log.error("Could not generate SecureRandom for session-id randomness", e);
+            log.error("Could not generate SecureRandom for session-id randomness", e_alt);
+            return null;
+         }
+      }
+
+      // set the generated seed for this PRNG
+      random.setSeed(seed);
+
+      return random;
+   }
+
+   /**
+    * get a MessageDigest hash-generator
+    *
+    * @return a hash generator
+    */
+   protected synchronized MessageDigest getDigest()
+   {
+      MessageDigest digest = null;
+
+      try
+      {
+         digest = MessageDigest.getInstance(SESSION_ID_HASH_ALGORITHM);
+      }
+      catch (NoSuchAlgorithmException e)
+      {
+         log.error("Could not generate MessageDigest for session-id hashing", e);
+         return null;
+      }
+
+      return digest;
+   }
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,301 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+public final class SessionReplicationContext
+{
+   private static final ThreadLocal replicationContext = new ThreadLocal();
+   
+   private static final SessionReplicationContext EMPTY = new SessionReplicationContext();
+   
+   private int webappCount;
+   private int activityCount;
+   private SnapshotManager soleManager;
+   private ClusteredSession soleSession;
+   private Map crossCtxSessions;
+   private Map expiredSessions;
+   private Request outerRequest;
+   private Response outerResponse;
+   
+   /**
+    * Associate a SessionReplicationContext with the current thread, if
+    * there isn't one already.  If there isn't one, associate the 
+    * given request and response with the context.
+    * <p/>
+    * <strong>NOTE:</strong> Nested calls to this method and {@link #exitWebapp()}
+    * are supported; once a context is established the number of calls to this
+    * method and <code>exitWebapp()</code> are tracked.
+    * 
+    * @param request
+    * @param response
+    */
+   public static void enterWebapp(Request request, Response response, boolean startCacheActivity)
+   {
+      SessionReplicationContext ctx = getCurrentContext();
+      if (ctx == null)
+      {
+         ctx = new SessionReplicationContext(request, response);
+         replicationContext.set(ctx);
+      }
+      
+      ctx.webappCount++;
+      if (startCacheActivity)
+         ctx.activityCount++;
+   }
+   
+   /**
+    * Signals that the webapp is finished handling the request (and
+    * therefore replication can begin.)
+    * 
+    * @return a SessionReplicationContext, from which information
+    *         about any sessions needing replication can be obtained.
+    *         Will not return <code>null</code>.
+    */
+   public static SessionReplicationContext exitWebapp()
+   {
+      SessionReplicationContext ctx = getCurrentContext();
+      if (ctx != null)
+      {
+         ctx.webappCount--;
+         if (ctx.webappCount < 1)
+         {
+            // We've unwound any nested webapp calls, so we'll clean up and 
+            // return the context to allow replication.  If all cache activity
+            // is done as well, clear the ThreadLocal
+            
+            ctx.outerRequest = null;
+            ctx.outerResponse = null;
+            
+            if (ctx.activityCount < 1)
+               replicationContext.set(null);
+            
+            return ctx;
+         }
+      }
+      
+      // A nested valve called us. Just return an empty context
+      return EMPTY;
+      
+   }
+   
+   public static void bindSession(ClusteredSession session, SnapshotManager manager)
+   {
+      SessionReplicationContext ctx = getCurrentContext();
+      if (ctx != null && ctx.webappCount > 0)
+      {
+         ctx.addReplicatableSession(session, manager);
+      }
+      /*else {
+         We are past the part of the request cycle where we 
+         track sessions for replication
+      }*/
+   }
+   
+   public static void sessionExpired(ClusteredSession session, String realId, SnapshotManager manager)
+   {
+      SessionReplicationContext ctx = getCurrentContext();
+      if (ctx != null && ctx.webappCount > 0)
+      {
+         ctx.addExpiredSession(session, manager);
+      }      
+   }
+   
+   public static boolean isSessionBoundAndExpired(String realId, SnapshotManager manager)
+   {
+      boolean result = false;
+      SessionReplicationContext ctx = getCurrentContext();
+      if (ctx != null)
+      {
+         result = ctx.isSessionExpired(realId, manager);
+      }
+      return result;
+   }
+   
+   /**
+    * Marks the current thread as actively processing the given session.
+    * If the thread has already been so marked, increases a counter
+    * so a subsequent call to finishLocalActivity does not remove
+    * the association (allowing nested calls).
+    */
+   public static void startCacheActivity()
+   {
+      SessionReplicationContext ctx = getCurrentContext();
+      if (ctx == null)
+      {
+         ctx = new SessionReplicationContext();
+         replicationContext.set(ctx);
+      }
+      
+      ctx.activityCount++;
+   }
+   
+   /**
+    * Marks the completion of activity on a given session.  Should be called
+    * once for each invocation of {@link #startCacheActivity()}.
+    */
+   public static void finishCacheActivity()
+   {
+      SessionReplicationContext ctx = getCurrentContext();
+      if (ctx != null)
+      {
+         ctx.activityCount--;
+         if (ctx.activityCount < 1 && ctx.webappCount < 1)
+         {
+            replicationContext.set(null);
+         }
+      }
+   }
+   
+   public static boolean isLocallyActive()
+   {
+      return getCurrentContext() != null;
+   }
+   
+   public static Request getOriginalRequest()
+   {
+      SessionReplicationContext ctx = getCurrentContext();
+      return (ctx == null ? null : ctx.outerRequest);
+   }
+   
+   public static Response getOriginalResponse()
+   {
+      SessionReplicationContext ctx = getCurrentContext();
+      return (ctx == null ? null : ctx.outerResponse);
+   }
+   
+   private static SessionReplicationContext getCurrentContext()
+   {
+      return (SessionReplicationContext) replicationContext.get();
+   }
+   
+   private SessionReplicationContext(Request request, Response response) 
+   {
+      this.outerRequest = request;
+      this.outerResponse = response;
+   }
+   
+   private SessionReplicationContext() {}
+
+   /**
+    * Gets a Map<SnapshotManager, ClusteredSession> of sessions that were accessed
+    * during the course of a request.  Will only be non-null if 
+    * {@link #bindSession(ClusteredSession, SnapshotManager)} was called
+    * with more than one SnapshotManager (i.e the request crossed session
+    * contexts.)
+    */
+   public Map getCrossContextSessions()
+   {
+      return crossCtxSessions;
+   }
+
+   /**
+    * Gets the SnapshotManager that was passed to  
+    * {@link #bindSession(ClusteredSession, SnapshotManager)} if and only
+    * if only one such SnapshotManager was passed. Returns <code>null</code>
+    * otherwise, in which case a cross-context request is a possibility,
+    * and {@link #getCrossContextSessions()} should be checked.
+    */
+   public SnapshotManager getSoleSnapshotManager()
+   {
+      return soleManager;
+   }
+
+   /**
+    * Gets the ClusteredSession that was passed to  
+    * {@link #bindSession(ClusteredSession, SnapshotManager)} if and only
+    * if only one SnapshotManager was passed. Returns <code>null</code>
+    * otherwise, in which case a cross-context request is a possibility,
+    * and {@link #getCrossContextSessions()} should be checked.
+    */
+   public ClusteredSession getSoleSession()
+   {
+      return soleSession;
+   }
+   
+   private void addReplicatableSession(ClusteredSession session, SnapshotManager mgr)
+   {
+      if (crossCtxSessions != null)
+      {
+         crossCtxSessions.put(session, mgr);
+      }
+      else if (soleManager == null)
+      {
+         // First one bound
+         soleManager = mgr;
+         soleSession = session;
+      }
+      else if (!mgr.equals(soleManager))
+      {
+         // We have a cross-context call; need a Map for the sessions
+         crossCtxSessions = new HashMap();
+         crossCtxSessions.put(soleSession, soleManager);
+         crossCtxSessions.put(session, mgr);
+         soleManager = null;
+         soleSession = null;
+      }
+      else
+      {
+         soleSession = session;
+      }
+   }
+   
+   private void addExpiredSession(ClusteredSession session, SnapshotManager manager)
+   {
+      boolean store = manager.equals(soleManager);
+      if (store)
+      {
+         soleManager = null;
+         soleSession = null;
+      }
+      else if (crossCtxSessions != null)
+      {
+         // Only store the session if it was previously in our map
+         store = (crossCtxSessions.remove(session) != null);
+      }
+      
+      if (store)
+      {
+         if (this.expiredSessions == null)
+         {
+            expiredSessions = new HashMap();
+         }
+         expiredSessions.put(manager, session.getRealId());      
+      }
+   }
+   
+   private boolean isSessionExpired(String realId, SnapshotManager manager)
+   {
+      boolean result = false;
+      if (expiredSessions != null)
+      {
+         result = realId.equals(expiredSessions.get(manager));
+      }      
+      return result;
+   }
+   
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,85 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.logging.Logger;
+
+/**
+ * Abstract base class for a session snapshot manager.
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @version $Revision: 57206 $
+ */
+public abstract class SnapshotManager
+{
+   // The manager the snapshot manager should use
+   protected AbstractJBossManager manager;
+
+   // The context-path
+   protected String contextPath;
+   
+   private Logger log;
+
+   public SnapshotManager(AbstractJBossManager manager, String path)
+   {
+      this.manager = manager;
+      contextPath = path;
+      
+      String suffix = path;
+      if (suffix == null || "".equals(suffix))
+         suffix = "ROOT";
+      log = Logger.getLogger(getClass().getName() + "." + suffix);
+   }
+
+   /**
+    * Tell the snapshot manager which session was modified and
+    * must be replicated
+    */
+   public abstract void snapshot(ClusteredSession session);
+
+   /**
+    * Start the snapshot manager
+    */
+   public abstract void start();
+
+   /**
+    * Stop the snapshot manager
+    */
+   public abstract void stop();
+
+   protected String getContextPath()
+   {
+      return contextPath;
+   }
+
+   protected Logger getLog()
+   {
+      return log;
+   }
+
+   protected AbstractJBossManager getManager()
+   {
+      return manager;
+   }
+   
+   
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,147 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.aop.Advised;
+
+/**
+ * Utility methods related to JBoss distributed sessions.
+ * 
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class Util
+{
+   // Types that are considered "primitive".
+   private static final Set immediates =
+      new HashSet(Arrays.asList(new Object[]{
+         String.class,
+         Boolean.class,
+         Double.class,
+         Float.class,
+         Integer.class,
+         Long.class,
+         Short.class,
+         Character.class,
+         Boolean.TYPE,
+         Double.TYPE,
+         Float.TYPE,
+         Integer.TYPE,
+         Long.TYPE,
+         Short.TYPE,
+         Character.TYPE,
+         Class.class}));
+
+   /**
+    * Returns a session id with any trailing jvmRoute removed.
+    * 
+    * @param sessionId the raw session id
+    * 
+    * @return <code>sessionId</code> with the final '.' and any
+    *         characters thereafter removed.
+    */
+   public static String getRealId(String sessionId)
+   {
+      int index = sessionId.lastIndexOf(".");
+      if (index > 0)
+      {
+         return sessionId.substring(0, index);
+      }
+      else
+      {
+         return sessionId;
+      }
+   }
+
+   /**
+    * Checks whether the given object is usable for FIELD granularity 
+    * replication.
+    * 
+    * @param pojo  the pojo
+    * @return <code>true</code> if the attribute type is acceptable,
+    *         <code>false</code> otherwise
+    */
+   public static boolean checkPojoType(Object pojo)
+   {
+      return (   (pojo instanceof Serializable)
+              || (pojo instanceof Collection) 
+              || (pojo instanceof Map) 
+              || (pojo instanceof Advised)
+              || (immediates.contains(pojo.getClass())));
+   }
+   
+   public static Set parseComplexFields(Class clazz)
+   {
+      Set result = new HashSet();
+      
+      while (clazz != null)
+      {
+         Field[] fields = clazz.getDeclaredFields();
+         for (int i = 0; i < fields.length; i++)
+         {
+            if (!immediates.contains(fields[i].getType()) 
+                  && isReplicatable(fields[i]))
+            {
+               result.add(fields[i].getName());
+            }
+         }
+         
+         clazz = clazz.getSuperclass();
+      }
+      return result;
+   }
+
+   /**
+    * Returns false if the given field is static, transient or final.
+    * 
+    * @param f the field
+    * @return
+    */
+   public static boolean isReplicatable(Field f) {
+      int mods = f.getModifiers();
+      /**
+       * The following modifiers are ignored in the cache, i.e., they will not be stored in the cache.
+       * Whenever, user trying to access these fields, it will be accessed from the in-memory version.
+       */
+      if (Modifier.isStatic(mods)
+            || Modifier.isTransient(mods)
+            || Modifier.isFinal(mods)) {
+         return false;
+      }
+      return true;
+   }
+
+   /**
+    * Prevent instantiation.
+    */
+   private Util() {}
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1313 @@
+/*
+ * 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.jboss.web.tomcat.service.sso;
+
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+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.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.session.ManagerBase;
+import org.jboss.web.tomcat.service.JBossWeb;
+import org.jboss.web.tomcat.service.session.JBossManager;
+
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * 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 Brian E. Stansberry based on the work of Craig R. McClanahan
+ * @version $Revision: 57329 $ $Date: 2006-10-02 00:35:46 +0200 (lun., 02 oct. 2006) $
+ */
+public class ClusteredSingleSignOn
+   extends org.apache.catalina.authenticator.SingleSignOn
+   implements LifecycleListener
+{
+   /** By default we process expired SSOs no more often than once per minute */
+   public static final int DEFAULT_PROCESS_EXPIRES_INTERVAL = 60;
+   /** By default we let SSOs without active sessions live for 30 mins */
+   public static final int DEFAULT_MAX_EMPTY_LIFE = 1800;
+   
+   // Override the superclass value
+   static
+   {
+      info = ClusteredSingleSignOn.class.getName();
+   }
+    
+   // ----------------------------------------------------- Instance Variables
+
+ 
+   /**
+    * Fully qualified name of a class implementing
+    * {@link SSOClusterManager SSOClusterManager} that will be used
+    * to manage SSOs across a cluster.
+    */
+   private String clusterManagerClass =
+      TreeCacheSSOClusterManager.class.getName();
+
+   /**
+    * Object used to provide cross-cluster support for single sign on.
+    */
+   private SSOClusterManager ssoClusterManager = null;
+
+   /**
+    * Object name of the tree cache used by SSOClusterManager.
+    * Only relevant if the SSOClusterManager implementation is
+    * TreeCacheSSOClusterManager.
+    */
+   private String treeCacheName = JBossWeb.DEFAULT_CACHE_NAME;
+
+// DISABLED UNTIL 4.0.6.CR1   
+//   /**
+//    * Object name of the thread pool used by SSOClusterManager.
+//    * Only relevant if the SSOClusterManager implementation is
+//    * TreeCacheSSOClusterManager.
+//    */
+//   private String threadPoolName = "jboss.system:service=ThreadPool";
+
+   /** Currently started Managers that have associated as session with an SSO */
+   private Set activeManagers = Collections.synchronizedSet(new HashSet());
+   
+   /** Max number of ms an SSO with no active sessions will be usable by a request */
+   private int maxEmptyLife = DEFAULT_MAX_EMPTY_LIFE * 1000;
+   
+   /** 
+    * Minimum number of ms since the last processExpires() run 
+    * before a new run is allowed.
+    */
+   private int processExpiresInterval = DEFAULT_PROCESS_EXPIRES_INTERVAL * 1000;
+   
+   /** Timestamp of the last processExpires() run */
+   private long lastProcessExpires = System.currentTimeMillis();
+   
+   /** 
+    * Map<String, Long> containing the ids of SSOs with no active sessions
+    * and the time at which they entered that state
+    */
+   private Map emptySSOs = new ConcurrentHashMap();
+   
+   /** Used for sync locking of processExpires runs */
+   private final Object mutex = new Object();
+
+   // ------------------------------------------------------------- Properties
+
+   /**
+    * Gets the object that provides SSO support across a cluster.
+    *
+    * @return the object provided cluster support, or <code>null</code> if
+    *         no such object has been configured.
+    */
+   public SSOClusterManager getClusterManager()
+   {
+      return this.ssoClusterManager;
+   }
+
+
+   /**
+    * Sets the object that provides SSO support across a cluster.
+    *
+    * @param clusterManager the object that provides SSO support.
+    * @throws IllegalStateException if this method is invoked after this valve
+    *                               has been started.
+    */
+   public void setClusterManager(SSOClusterManager clusterManager)
+   {
+      if (started && (clusterManager != ssoClusterManager))
+      {
+         throw new IllegalStateException("already started -- cannot set a " +
+            "new SSOClusterManager");
+      }
+
+      this.ssoClusterManager = clusterManager;
+
+      if (clusterManager != null)
+      {
+         clusterManagerClass = clusterManager.getClass().getName();
+      }
+   }
+
+
+   /**
+    * Gets the name of the class that will be used to provide SSO support
+    * across a cluster.
+    *
+    * @return Fully qualified name of a class implementing
+    *         {@link SSOClusterManager SSOClusterManager}
+    *         that is being used to manage SSOs across a cluster.
+    *         May return <code>null</code> (the default) if clustered
+    *         SSO support is not configured.
+    */
+   public String getClusterManagerClass()
+   {
+      return clusterManagerClass;
+   }
+
+
+   /**
+    * Sets the name of the class that will be used to provide SSO support
+    * across a cluster.
+    * <p><b>NOTE: </b>
+    * If this Valve has already started, and no SSOClusterManager has been
+    * configured for it, calling this method will
+    *
+    * @param managerClass Fully qualified name of a class implementing
+    *                     {@link SSOClusterManager SSOClusterManager}
+    *                     that will be used to manage SSOs across a cluster.
+    *                     Class must declare a public no-arguments
+    *                     constructor.  <code>null</code> is allowed.
+    */
+   public void setClusterManagerClass(String managerClass)
+   {
+      if (!started)
+      {
+         clusterManagerClass = managerClass;
+      }
+      else if (ssoClusterManager == null)
+      {
+         try
+         {
+            createClusterManager(managerClass);
+         }
+         catch (LifecycleException e)
+         {
+            getContainer().getLogger().error("Exception creating SSOClusterManager " +
+               managerClass, e);
+         }
+      }
+      else
+      {
+          getContainer().getLogger().error("Cannot set clusterManagerClass to " + managerClass +
+            "; already started using " + clusterManagerClass);
+      }
+   }
+
+   /**
+    * Object name of the tree cache used by SSOClusterManager.
+    * Only relevant if the SSOClusterManager implementation is
+    * TreeCacheSSOClusterManager.
+    */
+   public String getTreeCacheName()
+   {
+      return treeCacheName;
+   }
+
+   /**
+    * Sets the object name of the tree cache used by SSOClusterManager.
+    * Only relevant if the SSOClusterManager implementation is
+    * TreeCacheSSOClusterManager.
+    */
+   public void setTreeCacheName(String cacheName)
+      throws Exception
+   {
+      this.treeCacheName = cacheName;
+      if (ssoClusterManager != null
+         && ssoClusterManager instanceof TreeCacheSSOClusterManager)
+      {
+         ((TreeCacheSSOClusterManager) ssoClusterManager).setCacheName(cacheName);
+      }
+   }
+   
+// DISABLED UNTIL 4.0.6.CR1
+//   /**
+//    * Object name of the thread pool used by SSOClusterManager.
+//    * Only relevant if the SSOClusterManager implementation is
+//    * TreeCacheSSOClusterManager.
+//    */
+//   public String getThreadPoolName()
+//   {
+//      return threadPoolName;
+//   }
+//
+//   /**
+//    * Sets the object name of the thread pool used by SSOClusterManager.
+//    * Only relevant if the SSOClusterManager implementation is
+//    * TreeCacheSSOClusterManager.
+//    */
+//   public void setThreadPoolName(String poolName)
+//      throws Exception
+//   {
+//      this.threadPoolName = poolName;
+//      if (ssoClusterManager != null
+//         && ssoClusterManager instanceof TreeCacheSSOClusterManager)
+//      {
+//         ((TreeCacheSSOClusterManager) ssoClusterManager).setThreadPoolName(poolName);
+//      }
+//   }
+
+   /**
+    * Gets the max number of seconds an SSO with no active sessions will be 
+    * usable by a request.
+    * 
+    * @return a non-negative number
+    * 
+    * @see #DEFAULT_MAX_EMPTY_LIFE    * 
+    * @see #setMaxEmptyLife() 
+    */
+   public int getMaxEmptyLife()
+   {
+      return (maxEmptyLife / 1000);
+   }
+
+
+   /**
+    * Sets the maximum number of seconds an SSO with no active sessions will be 
+    * usable by a request.
+    * <p>
+    * A positive value for this property allows a user to continue to use an SSO
+    * even after all the sessions associated with it have been expired. It does not
+    * keep an SSO alive if a session associated with it has been invalidated due to 
+    * an <code>HttpSession.invalidate()</code> call.
+    * </p>
+    * <p>
+    * The primary purpose of this property is to avoid the situation where a server
+    * on which all of an SSO's sessions lives is shutdown, thus expiring all the
+    * sessions and causing the invalidation of the SSO. A positive value for this
+    * property would give the user an opportunity to fail over to another server
+    * and maintain the SSO.
+    * </p>
+    * 
+    * @param maxEmptyLife a non-negative number
+    * 
+    * @throws IllegalArgumentException if <code>maxEmptyLife < 0</code>
+    */
+   public void setMaxEmptyLife(int maxEmptyLife)
+   {
+      if (maxEmptyLife < 0)
+         throw new IllegalArgumentException("maxEmptyLife must be >= 0");
+      
+      this.maxEmptyLife = maxEmptyLife * 1000;
+   }
+
+
+   /**
+    * Gets the minimum number of seconds since the start of the last check for overaged
+    * SSO's with no active sessions before a new run is allowed.
+    * 
+    * @return a positive number
+    * 
+    * @see #DEFAULT_PROCESS_EXPIRES_INTERVAL
+    * @see #setMaxEmptyLife() 
+    * @see #setProcessExpiresInterval(int)
+    */
+   public int getProcessExpiresInterval()
+   {
+      return processExpiresInterval / 1000;
+   }
+
+   /**
+    * Sets the minimum number of seconds since the start of the last check for overaged
+    * SSO's with no active sessions before a new run is allowed.  During this check,
+    * any such overaged SSOs will be invalidated. 
+    * <p>
+    * Note that setting this value does not imply that a check will be performed
+    * every <code>processExpiresInterval</code> seconds, only that it will not
+    * be performed more often than that.
+    * </p>
+    * 
+    * @param processExpiresInterval a non-negative number. <code>0</code> means
+    *                               the overage check can be performed whenever
+    *                               the container wishes to.
+    *                               
+    * @throws IllegalArgumentException if <code>processExpiresInterval < 1</code>
+    * 
+    * @see #setMaxEmptyLife()
+    */
+   public void setProcessExpiresInterval(int processExpiresInterval)
+   {
+      if (processExpiresInterval < 0)
+         throw new IllegalArgumentException("processExpiresInterval must be >= 0");
+      
+      this.processExpiresInterval = processExpiresInterval * 1000;
+   }
+
+
+   /**
+    * Gets the timestamp of the start of the last check for overaged
+    * SSO's with no active sessions.
+    * 
+    * @see #setProcessExpiresInterval(int)
+    */
+   public long getLastProcessExpires()
+   {
+      return lastProcessExpires;
+   }
+
+
+   // ------------------------------------------------------ Lifecycle 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.
+    *
+    * @throws 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"));
+      }
+
+      // Attempt to create an SSOClusterManager
+      createClusterManager(getClusterManagerClass());
+
+      lifecycle.fireLifecycleEvent(START_EVENT, null);
+      started = true;
+
+      if (ssoClusterManager != null)
+      {
+         ssoClusterManager.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.
+    *
+    * @throws 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"));
+      }
+
+      if (ssoClusterManager != null)
+      {
+         ssoClusterManager.stop();
+      }
+
+      lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+      started = false;
+
+   }
+
+
+   // ------------------------------------------------ SessionListener Methods
+
+
+   /**
+    * Updates the state of a single sign on session to reflect the destruction
+    * of a standard HTTP session.
+    * <p/>
+    * If the given event is a {@link Session#SESSION_DESTROYED_EVENT
+    * Session destroyed event}, checks whether the session was destroyed due
+    * to timeout or user action (i.e. logout).  If due to timeout, disassociates
+    * the Session from the single sign on session.  If due to logout, invokes
+    * the {@link #logout} method.
+    *
+    * @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()))
+         return;
+
+      // Look up the single session id associated with this session (if any)
+      Session session = event.getSession();
+      if (getContainer().getLogger().isDebugEnabled())
+          getContainer().getLogger().debug("Process session destroyed on " + session);
+
+      String ssoId = null;
+      synchronized (reverse)
+      {
+         ssoId = (String) reverse.get(session);
+      }
+      if (ssoId == null)
+         return;
+
+      try
+      {
+         // Was the session destroyed as the result of a timeout or
+         // the undeployment of the containing webapp?
+         // If so, we'll just remove the expired session from the
+         // SSO.  If the session was logged out, we'll log out
+         // of all sessions associated with the SSO.
+         if (isSessionTimedOut(session) || isManagerStopped(session))
+         {
+            removeSession(ssoId, session);
+
+            // Quite poor.  We hijack the caller thread (the Tomcat background thread)
+            // to do our cleanup of expired sessions
+            processExpires();
+         }
+         else
+         {
+            // The session was logged out.
+            logout(ssoId);
+         }
+      }
+      catch (Exception e)
+      {
+         // Don't propagate back to the webapp; we don't want to disrupt
+         // the session expiration process
+         getContainer().getLogger().error("Caught exception updating SSO " + ssoId +
+                                          " following destruction of session " +
+                                          session.getIdInternal(), e);
+      }
+   }
+
+   private boolean isSessionTimedOut(Session session)
+   {
+      return (session.getMaxInactiveInterval() > 0)
+               && (System.currentTimeMillis() - session.getLastAccessedTime() >=
+                  session.getMaxInactiveInterval() * 1000);
+   }
+
+   private boolean isManagerStopped(Session session)
+   {
+      boolean stopped = false;
+      
+      Manager manager = session.getManager();
+      
+      if (manager instanceof ManagerBase)
+      {
+         ObjectName mgrName = ((ManagerBase)manager).getObjectName();
+         stopped = (!activeManagers.contains(mgrName));
+      }
+      else if (manager instanceof JBossManager)
+      {
+         ObjectName mgrName = ((JBossManager)manager).getObjectName();
+         stopped = (!activeManagers.contains(mgrName));
+      }
+      else if (manager instanceof Lifecycle)
+      {
+         stopped = (!activeManagers.contains(manager));
+      }
+      // else we have no way to tell, so assume not
+      
+      return stopped;
+   }
+
+   // ----------------------------------------------  LifecycleListener Methods
+
+
+   public void lifecycleEvent(LifecycleEvent event)
+   {
+      String type = event.getType();
+      if (Lifecycle.BEFORE_STOP_EVENT.equals(type) 
+            || Lifecycle.STOP_EVENT.equals(type) 
+            || Lifecycle.AFTER_STOP_EVENT.equals(type))
+      {
+         Lifecycle source = event.getLifecycle();
+         boolean removed;
+         if (source instanceof ManagerBase)
+         {
+            removed = activeManagers.remove(((ManagerBase)source).getObjectName());
+         }
+         else if (source instanceof JBossManager)
+         {
+            removed = activeManagers.remove(((JBossManager)source).getObjectName());
+         }
+         else
+         {
+            removed = activeManagers.remove(source);
+         }
+         
+         if (removed)
+         {
+            source.removeLifecycleListener(this);
+            
+            if (getContainer().getLogger().isDebugEnabled())
+            {
+                getContainer().getLogger().debug("ClusteredSSO: removed " +
+                        "stopped manager " + source.toString());
+            }
+         }
+         
+         // TODO consider getting the sessions and removing any from our sso's
+         // Idea is to cleanup after managers that don't destroy sessions
+         
+      }      
+   }
+   
+
+   // ---------------------------------------------------------- Valve Methods
+
+
+   /**
+    * Perform single-sign-on support processing for this request.
+    * <p/>
+    * Overrides the superclass version by handling the fact that a
+    * single sign on may have been originated on another cluster node and
+    * thus may not have a <code>Principal</code> object associated with it
+    * on this node.
+    *
+    * @param request  The servlet request we are processing
+    * @param response The servlet response we are creating
+    * @param context  The valve context used to invoke the next valve
+    *                 in the current processing pipeline
+    * @throws IOException      if an input/output error occurs
+    * @throws 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 (getContainer().getLogger().isDebugEnabled())
+         getContainer().getLogger().debug("Process request for '" + request.getRequestURI() + "'");
+      if (request.getUserPrincipal() != null)
+      {
+         if (getContainer().getLogger().isDebugEnabled())
+            getContainer().getLogger().debug(" Principal '" + request.getUserPrincipal().getName() +
+               "' has already been authenticated");
+         getNext().invoke(request, response);
+         return;
+      }
+
+      // Check for the single sign on 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 (getContainer().getLogger().isDebugEnabled())
+            getContainer().getLogger().debug(" SSO cookie is not present");
+         getNext().invoke(request, response);
+         return;
+      }
+
+      // Look up the cached Principal associated with this cookie value
+      String ssoId = cookie.getValue();
+      if (getContainer().getLogger().isDebugEnabled())
+          getContainer().getLogger().debug(" Checking for cached principal for " + ssoId);
+      SingleSignOnEntry entry = getSingleSignOnEntry(cookie.getValue());
+      if (entry != null && isValid(ssoId, entry))
+      {
+         Principal ssoPrinc = entry.getPrincipal();
+         // have to deal with the fact that the entry may not have an
+         // associated Principal. SSO entries retrieved via a lookup from a 
+         // cluster will not have a Principal, as Principal is not Serializable
+         if (getContainer().getLogger().isDebugEnabled())
+         {
+             getContainer().getLogger().debug(" Found cached principal '" +
+               (ssoPrinc == null ? "NULL" : ssoPrinc.getName()) +
+               "' with auth type '" + entry.getAuthType() + "'");
+         }
+         request.setNote(Constants.REQ_SSOID_NOTE, cookie.getValue());
+         // Only set security elements if per-request reauthentication is 
+         // not required AND the SSO entry had a Principal.  
+         if (!getRequireReauthentication() && ssoPrinc != null)
+         {
+            request.setAuthType(entry.getAuthType());
+            request.setUserPrincipal(ssoPrinc);
+         }
+      }
+      else
+      {
+         if (getContainer().getLogger().isDebugEnabled())
+            getContainer().getLogger().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);
+   }
+
+
+   // ------------------------------------------------------ Protected Methods
+
+
+   /**
+    * Associate the specified single sign on identifier with the
+    * specified Session.
+    * <p/>
+    * Differs from the superclass version in that it notifies the cluster
+    * of any new association of SSO and Session.
+    *
+    * @param ssoId   Single sign on identifier
+    * @param session Session to be associated
+    */
+   protected void associate(String ssoId, Session session)
+   {
+      if (getContainer().getLogger().isDebugEnabled())
+          getContainer().getLogger().debug("Associate sso id " + ssoId + " with session " + session);
+
+      SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+      boolean added = false;
+      if (sso != null)
+         added = sso.addSession(this, session);
+
+      synchronized (reverse)
+      {
+         reverse.put(session, ssoId);
+      }
+
+      // If we made a change, track the manager and notify any cluster
+      if (added)
+      {
+         Manager manager = session.getManager();
+         
+         // Prefer to cache an ObjectName to avoid risk of leaking a manager,
+         // so if the manager exposes one, use it
+         Object mgrKey = null;
+         if (manager instanceof ManagerBase)
+         {            
+            mgrKey = ((ManagerBase)manager).getObjectName();
+         }
+         else if (manager instanceof JBossManager)
+         {
+            mgrKey = ((JBossManager)manager).getObjectName();
+         }
+         else if (manager instanceof Lifecycle)
+         {
+            mgrKey = manager;
+         }
+         else {
+            getContainer().getLogger().warn("Manager for session " + 
+                      session.getIdInternal() +
+                      " does not implement Lifecycle; web app shutdown may " +
+                      " lead to incorrect SSO invalidations");
+         }
+         
+         if (mgrKey != null)
+         {
+            synchronized (activeManagers)
+            {
+               if (!activeManagers.contains(mgrKey))
+               {
+                  activeManagers.add(mgrKey);
+                  ((Lifecycle) manager).addLifecycleListener(this);
+               }
+            }
+         }
+         
+         if (ssoClusterManager != null)         
+            ssoClusterManager.addSession(ssoId, session);
+      }
+   }
+
+
+   /**
+    * Deregister the specified session.  If it is the last session,
+    * then also get rid of the single sign on identifier.
+    * <p/>
+    * Differs from the superclass version in that it notifies the cluster
+    * of any disassociation of SSO and Session.
+    *
+    * @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 = getSingleSignOnEntry(ssoId);
+      if (sso == null)
+         return;
+
+      boolean removed = sso.removeSession(session);
+      // If we changed anything, notify any cluster
+      if (removed && ssoClusterManager != null)
+      {
+         ssoClusterManager.removeSession(ssoId, session);
+      }
+
+      // see if this was the last session on this node, 
+      // if remove sso entry from our local cache
+      if (sso.getSessionCount() == 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 (getContainer().getLogger().isDebugEnabled())
+          getContainer().getLogger().debug("Deregistering sso id '" + ssoId + "'");
+
+      
+      // It's possible we don't have the SSO locally but it's in
+      // the emptySSOs map; if so remove it
+      emptySSOs.remove(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 (getContainer().getLogger().isDebugEnabled())
+             getContainer().getLogger().debug(" 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
+   }
+
+
+   /**
+    * Deregister the given SSO, invalidating any associated sessions, then
+    * notify any cluster of the logout.
+    *
+    * @param ssoId the id of the SSO session
+    */
+   protected void logout(String ssoId)
+   {
+      deregister(ssoId);
+      //  broadcast logout to any cluster
+      if (ssoClusterManager != null)
+         ssoClusterManager.logout(ssoId);
+   }
+
+
+   /**
+    * 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 getSingleSignOnEntry(String ssoId)
+   {
+      SingleSignOnEntry sso = localLookup(ssoId);
+      // If we don't have one locally and there is a cluster,
+      // query the cluster for the SSO
+      if (sso == null && ssoClusterManager != null)
+      {
+         sso = ssoClusterManager.lookup(ssoId);
+         if (sso != null)
+         {
+            // Store it locally
+            synchronized (cache)
+            {
+               cache.put(ssoId, sso);
+            }
+         }
+      }
+      
+      return sso;
+   }
+
+
+   /**
+    * 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>HttpRequest</code> object via calls to
+    * {@link HttpRequest#setAuthType HttpRequest.setAuthType()} and
+    * {@link HttpRequest#setUserPrincipal HttpRequest.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 = getSingleSignOnEntry(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);
+               // JBAS-2314 -- bind principal to the entry as well
+               entry.setPrincipal(reauthPrincipal);
+            }
+         }
+      }
+
+      return reauthenticated;
+   }
+
+
+   /**
+    * Register the specified Principal as being associated with the specified
+    * value for the single sign on identifier.
+    * <p/>
+    * Differs from the superclass version in that it notifies the cluster
+    * of the registration.
+    *
+    * @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)
+   {
+      registerLocal(ssoId, principal, authType, username, password);
+
+      // broadcast change to any cluster
+      if (ssoClusterManager != null)
+         ssoClusterManager.register(ssoId, authType, username, password);
+   }
+
+
+   /**
+    * Remove a single Session from a SingleSignOn.  Called when
+    * a session is timed out and no longer active.
+    * <p/>
+    * Differs from the superclass version in that it notifies the cluster
+    * of any disassociation of SSO and Session.
+    *
+    * @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 (getContainer().getLogger().isDebugEnabled())
+          getContainer().getLogger().debug("Removing session " + session.toString() +
+            " from sso id " + ssoId);
+
+      // Get a reference to the SingleSignOn
+      SingleSignOnEntry entry = getSingleSignOnEntry(ssoId);
+      if (entry == null)
+         return;
+
+      // Remove the inactive session from SingleSignOnEntry
+      boolean removed = entry.removeSession(session);
+      // If we changed anything, notify any cluster
+      if (removed && ssoClusterManager != null)
+      {
+         ssoClusterManager.removeSession(ssoId, session);
+      }
+
+      // Remove the inactive session from the 'reverse' Map.
+      synchronized (reverse)
+      {
+         reverse.remove(session);
+      }
+
+//      // If there are no sessions left in the SingleSignOnEntry,
+//      // deregister the entry.
+//      if (entry.getSessionCount() == 0)
+//      {
+//         deregister(ssoId);
+//      }
+   }
+
+
+   /**
+    * 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.
+    * <p/>
+    * Differs from the superclass version in that it notifies the cluster
+    * of any update.
+    *
+    * @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)
+   {
+      boolean needToBroadcast = updateLocal(ssoId, principal, authType,
+         username, password);                                    
+                                    
+      // if there was a change, broadcast it to any cluster
+      if (needToBroadcast && ssoClusterManager != null)
+      {
+         ssoClusterManager.updateCredentials(ssoId, authType,
+            username, password);
+      }
+   }
+
+   //----------------------------------------------  Package-Protected Methods
+
+   /**
+    * Search in our local cache for an SSO entry.
+    *
+    * @param ssoId the id of the SSO session
+    * @return any SingleSignOnEntry associated with the given id, or
+    *         <code>null</code> if there is none.
+    */
+   SingleSignOnEntry localLookup(String ssoId)
+   {
+      synchronized (cache)
+      {
+         return ((SingleSignOnEntry) cache.get(ssoId));
+      }
+
+   }
+
+   /**
+    * Create a SingleSignOnEntry using the passed configuration parameters and
+    * register it in the local cache, bound to the given id.
+    *
+    * @param ssoId     the id of the SSO session
+    * @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
+    */
+   void registerLocal(String ssoId, Principal principal, String authType,
+      String username, String password)
+   {
+      if (getContainer().getLogger().isDebugEnabled())
+      {
+          getContainer().getLogger().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.
+    *
+    * @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
+    * @return <code>true</code> if the update resulted in an actual change
+    *         to the entry's authType, username or principal properties
+    */
+   boolean updateLocal(String ssoId, Principal principal, String authType,
+      String username, String password)
+   {
+      boolean shouldBroadcast = false;
+
+      SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+      // Only update if the entry is missing information
+      if (sso != null)
+      {
+         if (sso.getCanReauthenticate() == false)
+         {
+            if (getContainer().getLogger().isDebugEnabled())
+                getContainer().getLogger().debug("Update sso id " + ssoId + " to auth type " + authType);
+
+            synchronized (sso)
+            {
+               shouldBroadcast = sso.updateCredentials(principal, authType,
+                  username, password);
+            }
+         }
+         else if (sso.getPrincipal() == null && principal != null)
+         {
+            if (getContainer().getLogger().isDebugEnabled())
+                getContainer().getLogger().debug("Update sso id " + ssoId + " with principal " +
+                  principal.getName());
+
+            synchronized (sso)
+            {
+               sso.setPrincipal(principal);
+               // No need to notify cluster; Principals don't replicate
+            }
+         }
+
+      }
+
+      return shouldBroadcast;
+
+   }
+
+   void remoteUpdate(String ssoId, String authType,
+      String username, String password)
+   {
+      SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+      // Only update if the entry is missing information
+      if (sso != null && sso.getCanReauthenticate() == false)
+      {
+         if (getContainer().getLogger().isDebugEnabled())
+             getContainer().getLogger().debug("Update sso id " + ssoId + " to auth type " + authType);
+
+         synchronized (sso)
+         {
+            // Use the existing principal
+            Principal p = sso.getPrincipal();
+            sso.updateCredentials(p, authType, username, password);
+         }
+      }
+
+   }
+   
+   /**
+    * Callback from the SSOManager when it detects an SSO without
+    * any active sessions across the cluster
+    */
+   void notifySSOEmpty(String ssoId)
+   {
+      Object obj = emptySSOs.put(ssoId, new Long(System.currentTimeMillis()));
+      
+      if (obj == null && getContainer().getLogger().isDebugEnabled())
+      {
+         getContainer().getLogger().debug("Notified that SSO " + ssoId + " is empty");
+      }
+   }
+   
+   /**
+    * Callback from the SSOManager when it detects an SSO that
+    * has active sessions across the cluster
+    */
+   void notifySSONotEmpty(String ssoId)
+   {
+      Object obj = emptySSOs.remove(ssoId);
+      
+      if (obj != null && getContainer().getLogger().isDebugEnabled())
+      {
+         getContainer().getLogger().debug("Notified that SSO " + ssoId + 
+                                          " is no longer empty");
+      }
+   }
+
+   
+   // -------------------------------------------------------  Private Methods
+
+   
+   /**
+    * Instantiates an instance of the given class, making it this valve's
+    * SSOClusterManager.
+    * <p/>
+    * If this valve has been started and the given class implements
+    * <code>Lifecycle</code>, starts the new SSOClusterManager.
+    *
+    * @param className fully qualified class name of an implementation
+    *                  of {@link SSOClusterManager SSOClusterManager}.
+    * @throws LifecycleException if there is any problem instantiating or
+    *                            starting the object, or if the created
+    *                            object does not implement
+    *                            <code>SSOClusterManger</code>
+    */
+   private void createClusterManager(String className)
+      throws LifecycleException
+   {
+      if (ssoClusterManager != null)
+         return;
+
+      if (className != null)
+      {
+         SSOClusterManager mgr = null;
+         try
+         {
+            ClassLoader tcl =
+               Thread.currentThread().getContextClassLoader();
+            Class clazz = tcl.loadClass(className);
+            mgr = (SSOClusterManager) clazz.newInstance();
+            mgr.setSingleSignOnValve(this);
+            if (mgr instanceof TreeCacheSSOClusterManager)
+            {
+               ((TreeCacheSSOClusterManager) mgr).setCacheName(getTreeCacheName());
+// DISABLED UNTIL 4.0.6.CR1
+//               ((TreeCacheSSOClusterManager) mgr).setThreadPoolName(getThreadPoolName());
+            }
+            ssoClusterManager = mgr;
+            clusterManagerClass = className;
+         }
+         catch (Throwable t)
+         {
+            throw new LifecycleException("Cannot create " +
+               "SSOClusterManager using " +
+               className, t);
+         }
+
+         if (started)
+         {
+            ssoClusterManager.start();
+         }
+      }
+   }
+   
+   
+   private void processExpires()
+   {
+      long now = 0L;
+      synchronized (mutex)
+      {
+         now = System.currentTimeMillis();
+      
+         if (now - lastProcessExpires > processExpiresInterval)
+         {
+            lastProcessExpires = now;
+         }
+         else
+         {
+             return;
+         }
+      }
+      
+      clearExpiredSSOs(now);
+   }
+   
+   private synchronized void clearExpiredSSOs(long now)
+   {      
+      for (Iterator iter = emptySSOs.entrySet().iterator(); iter.hasNext();)
+      {
+         Map.Entry entry = (Map.Entry) iter.next();
+         if ( (now - ((Long) entry.getValue()).longValue()) > maxEmptyLife)
+         {
+            String ssoId = (String) entry.getKey();
+            if (getContainer().getLogger().isDebugEnabled())
+            {
+               getContainer().getLogger().debug("Invalidating expired SSO " + ssoId);
+            }
+            logout(ssoId);
+         }
+      }      
+   }
+   
+   private boolean isValid(String ssoId, SingleSignOnEntry entry)
+   {
+      boolean valid = true;
+      if (entry.getSessionCount() == 0)
+      {
+         Long expired = (Long) emptySSOs.get(ssoId);
+         if (expired != null 
+               && (System.currentTimeMillis() - expired.longValue()) > maxEmptyLife)
+         {
+            valid = false;
+            
+            if (getContainer().getLogger().isDebugEnabled())
+            {
+               getContainer().getLogger().debug("Invalidating expired SSO " + ssoId);
+            }
+            
+            logout(ssoId);
+         }
+      }
+      
+      return valid;
+   }
+
+}
\ No newline at end of file


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,121 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.sso;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.Lifecycle;
+
+/**
+ * Provides communications support between a SingleSignOn valve and other
+ * such valves configured for the same hostname within a server cluster.
+ * <p/>
+ * Implementations of this interface must declare a public no-arguments
+ * constructor.
+ *
+ * @author Brian E. Stansberry
+ * @version $Revision: 57206 $ $Date: 2006-09-26 14:25:30 +0200 (mar., 26 sept. 2006) $
+ * @see ClusteredSingleSignOn
+ */
+public interface SSOClusterManager
+   extends Lifecycle
+{
+
+   /**
+    * Notify the cluster of the addition of a Session to an SSO session.
+    *
+    * @param ssoId   the id of the SSO session
+    * @param session the Session that has been added
+    */
+   void addSession(String ssoId, Session session);
+
+   /**
+    * Gets the SingleSignOn valve for which this object is handling
+    * cluster communications.
+    *
+    * @return the <code>SingleSignOn</code> valve.
+    */
+   ClusteredSingleSignOn getSingleSignOnValve();
+
+   /**
+    * Sets the SingleSignOn valve for which this object is handling
+    * cluster communications.
+    * <p><b>NOTE:</b> This method must be called before calls can be
+    * made to the other methods of this interface.
+    *
+    * @param valve a <code>SingleSignOn</code> valve.
+    */
+   void setSingleSignOnValve(ClusteredSingleSignOn valve);
+
+   /**
+    * Notifies the cluster that a single sign on session has been terminated
+    * due to a user logout.
+    *
+    * @param ssoId the id of the SSO session
+    */
+   void logout(String ssoId);
+
+   /**
+    * Queries the cluster for the existence of a SSO session with the given
+    * id, returning a <code>SingleSignOnEntry</code> if one is found.
+    *
+    * @param ssoId the id of the SSO session
+    * @return a <code>SingleSignOnEntry</code> created using information
+    *         found on another cluster node, or <code>null</code> if no
+    *         entry could be found.
+    */
+   SingleSignOnEntry lookup(String ssoId);
+
+   /**
+    * Notifies the cluster of the creation of a new SSO entry.
+    *
+    * @param ssoId    the id of the SSO session
+    * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+    *                 or FORM) used to authenticate the SSO.
+    * @param username the username (if any) used for the authentication
+    * @param password the password (if any) used for the authentication
+    */
+   void register(String ssoId, String authType, String username,
+      String password);
+
+   /**
+    * Notify the cluster of the removal of a Session from an SSO session.
+    *
+    * @param ssoId   the id of the SSO session
+    * @param session the Session that has been removed
+    */
+   void removeSession(String ssoId, Session session);
+
+   /**
+    * Notifies the cluster of an update of the security credentials
+    * associated with an SSO session.
+    *
+    * @param ssoId    the id of the SSO session
+    * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+    *                 or FORM) used to authenticate the SSO.
+    * @param username the username (if any) used for the authentication
+    * @param password the password (if any) used for the authentication
+    */
+   void updateCredentials(String ssoId, String authType, String username,
+      String password);
+
+
+}


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,264 @@
+/*
+ * 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.jboss.web.tomcat.service.sso;
+
+import java.security.Principal;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.authenticator.SingleSignOn;
+
+/**
+ * A class that represents entries in the cache of authenticated users.
+ *
+ * @author Brian E. Stansberry, based on work by Craig R. McClanahan
+ * @version $Revision: 41072 $ $Date: 2006-02-09 08:47:17 +0100 (jeu., 09 févr. 2006) $
+ * @see SingleSignOn
+ */
+class SingleSignOnEntry
+{
+   // ------------------------------------------------------  Instance Fields
+
+   private String authType = null;
+
+   private String password = null;
+
+   private Principal principal = null;
+
+   private Session sessions[] = new Session[0];
+
+   private String username = null;
+
+   private 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
+    */
+   SingleSignOnEntry(Principal principal, String authType,
+      String username, String password)
+   {
+      updateCredentials(principal, authType, username, password);
+   }
+
+   // ------------------------------------------------------- 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.
+    * @return <code>true</code> if the given Session was a new addition (i.e.
+    *         was not previously associated with this entry);
+    *         <code>false</code> otherwise.
+    */
+   synchronized boolean addSession(SingleSignOn sso, Session session)
+   {
+      for (int i = 0; i < sessions.length; i++)
+      {
+         if (session == sessions[i])
+            return false;
+      }
+      Session results[] = new Session[sessions.length + 1];
+      System.arraycopy(sessions, 0, results, 0, sessions.length);
+      results[sessions.length] = session;
+      sessions = results;
+      session.addSessionListener(sso);
+      return true;
+   }
+
+   /**
+    * Removes the given <code>Session</code> from the list of those
+    * associated with this SSO.
+    *
+    * @param session the <code>Session</code> to remove.
+    * @return <code>true</code> if the given Session needed to be removed
+    *         (i.e. was in fact previously associated with this entry);
+    *         <code>false</code> otherwise.
+    */
+   synchronized boolean removeSession(Session session)
+   {
+      if (sessions.length == 0)
+         return false;
+
+      boolean removed = false;
+      Session[] nsessions = new Session[sessions.length - 1];
+      for (int i = 0, j = 0; i < sessions.length; i++)
+      {
+         if (session == sessions[i])
+         {
+            removed = true;
+            continue;
+         }
+         else if (!removed && i == nsessions.length)
+         {
+            // We have tested all our sessions, and have not had to
+            // remove any; break loop now so we don't cause an
+            // ArrayIndexOutOfBounds on nsessions
+            break;
+         }
+         nsessions[j++] = sessions[i];
+      }
+      sessions = nsessions;
+      // Only if we removed a session, do we replace our session list
+      if (removed)
+         sessions = nsessions;
+      return removed;
+   }
+
+   /**
+    * Returns the <code>Session</code>s associated with this SSO.
+    */
+   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"
+    */
+   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.
+    */
+   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.
+    */
+   String getPassword()
+   {
+      return (this.password);
+   }
+
+   /**
+    * Gets the <code>Principal</code> that has been authenticated by
+    * the SSO.
+    * <p/>
+    * <b>NOTE: </b> May return <code>null</code> if this object was
+    * retrieved via a lookup from another node in a cluster. Interface
+    * <code>Principal</code> does not extend <code>Serializable</code>,
+    * so a <code>SingleSignOnEntry</code>'s principal member cannot be
+    * serialized as part of SSO management in a cluster.  A
+    * <code>Principal</code> cannot be bound to a
+    * <code>SingleSignOnEntry</code> until the SSO has been authenticated
+    * by the local node.
+    *
+    * @return The <code>Principal</code> that has been authenticated by
+    *         the local SSO, or <code>null</code> if no authentication
+    *         has been performed yet in this cluster node.
+    */
+   Principal getPrincipal()
+   {
+      return (this.principal);
+   }
+
+   /**
+    * Sets the <code>Principal</code> that has been authenticated by
+    * the SSO.
+    */
+   void setPrincipal(Principal principal)
+   {
+      this.principal = principal;
+   }
+
+   /**
+    * Returns the number of sessions associated with this SSO, either
+    * locally or remotely.
+    */
+   int getSessionCount()
+   {
+      return (sessions.length);
+   }
+
+   /**
+    * Gets the username provided by the user as part of the authentication
+    * process.
+    */
+   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
+    */
+   synchronized boolean updateCredentials(Principal principal, String authType,
+      String username, String password)
+   {
+
+      boolean changed =
+         (safeEquals(this.principal, principal)
+         || safeEquals(this.authType, authType)
+         || safeEquals(this.username, username)
+         || safeEquals(this.password, 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));
+      return changed;
+   }
+
+   // -------------------------------------------------------  Private Methods
+
+   private boolean safeEquals(Object a, Object b)
+   {
+      return ((a == b)
+         || (a != null && a.equals(b))
+         || (b != null && b.equals(a)));
+   }
+}
\ No newline at end of file


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1547 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.sso;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.management.AttributeNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Session;
+import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.cache.AbstractTreeCacheListener;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.TreeCacheListener;
+import org.jboss.cache.config.Option;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.util.NestedRuntimeException;
+import org.jboss.util.threadpool.ThreadPool;
+import org.jboss.util.threadpool.ThreadPoolMBean;
+import org.jboss.web.tomcat.service.JBossWeb;
+import org.jgroups.Address;
+import org.jgroups.View;
+
+/**
+ * An implementation of SSOClusterManager that uses a TreeCache
+ * to share SSO information between cluster nodes.
+ *
+ * @author Brian E. Stansberry
+ * @version $Revision: 57330 $ $Date: 2006-10-02 00:36:06 +0200 (lun., 02 oct. 2006) $
+ */
+public final class TreeCacheSSOClusterManager
+   extends AbstractTreeCacheListener
+   implements SSOClusterManager
+{
+   // -------------------------------------------------------------  Constants
+
+   /**
+    * Final segment of any FQN that names a TreeCache node storing
+    * SSO credential information.
+    */
+   private static final String CREDENTIALS = "credentials";
+
+   /**
+    * First segment of any FQN that names a TreeCache node associated
+    * with an SSO
+    */
+   private static final String SSO = "SSO";
+
+   /**
+    * Final segment of any FQN that names a TreeCache node storing
+    * the set of Sessions associated with an SSO.
+    */
+   private static final String SESSIONS = "sessions";
+
+   /**
+    * Key under which data is stored to the TreeCache.
+    */
+   private static final String KEY = "key";
+
+   /**
+    * Default global value for the cacheName property
+    */
+   public static final String DEFAULT_GLOBAL_CACHE_NAME =
+      JBossWeb.DEFAULT_CACHE_NAME;
+
+   /**
+    * Parameter signature used for TreeCache.get calls over JMX
+    */
+   private static final String[] GET_SIGNATURE =
+      {Fqn.class.getName(), Object.class.getName(), Option.class.getName()};
+
+   /**
+    * Parameter signature used for TreeCache.put calls over JMX
+    */
+   private static final String[] PUT_SIGNATURE =
+      {Fqn.class.getName(), Object.class.getName(), 
+       Object.class.getName(), Option.class.getName()};
+
+   /**
+    * Parameter signature used for TreeCache.remove calls over JMX
+    */
+   private static final String[] REMOVE_SIGNATURE = 
+         {Fqn.class.getName(), Option.class.getName()};
+   
+   private static final Option GRAVITATE_OPTION = new Option();
+   private static final Option LOCAL_OPTION = new Option();
+   
+   static
+   {
+      GRAVITATE_OPTION.setForceDataGravitation(true);
+      LOCAL_OPTION.setCacheModeLocal(true);
+   }
+   
+   
+   // -------------------------------------------------------  Instance Fields
+   
+   /**
+    * SSO id which the thread is currently storing to the cache
+    */
+   private ThreadLocal beingLocallyAdded = new ThreadLocal();
+
+   /**
+    * SSO id which a thread is currently removing from the cache
+    */
+   private ThreadLocal beingLocallyRemoved = new ThreadLocal();
+
+   /**
+    * SSO id which the thread is deregistering due to removal on another node
+    */
+   private ThreadLocal beingRemotelyRemoved = new ThreadLocal();
+
+   /**
+    * ObjectName of the TreeCache
+    */
+   private ObjectName cacheObjectName = null;
+   
+// DISABLED UNTIL 4.0.6.CR1
+//   private String threadPoolName = "jboss.system:service=ThreadPool";
+//
+//   private ThreadPool threadPool;
+   
+   /**
+    * String version of the object name to use to access the TreeCache
+    */
+   private String cacheName = null;
+
+   /**
+    * Transaction Manager
+    */
+   private TransactionManager tm = null;
+   
+   /**
+    * The lifecycle event support for this component.
+    */
+   private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+   /**
+    * The Log-object for this class
+    */
+   private Logger log = Logger.getLogger(getClass().getName());;
+
+   /**
+    * Whether we are registered as a TreeCacheListener anywhere
+    */
+   private boolean registeredAsListener = false;
+
+   /**
+    * The MBean server we use to access our TreeCache
+    */
+   private MBeanServer server = null;
+
+   /**
+    * The SingleSignOn for which we are providing cluster support
+    */
+   private ClusteredSingleSignOn ssoValve = null;
+
+   /**
+    * Whether we have been started
+    */
+   private boolean started = false;
+
+   /**
+    * Whether a valid TreeCache is available for use
+    */
+   private boolean treeCacheAvailable = false;
+
+   /**
+    * Whether we have logged an error due to not having a valid cache
+    */
+   private boolean missingCacheErrorLogged = false;
+   
+   /**
+    * Our node's address in the cluster.
+    */
+   private Serializable localAddress = null;
+   
+// DISABLED UNTIL 4.0.6.CR1
+//   /** The last view passed to viewAccepted() */
+//   private View currentView;
+//   
+//   /** Mutex lock to ensure only one view change at a time is being processed */
+//   private Object cleanupMutex = new Object();
+   
+   // ----------------------------------------------------------  Constructors
+
+   
+   /**
+    * Creates a new TreeCacheSSOClusterManager
+    */
+   public TreeCacheSSOClusterManager()
+   {
+      // Find our MBeanServer
+      server = MBeanServerLocator.locateJBoss();
+      if (server == null)
+         server = MBeanServerLocator.locate();
+   }
+
+   
+   /**
+    * Creates a new TreeCacheSSOClusterManager that works with the given
+    * MBeanServer.  This constructor is only intended for use in unit testing.
+    */
+   public TreeCacheSSOClusterManager(MBeanServer server)
+   {
+      this.server = server;
+   }
+   
+   
+   // ------------------------------------------------------------  Properties
+  
+   public String getCacheName()
+   {
+      return cacheName;
+   }
+
+   public void setCacheName(String objectName)
+      throws Exception
+   {
+      if (objectName == null)
+      {
+         setCacheObjectName(null);
+      }
+      else if (objectName.equals(cacheName) == false)
+      {
+         setCacheObjectName(new ObjectName(objectName));
+      }
+   }
+
+   public ObjectName getCacheObjectName()
+   {
+      return cacheObjectName;
+   }
+
+   public void setCacheObjectName(ObjectName objectName)
+      throws Exception
+   {
+      // If no change, do nothing
+      if ((objectName != null && objectName.equals(cacheObjectName))
+         || (cacheObjectName != null && cacheObjectName.equals(objectName))
+         || (objectName == null && cacheObjectName == null))
+      {
+         return;
+      }
+
+      removeAsTreeCacheListener();
+      this.tm = null;
+      
+      this.cacheObjectName = objectName;
+      this.cacheName = (objectName == null
+         ? null
+         : objectName.getCanonicalName());
+
+      if (false == isTreeCacheAvailable(true))
+      {
+         if (started)
+         {
+            logMissingCacheError();
+         }
+         else
+         {
+            // Just put an advice in the log
+            log.info("Cannot find TreeCache using " + cacheName + " -- tree" +
+               "CacheName must be set to point to a running TreeCache " +
+               "before ClusteredSingleSignOn can handle requests");
+         }
+      }
+   }
+   
+// DISABLED UNTIL 4.0.6.CR1
+//   public String getThreadPoolName()
+//   {
+//      return threadPoolName;
+//   }
+//
+//
+//   public void setThreadPoolName(String threadPoolName)
+//   {
+//      if (started)
+//      {
+//         log.info("Call to setThreadPoolName() ignored; already started");
+//      }
+//      else
+//      {
+//         this.threadPoolName = threadPoolName;
+//      }
+//   }
+   
+   // -----------------------------------------------------  SSOClusterManager
+
+
+   /**
+    * Notify the cluster of the addition of a Session to an SSO session.
+    *
+    * @param ssoId   the id of the SSO session
+    * @param session the Session that has been added
+    */
+   public void addSession(String ssoId, Session session)
+   {
+      if (ssoId == null || session == null)
+      {
+         return;
+      }
+
+      if (!checkTreeCacheAvailable())
+      {
+         return;
+      }
+
+      if (log.isTraceEnabled())
+      {
+         log.trace("addSession(): adding Session " + session.getId() +
+            " to cached session set for SSO " + ssoId);
+      }
+
+      Fqn fqn = getSessionsFqn(ssoId);
+      boolean doTx = false;
+      try
+      {
+         // Confirm we have a transaction manager; if not get it from TreeCache
+         // failure to find will throw an IllegalStateException
+         if (tm == null)
+            configureFromCache();
+         
+         // Don't do anything if there is already a transaction 
+         // context associated with this thread.
+         if(tm.getTransaction() == null)
+            doTx = true;
+
+         if(doTx)
+            tm.begin();
+         
+         Set sessions = getSessionSet(fqn, true, false);
+         sessions.add(new SessionAddress(session.getId(), localAddress));
+         putInTreeCache(fqn, sessions, false);
+      }
+      catch (Exception e)
+      {
+         try
+         {
+            if(doTx)
+               tm.setRollbackOnly();
+         }
+         catch (Exception ignored)
+         {
+         }
+         String sessId = (session == null ? "NULL" : session.getId());
+         log.error("caught exception adding session " + sessId +
+            " to SSO id " + ssoId, e);
+      }
+      finally
+      {
+         if (doTx)
+            endTransaction();
+      }
+   }
+
+
+   /**
+    * Gets the SingleSignOn valve for which this object is handling
+    * cluster communications.
+    *
+    * @return the <code>SingleSignOn</code> valve.
+    */
+   public ClusteredSingleSignOn getSingleSignOnValve()
+   {
+      return ssoValve;
+   }
+
+
+   /**
+    * Sets the SingleSignOn valve for which this object is handling
+    * cluster communications.
+    * <p><b>NOTE:</b> This method must be called before calls can be
+    * made to the other methods of this interface.
+    *
+    * @param valve a <code>SingleSignOn</code> valve.
+    */
+   public void setSingleSignOnValve(ClusteredSingleSignOn valve)
+   {
+      ssoValve = valve;
+   }
+
+
+   /**
+    * Notifies the cluster that a single sign on session has been terminated
+    * due to a user logout.
+    *
+    * @param ssoId
+    */
+   public void logout(String ssoId)
+   {
+      if (!checkTreeCacheAvailable())
+      {
+         return;
+      }
+      
+      // Check whether we are already handling this removal 
+      if (ssoId.equals(beingLocallyRemoved.get()))
+      {
+         return;
+      }         
+      
+      // Add this SSO to our list of in-process local removals so
+      // this.nodeRemoved() will ignore the removal
+      beingLocallyRemoved.set(ssoId);
+
+      if (log.isTraceEnabled())
+      {
+         log.trace("Registering logout of SSO " + ssoId +
+            " in clustered cache");
+      }
+
+      Fqn fqn = getSingleSignOnFqn(ssoId);
+      
+      try
+      {
+         removeFromTreeCache(fqn);
+      }
+      catch (Exception e)
+      {
+         log.error("Exception attempting to remove node " +
+            fqn.toString() + " from TreeCache", e);
+      }
+      finally
+      {
+         beingLocallyRemoved.set(null);
+      }
+   }
+
+
+   /**
+    * Queries the cluster for the existence of an SSO session with the given
+    * id, returning a <code>SingleSignOnEntry</code> if one is found.
+    *
+    * @param ssoId the id of the SSO session
+    * @return a <code>SingleSignOnEntry</code> created using information
+    *         found on another cluster node, or <code>null</code> if no
+    *         entry could be found.
+    */
+   public SingleSignOnEntry lookup(String ssoId)
+   {
+      if (!checkTreeCacheAvailable())
+      {
+         return null;
+      }
+
+      SingleSignOnEntry entry = null;
+      // Find the latest credential info from the cluster
+      Fqn fqn = getCredentialsFqn(ssoId);
+      try
+      {
+         SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn, false);
+         if (data != null)
+         {
+            entry = new SingleSignOnEntry(null,
+               data.getAuthType(),
+               data.getUsername(),
+               data.getPassword());
+         }
+      }
+      catch (Exception e)
+      {
+         log.error("caught exception looking up SSOCredentials for SSO id " +
+            ssoId, e);
+      }
+      return entry;
+   }
+
+
+   /**
+    * Notifies the cluster of the creation of a new SSO entry.
+    *
+    * @param ssoId    the id of the SSO session
+    * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+    *                 or FORM) used to authenticate the SSO.
+    * @param username the username (if any) used for the authentication
+    * @param password the password (if any) used for the authentication
+    */
+   public void register(String ssoId, String authType,
+      String username, String password)
+   {
+      if (!checkTreeCacheAvailable())
+      {
+         return;
+      }
+
+      if (log.isTraceEnabled())
+      {
+         log.trace("Registering SSO " + ssoId + " in clustered cache");
+      }
+
+      storeSSOData(ssoId, authType, username, password);
+   }
+
+
+   /**
+    * Notify the cluster of the removal of a Session from an SSO session.
+    *
+    * @param ssoId   the id of the SSO session
+    * @param session the Session that has been removed
+    */
+   public void removeSession(String ssoId, Session session)
+   {
+      if (ssoId == null || session == null)
+      {
+         return;
+      }
+      
+      if (!checkTreeCacheAvailable())
+      {
+         return;
+      }
+      
+      // Check that this session removal is not due to our own deregistration
+      // of an SSO following receipt of a nodeRemoved() call
+      if (ssoId.equals(beingRemotelyRemoved.get()))
+      {
+         return;
+      }
+      
+      Fqn fqn = getSessionsFqn(ssoId);
+      boolean doTx = false;
+//      boolean removing = false;
+      try
+      {
+         // Confirm we have a transaction manager; if not get it from TreeCache
+         // failure to find will throw an IllegalStateException
+         if (tm == null)
+            configureFromCache();
+
+         // Don't do anything if there is already a transaction 
+         // context associated with this thread.
+         if(tm.getTransaction() == null)
+            doTx = true;
+
+         if(doTx)
+            tm.begin();
+         
+         Set sessions = getSessionSet(fqn, false, false);
+         if (sessions != null)
+         {
+            sessions.remove(new SessionAddress(session.getId(), localAddress));
+
+            if (log.isTraceEnabled())
+            {
+               log.trace("removed session " + session.getId() +
+                  " from cached session set for SSO " + ssoId + " -- " +
+                  sessions.size() + " sessions remain");
+            }
+            
+//            if (sessions.size() == 0)
+//            {               
+//               // No sessions left; remove node
+//               
+//               // Add this SSO to our list of in-process local removals so
+//               // this.nodeRemoved() will ignore the removal
+//               removing = true;
+//               beingLocallyRemoved.set(ssoId);
+//               removeFromTreeCache(getSingleSignOnFqn(ssoId));
+//            }
+//            else
+//            {
+               putInTreeCache(fqn, sessions, false);
+//            }
+         }
+      }
+      catch (Exception e)
+      {
+         try
+         {
+            if(doTx)
+               tm.setRollbackOnly();
+         }
+         catch (Exception x)
+         {
+         }
+         
+         String sessId = (session == null ? "NULL" : session.getId());
+         log.error("caught exception removing session " + sessId +
+            " from SSO id " + ssoId, e);
+      }
+      finally
+      {
+//         try
+//         {
+//            if (removing)
+//            {
+//               beingLocallyRemoved.set(null);
+//            }
+//         }
+//         finally
+//         {
+            if (doTx)
+               endTransaction();
+//         }
+      }
+   }
+
+
+   /**
+    * Notifies the cluster of an update of the security credentials
+    * associated with an SSO session.
+    *
+    * @param ssoId    the id of the SSO session
+    * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+    *                 or FORM) used to authenticate the SSO.
+    * @param username the username (if any) used for the authentication
+    * @param password the password (if any) used for the authentication
+    */
+   public void updateCredentials(String ssoId, String authType,
+      String username, String password)
+   {
+      if (!checkTreeCacheAvailable())
+      {
+         return;
+      }
+
+      if (log.isTraceEnabled())
+      {
+         log.trace("Updating credentials for SSO " + ssoId +
+            " in clustered cache");
+      }
+
+      storeSSOData(ssoId, authType, username, password);
+   }
+
+   
+   // ------------------------------------------------------  TreeCacheListener
+
+   /**
+    * Extracts an SSO session id from the Fqn and uses it in an invocation of
+    * {@link ClusteredSingleSignOn#deregister(String) ClusteredSingleSignOn.deregister(String)}.
+    * <p/>
+    * Ignores invocations resulting from TreeCache changes originated by
+    * this object.
+    *
+    * @param fqn the fully-qualified name of the node that was removed
+    */
+   public void nodeRemoved(Fqn fqn)
+   {
+      String ssoId = getIdFromFqn(fqn);
+      
+      if (ssoId == null)
+         return;
+
+      // Ignore messages generated by our own activity
+      if (ssoId.equals(beingLocallyRemoved.get()))
+      {
+         return;
+      }
+      
+      beingRemotelyRemoved.set(ssoId);
+
+      try
+      {
+         if (log.isTraceEnabled())
+         {
+            log.trace("received a node removed message for SSO " + ssoId);
+         }
+
+         ssoValve.deregister(ssoId);
+      }
+      finally
+      {
+         beingRemotelyRemoved.set(null);
+      }
+
+   }
+
+
+   /**
+    * Extracts an SSO session id from the Fqn and uses it in an invocation of
+    * {@link ClusteredSingleSignOn#update ClusteredSingleSignOn.update()}.
+    * <p/>
+    * Only responds to modifications of nodes whose FQN's final segment is
+    * "credentials".
+    * <p/>
+    * Ignores invocations resulting from TreeCache changes originated by
+    * this object.
+    * <p/>
+    * Ignores invocations for SSO session id's that are not registered
+    * with the local SingleSignOn valve.
+    *
+    * @param fqn the fully-qualified name of the node that was modified
+    */
+   public void nodeModified(Fqn fqn)
+   {
+      String type = getTypeFromFqn(fqn);
+      if (CREDENTIALS.equals(type))
+      {
+         handleCredentialUpdate(fqn);
+      }
+      else if (SESSIONS.equals(type))
+      {
+         handleSessionSetChange(fqn);
+      }
+   }
+
+
+   /**
+    *  
+    * @param fqn an Fqn that points to the CREDENTIALS node of an SSO
+    */
+   private void handleCredentialUpdate(Fqn fqn)
+   {
+      String ssoId = getIdFromFqn(fqn); // won't be null per the API contract
+
+      // Ignore invocations that come as a result of our additions
+      if (ssoId.equals(beingLocallyAdded.get()))
+      {
+         return;
+      }
+
+      SingleSignOnEntry sso = ssoValve.localLookup(ssoId);
+      if (sso == null || sso.getCanReauthenticate())
+      {
+         // No reason to update
+         return;
+      }
+
+      if (log.isTraceEnabled())
+      {
+         log.trace("received a credentials modified message for SSO " + ssoId);
+      }
+
+      // Put this SSO in the queue of those to be updated
+//      credentialUpdater.enqueue(sso, ssoId);
+      try
+      {
+         SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn, false);
+         if (data != null)
+         {
+            // We want to release our read lock quickly, so get the needed
+            // data from the cache, commit the tx, and then use the data
+            String authType = data.getAuthType();
+            String username = data.getUsername();
+            String password = data.getPassword();
+
+            if (log.isTraceEnabled())
+            {
+               log.trace("CredentialUpdater: Updating credentials for SSO " + sso);
+            }
+
+            synchronized (sso)
+            {
+               // Use the existing principal
+               Principal p = sso.getPrincipal();
+               sso.updateCredentials(p, authType, username, password);
+            }
+         }
+      }
+      catch (Exception e)
+      {
+         log.error("failed to update credentials for SSO " + ssoId, e);
+      }
+   }
+   
+   /**
+    *  
+    * @param fqn an Fqn that points to the SESSIONS node of an SSO
+    */
+   private void handleSessionSetChange(Fqn fqn)
+   {
+      // Here we *want* to handle local activity as well as remote,
+      // as the distributed cache set of sessions is the only
+      // complete representation
+      
+      String ssoId = getIdFromFqn(fqn);
+      try      
+      {
+         Set sessions = (Set) getFromTreeCache(fqn, false);
+         if (sessions == null)
+         {
+            // don't think we'd get this event; 
+            // in any case we don't deal with this
+         }
+         else if (sessions.size() == 0)
+         {
+            ssoValve.notifySSOEmpty(ssoId);            
+         }
+         else
+         {
+            ssoValve.notifySSONotEmpty(ssoId);
+         }
+      }
+      catch (Exception e)
+      {
+         log.error("failed in check for empty SSO " + ssoId, e);
+      }
+   }
+
+// DISABLE UNTIL 4.0.6.CR1   
+//   /**
+//    * If any nodes have been removed from the view, asynchronously scans
+//    * all SSOs looking for and removing sessions owned by the removed node.
+//    * Notifies the SSO valve if as a result any SSOs no longer have active
+//    * sessions.  If the removed node is the one associated with this object,
+//    * does nothing.
+//    */
+//   public void viewChange(View new_view)
+//   {
+//      if (currentView == null)
+//      {
+//         currentView = new_view;
+//         return;
+//      }
+//      
+//      Vector oldMembers = currentView.getMembers();
+//      
+//      currentView = new_view;
+//      
+//      // If we're not in the view, just exit
+//      if (localAddress == null || !currentView.containsMember((Address) localAddress))
+//         return;
+//      
+//      boolean hasDeadMembers = false;
+//      for (Iterator iter = oldMembers.iterator(); iter.hasNext(); )
+//      {
+//         Address member = (Address) iter.next();
+//         if (!new_view.containsMember(member))
+//         {
+//            hasDeadMembers = true;
+//            break;
+//         }
+//      }
+//      
+//      if (hasDeadMembers)
+//      {
+//         DeadMemberCleaner cleaner = new DeadMemberCleaner();
+//         if (threadPool != null)
+//         {
+//            threadPool.run(cleaner);
+//         }
+//         else
+//         {
+//            Thread t = new Thread(cleaner, "ClusteredSSOCleaner");
+//            t.setDaemon(true);
+//            t.start();
+//         }
+//      }
+//         
+//   }   
+   
+   // -------------------------------------------------------------  Lifecycle
+
+
+   /**
+    * 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.
+    *
+    * @throws 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
+            ("TreeCacheSSOClusterManager already Started");
+      }
+
+      try 
+      {
+         if (isTreeCacheAvailable(true))
+         {
+            integrateWithCache();
+         }
+      }
+      catch (Exception e)
+      {
+         throw new LifecycleException("Caught exception looking up " +
+                                      "TransactionManager from TreeCache", e);
+      }
+      
+//    DISABLED UNTIL 4.0.6.CR1
+//      initThreadPool();
+      
+      started = true;
+
+      // Notify our interested LifecycleListeners
+      lifecycle.fireLifecycleEvent(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.
+    *
+    * @throws 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
+            ("TreeCacheSSOClusterManager not Started");
+      }
+      
+      started = false;
+
+      // Notify our interested LifecycleListeners
+      lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+   }
+
+   
+   // -------------------------------------------------------  Private Methods
+
+   private Object getFromTreeCache(Fqn fqn, boolean localOnly) throws Exception
+   {
+      Object[] args = new Object[]{fqn, KEY, localOnly ? LOCAL_OPTION : GRAVITATE_OPTION};
+      return server.invoke(getCacheObjectName(), "get", args, GET_SIGNATURE);
+   }
+   
+// DISABLED UNTIL 4.0.6.CR1
+//   private Set getSSOIds() throws Exception
+//   {
+//      Fqn ssoRoot = new Fqn(new Object[] {SSO});
+//      return (Set) server.invoke(getCacheObjectName(), "getChildrenNames", 
+//                                 new Object[]{ ssoRoot }, 
+//                                 new String[]{ Fqn.class.getName() });
+//   }
+
+   private Fqn getCredentialsFqn(String ssoid)
+   {
+      Object[] objs = new Object[]{SSO, ssoid, CREDENTIALS};
+      return new Fqn(objs);
+   }
+
+   private Fqn getSessionsFqn(String ssoid)
+   {
+      Object[] objs = new Object[]{SSO, ssoid, SESSIONS};
+      return new Fqn(objs);
+   }
+
+   private Fqn getSingleSignOnFqn(String ssoid)
+   {
+      Object[] objs = new Object[]{SSO, ssoid};
+      return new Fqn(objs);
+   }
+
+   /**
+    * Extracts an SSO session id from a fully qualified name object.
+    *
+    * @param fqn the Fully Qualified Name used by TreeCache
+    * @return the second element in the Fqn -- the SSO session id
+    */
+   private String getIdFromFqn(Fqn fqn)
+   {
+      String id = null;
+      if (fqn.size() > 1 && SSO.equals(fqn.get(0)))
+      {
+         id = (String) fqn.get(1);
+      }
+      return id;
+   }
+
+   private Set getSessionSet(Fqn fqn, boolean create, boolean localOnly)
+      throws Exception
+   {
+      Set sessions = (Set) getFromTreeCache(fqn, localOnly);
+      if (create && sessions == null)
+      {
+         sessions = new HashSet();
+      }
+      return sessions;
+   }
+
+   /**
+    * Extracts the SSO tree cache node type from a fully qualified name
+    * object.
+    *
+    * @param fqn the Fully Qualified Name used by TreeCache
+    * @return the 3rd in the Fqn -- either
+    *         {@link #CREDENTIALS CREDENTIALS} or {@link #SESSIONS SESSIONS},
+    *         or <code>null</code> if <code>fqn</code> is not for an SSO.
+    */
+   private String getTypeFromFqn(Fqn fqn)
+   {
+      String type = null;
+      if (fqn.size() > 2 && SSO.equals(fqn.get(0)))
+         type = (String) fqn.get(2);
+      return type;
+   }
+   
+   /**
+    * Obtains needed configuration information from the tree cache.
+    * Invokes "getTransactionManager" on the tree cache, caching the
+    * result or throwing an IllegalStateException if one is not found.
+    * Also get our cluster-wide unique local address from the cache.
+    * 
+    * @throws Exception
+    */
+   private void configureFromCache() throws Exception
+   {  
+      tm = (TransactionManager) server.getAttribute(getCacheObjectName(), 
+                                                    "TransactionManager");
+
+      if (tm == null) 
+      {
+         throw new IllegalStateException("TreeCache does not have a " +
+                                         "transaction manager; please " +
+                                         "configure a valid " +
+                                         "TransactionManagerLookupClass");
+      }
+      
+      // Find out our address
+      Object address = server.getAttribute(cacheObjectName, "LocalAddress");
+      // In reality this is a JGroups IpAddress, but the API says
+      // "Object" so we have to be sure its Serializable
+      if (address instanceof Serializable)
+         localAddress = (Serializable) address;
+      else
+         localAddress = address.toString();
+   }
+   
+
+   private void endTransaction()
+   {
+      try 
+      {
+         if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
+         {
+            tm.commit();
+         } 
+         else
+         {
+            tm.rollback();
+         }
+      } 
+      catch (Exception e) 
+      {
+         log.error(e);
+         throw new NestedRuntimeException("TreeCacheSSOClusterManager.endTransaction(): ", e);
+      }
+   }
+
+   /**
+    * Checks whether an MBean is registered under the value of property
+    * "cacheObjectName".
+    *
+    * @param forceCheck check for availability whether or not it has already
+    *                   been positively established
+    * @return <code>true</code> if property <code>cacheName</code> has been
+    *         set and points to a registered MBean.
+    */
+   private synchronized boolean isTreeCacheAvailable(boolean forceCheck)
+   {
+      if (forceCheck || treeCacheAvailable == false)
+      {
+         boolean available = (cacheObjectName != null);
+         if (available)
+         {
+            Set s = server.queryMBeans(cacheObjectName, null);
+            available = s.size() > 0;
+            if (available)
+            {
+               try
+               {
+                  // If Tomcat5 overrides the default cache name, it will do so
+                  // after we are started. So we need to configure ourself here
+                  // and throw an exception if there is a problem. Having this
+                  // here also allows us to recover if our cache is started
+                  // after we are
+                  if (started)
+                     integrateWithCache();
+                  setMissingCacheErrorLogged(false);
+               }
+               catch (Exception e)
+               {
+                  log.error("Caught exception configuring from cache " +
+                            cacheObjectName, e);
+                  available = false;
+               }
+            }
+         }
+         treeCacheAvailable = available;
+      }
+      return treeCacheAvailable;
+   }
+   
+   private boolean checkTreeCacheAvailable()
+   {
+      boolean avail = isTreeCacheAvailable(false);
+      if (!avail)
+         logMissingCacheError();
+      return avail;
+   }
+
+   private void putInTreeCache(Fqn fqn, Object data, boolean localOnly) throws Exception
+   {
+      Object[] args = new Object[]{fqn, KEY, data, localOnly ? LOCAL_OPTION : GRAVITATE_OPTION};
+      server.invoke(getCacheObjectName(), "put", args, PUT_SIGNATURE);
+   }
+
+   private void integrateWithCache() throws Exception
+   {
+      // Ensure we have a transaction manager and a cluster-wide unique address
+      configureFromCache();
+      
+      // If the SSO region is inactive, activate it
+      activateCacheRegion();
+      
+      registerAsTreeCacheListener();
+      
+      log.debug("Successfully integrated with cache service " + cacheObjectName);
+   }
+
+
+   /**
+    * If we are sharing a cache with HttpSession replication, the SSO
+    * region may not be active, so here we ensure it is.
+    * 
+    * @throws Exception
+    */
+   private void activateCacheRegion() throws Exception
+   {
+      // NOTE: to be compatible with 1.2.3, no direct use of the 1.2.4
+      // region activation API can be used here even if the rest of this
+      // class is converted to using MBean proxies
+      try
+      {
+         Boolean inactive = (Boolean) server.getAttribute(cacheObjectName, "InactiveOnStartup");
+         if (inactive.booleanValue())
+         {
+            Boolean useMarshalling = (Boolean) server.getAttribute(cacheObjectName, "UseMarshalling");
+            if (useMarshalling.booleanValue())
+            {
+               // TODO replace this try/catch with a call to an isRegionActive API
+               try
+               {
+                  server.invoke(cacheObjectName, "activateRegion",
+                                new Object[]{ "/" + SSO },
+                                new String[]{ "java.lang.String"});
+               }
+               catch (MBeanException e)
+               {
+                  Exception cause = e.getTargetException();
+                  if (cause != null 
+                        && "org.jboss.cache.RegionNotEmptyException".equals(cause.getClass().getName()))
+                  {
+                     log.debug(SSO + " region already active", cause);
+                  }
+                  else
+                     throw e;
+               }
+            }
+         }
+      }
+      catch (AttributeNotFoundException ignore)
+      {
+         log.debug("Attribute InactiveOnStartup not available; " +
+                   "must be using JBossCache 1.2.3 or earlier");
+      }
+   }
+
+   /**
+    * Invokes an operation on the JMX server to register ourself as a
+    * listener on the TreeCache service.
+    *
+    * @throws Exception
+    */
+   private void registerAsTreeCacheListener() throws Exception
+   {
+      server.invoke(cacheObjectName, "addTreeCacheListener",
+         new Object[]{this},
+         new String[]{TreeCacheListener.class.getName()});
+      registeredAsListener = true;
+   }
+
+
+   /**
+    * Invokes an operation on the JMX server to register ourself as a
+    * listener on the TreeCache service.
+    *
+    * @throws Exception
+    */
+   private void removeAsTreeCacheListener() throws Exception
+   {
+      if (registeredAsListener && cacheObjectName != null)
+      {
+         server.invoke(cacheObjectName, "removeTreeCacheListener",
+            new Object[]{this},
+            new String[]{TreeCacheListener.class.getName()});
+      }
+   }
+
+   private void removeFromTreeCache(Fqn fqn) throws Exception
+   {
+      server.invoke(getCacheObjectName(), "remove",
+         new Object[]{fqn, GRAVITATE_OPTION},
+         REMOVE_SIGNATURE);
+   }
+   
+// DISABLED UNTIL 4.0.6.CR1   
+//   private void initThreadPool()
+//   {      
+//      if (threadPoolName != null)
+//      {
+//         try
+//         {
+//            ObjectName on = new ObjectName(threadPoolName);
+//            threadPool = (ThreadPool) MBeanProxyExt.create(ThreadPoolMBean.class, on);
+//         }
+//         catch (Exception e)
+//         {
+//            log.info("Unable to access ThreadPool at " + threadPoolName + 
+//                     " -- will use individual threads for cleanup work");
+//         }
+//      }
+//   }
+
+   /**
+    * Stores the given data to the clustered cache in a tree branch whose FQN
+    * is the given SSO id.  Stores the given credential data in a child node
+    * named "credentials".  If parameter <code>storeSessions</code> is
+    * <code>true</code>, also stores an empty HashSet in a sibling node
+    * named "sessions".  This HashSet will later be used to hold session ids
+    * associated with the SSO.
+    * <p/>
+    * Any items stored are stored under the key "key".
+    *
+    * @param ssoId    the id of the SSO session
+    * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+    *                 or FORM) used to authenticate the SSO.
+    * @param username the username (if any) used for the authentication
+    * @param password the password (if any) used for the authentication
+    */
+   private void storeSSOData(String ssoId, String authType, String username,
+      String password)
+   {
+      SSOCredentials data = new SSOCredentials(authType, username, password);
+      
+      // Add this SSO to our list of in-process local adds so
+      // this.nodeModified() will ignore the addition
+      beingLocallyAdded.set(ssoId);
+      
+      try
+      {
+         putInTreeCache(getCredentialsFqn(ssoId), data, false);
+      }
+      catch (Exception e)
+      {
+         log.error("Exception attempting to add TreeCache nodes for SSO " +
+            ssoId, e);
+      }
+      finally
+      {
+         beingLocallyAdded.set(null);
+      }
+   }
+
+   private boolean isMissingCacheErrorLogged()
+   {
+      return missingCacheErrorLogged;
+   }
+
+   private void setMissingCacheErrorLogged(boolean missingCacheErrorLogged)
+   {
+      this.missingCacheErrorLogged = missingCacheErrorLogged;
+   }
+
+   private void logMissingCacheError()
+   {
+      StringBuffer msg = new StringBuffer("Cannot find TreeCache using ");
+      msg.append(getCacheName());
+      msg.append(" -- TreeCache must be started before ClusteredSingleSignOn ");
+      msg.append("can handle requests");
+
+      if (isMissingCacheErrorLogged())
+      {
+         // Just log it as a warning
+         log.warn(msg);
+      }
+      else
+      {
+         log.error(msg);
+         // Set a flag so we don't relog this error over and over
+         setMissingCacheErrorLogged(true);
+      }
+   }
+
+   // ---------------------------------------------------------  Outer Classes
+
+   /**
+    * Private class used to store authentication credentials in the TreeCache.
+    * <p/>
+    * For security, password accessor is private.
+    */
+   public static class SSOCredentials
+      implements Serializable
+   {
+      /** The serialVersionUID */
+      private static final long serialVersionUID = 5704877226920571663L;
+      
+      private String authType = null;
+      private String password = null;
+      private String username = null;
+
+      /**
+       * Creates a new SSOCredentials.
+       *
+       * @param authType The authorization method used to authorize the
+       *                 SSO (BASIC, CLIENT-CERT, DIGEST, FORM or NONE).
+       * @param username The username of the user associated with the SSO
+       * @param password The password of the user associated with the SSO
+       */
+      private SSOCredentials(String authType, String username, String password)
+      {
+         this.authType = authType;
+         this.username = username;
+         this.password = password;
+      }
+
+      /**
+       * Gets the username of the user associated with the SSO.
+       *
+       * @return the username
+       */
+      public String getUsername()
+      {
+         return username;
+      }
+
+      /**
+       * Gets the authorization method used to authorize the SSO.
+       *
+       * @return "BASIC", "CLIENT-CERT", "DIGEST" or "FORM"
+       */
+      public String getAuthType()
+      {
+         return authType;
+      }
+
+      /**
+       * Gets the password of the user associated with the SSO.
+       *
+       * @return the password, or <code>null</code> if the authorization
+       *         type was DIGEST or CLIENT-CERT.
+       */
+      private String getPassword()
+      {
+         return password;
+      }
+
+   } // end SSOCredentials
+   
+   static class SessionAddress implements Serializable
+   {
+      /** The serialVersionUID */
+      private static final long serialVersionUID = -3702932999380140004L;
+      
+      Serializable address;
+      String sessionId;
+      
+      SessionAddress(String sessionId, Serializable address)
+      {
+         this.sessionId = sessionId;
+         this.address   = address;
+      }
+
+      public boolean equals(Object obj)
+      {
+         if (this == obj)
+            return true;
+         
+         if (!(obj instanceof SessionAddress))
+            return false;
+         
+         SessionAddress other = (SessionAddress) obj;
+         
+         return (sessionId.equals(other.sessionId) 
+                 && address.equals(other.address));
+      }
+
+      public int hashCode()
+      {
+         int total = (19 * 43) + sessionId.hashCode();
+         return ((total * 43) + address.hashCode());
+      }
+      
+      
+   }
+
+// DISABLED UNTIL 4.0.6.CR1
+//   /**
+//    * Runnable that's run when the removal of a node from the cluster has been detected. 
+//    * Removes any SessionAddress objects associated with dead members from the
+//    * session set of each SSO.  Operates locally only so each node can independently clean
+//    * its SSOs without concern about replication lock conflicts.
+//    */
+//   private class DeadMemberCleaner implements Runnable
+//   {    
+//      public void run()
+//      {
+//         synchronized (cleanupMutex)
+//         {
+//            try
+//            {      
+//               // Ensure we have a TransactionManager
+//               if (tm == null)
+//                  configureFromCache();
+//               
+//               Set ids = getSSOIds();
+//               
+//               for (Iterator iter = ids.iterator(); iter.hasNext();)
+//               {
+//                  cleanDeadMembersFromSSO((String) iter.next());
+//                  
+//               }
+//            }
+//            catch (Exception e)
+//            {
+//               log.error("Caught exception cleaning sessions from dead cluster members from SSOs ", e);
+//            }
+//         }
+//      }
+//      
+//      private void cleanDeadMembersFromSSO(String ssoId)
+//      {
+//         Fqn fqn = getSessionsFqn(ssoId);
+//         boolean doTx = false;
+//         try
+//         {
+//            // Don't start tx if there is already one associated with this thread.
+//            if(tm.getTransaction() == null)
+//               doTx = true;
+//
+//            if(doTx)
+//               tm.begin();
+//            
+//            Set sessions = getSessionSet(fqn, false, true);
+//            if (sessions != null && sessions.size() > 0)
+//            {
+//               boolean changed = false;
+//               for (Iterator iter = sessions.iterator(); iter.hasNext();)
+//               {
+//                  SessionAddress session = (SessionAddress) iter.next();
+//                  if (!currentView.containsMember((Address) session.address))
+//                  {
+//                     iter.remove();
+//                     changed = true;
+//                  }
+//               }
+//               
+//               if (changed)
+//               {                  
+//                  if (sessions.size() == 0)
+//                  {
+//                     ssoValve.notifySSOEmpty(ssoId);
+//                  }
+//                  
+//                  putInTreeCache(fqn, sessions, true);
+//               }
+//            }
+//         }
+//         catch (Exception e)
+//         {
+//            try
+//            {
+//               if(doTx)
+//                  tm.setRollbackOnly();
+//            }
+//            catch (Exception ignored)
+//            {
+//            }
+//            log.error("caught exception cleaning dead members from SSO " + ssoId, e);
+//         }
+//         finally
+//         {
+//            if (doTx)
+//               endTransaction();
+//         }         
+//      }
+//   }
+
+} // end TreeCacheSSOClusterManager
+


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+   <mbean name="ClusteredSingleSignOn"
+      description="A Valve that supports a 'single signon' user experience"
+      domain="Catalina"
+      group="Valve"
+      type="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn">
+
+      <attribute name="className"
+         description="Fully qualified class name of the managed object"
+         type="java.lang.String"
+         writeable="false"/>
+    
+      <attribute name="cookieDomain"
+               description="Domain to which sso cookies should be scoped"
+               type="java.lang.String"/>
+
+      <attribute name="requireReauthentication"
+         description="Should we attempt to reauthenticate each request against the security Realm?"
+         type="boolean"/>
+
+      <attribute name="maxEmptyLife"
+         description="The maximum number of seconds an SSO with no active sessions will be usable by a request"
+         type="int"/>
+
+      <attribute name="processExpiresInterval"
+         description="The maximum number of seconds an SSO with no active sessions will be usable by a request"
+         type="int"/>
+
+      <attribute name="lastProcessExpires"
+         writeable="false"
+         description="The timestamp of the start of the last check for overaged SSO's with no active sessions."
+         type="long"/>
+
+      <attribute name="clusterManager"
+         description="SSOClusterManager to use for cluster support"
+         type="org.jboss.web.tomcat.service.sso.SSOClusterManager"/>
+
+      <attribute name="clusterManagerClass"
+         description="Fully qualified class name of the SSOClusterManager to use for cluster support"
+         type="java.lang.String"/>
+
+      <attribute name="treeCacheName"
+         description="The ObjectName of the TreeCache that this Valve should use for cluster communications.
+                      Only relevant if clusterManagerClass is org.jboss.web.tomcat.tc5.sso.TreeCacheSSOClusterManager"
+         type="java.lang.String"/>
+
+   </mbean>
+
+</mbeans-descriptors>


Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: branches/Branch_4_2/tomcat/src/resources/META-INF/jboss-aop.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/META-INF/jboss-aop.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/META-INF/jboss-aop.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -3,19 +3,19 @@
 
    <!-- With this declaration, FieldBasedClusteredSession will implement the
         Observable interface to receive the event. 
-        Note that this file is bundled in tc5-cluster.aop. The aop package
+        Note that this file is bundled in jboss-web-cluster.aop. The aop package
         is needed such that a user doesn't depend on JBossCache library.
    -->
 
    <!-- If a POJO has AopMarker annotation, it will be aspectized. -->
-   <prepare expr="field(* @org.jboss.web.tomcat.tc5.session.AopMarker->*)" />
+   <prepare expr="field(* @org.jboss.web.tomcat.service.session.AopMarker->*)" />
 
    <!-- Make any POJO with a AopMarker annotation as a Subject/Observerable -->
-   <bind pointcut="set(* @org.jboss.web.tomcat.tc5.session.AopMarker->*)">
+   <bind pointcut="set(* @org.jboss.web.tomcat.service.session.AopMarker->*)">
       <interceptor class="org.jboss.aspects.patterns.observable.SubjectInterceptor"/>
    </bind>
 
-   <introduction class="@org.jboss.web.tomcat.tc5.session.AopMarker">
+   <introduction class="@org.jboss.web.tomcat.service.session.AopMarker">
       <mixin>
          <interfaces>org.jboss.aspects.patterns.observable.Subject</interfaces>
          <class>org.jboss.aspects.patterns.observable.SubjectImpl</class>
@@ -24,14 +24,14 @@
    </introduction>
   
    <!-- If a POJO has InstanceOfAopMarker annotation, it will be aspectized. -->
-   <prepare expr="field(* $instanceof{@org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker}->*)" />
+   <prepare expr="field(* $instanceof{@org.jboss.web.tomcat.service.session.InstanceOfAopMarker}->*)" />
 
    <!-- Make any POJO with a AopMarker annotation as a Subject/Observerable -->
-   <bind pointcut="set(* @org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker->*)">
+   <bind pointcut="set(* @org.jboss.web.tomcat.service.session.InstanceOfAopMarker->*)">
       <interceptor class="org.jboss.aspects.patterns.observable.SubjectInterceptor"/>
    </bind>
 
-   <introduction class="@org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker">
+   <introduction class="@org.jboss.web.tomcat.service.session.InstanceOfAopMarker">
       <mixin>
          <interfaces>org.jboss.aspects.patterns.observable.Subject</interfaces>
          <class>org.jboss.aspects.patterns.observable.SubjectImpl</class>

Modified: branches/Branch_4_2/tomcat/src/resources/jboss-service-all.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/jboss-service-all.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/jboss-service-all.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -2,8 +2,8 @@
 <!-- $Id$ -->
 
 <server> 
-   <!-- Tomcat 5 Service-->   
-   <mbean code="org.jboss.web.tomcat.tc5.Tomcat5"
+   <!-- JBoss Web Service-->   
+   <mbean code="org.jboss.web.tomcat.service.JBossWeb"
       name="jboss.web:service=WebServer" xmbean-dd="META-INF/webserver-xmbean.xml">
 
       <!-- You can configure a set of authenticators keyed by http-auth method used. This
@@ -71,9 +71,9 @@
 
       <!--
           Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
-          - org.jboss.web.tomcat.tc5.session.JBossCacheManager
+          - org.jboss.web.tomcat.service.session.JBossCacheManager
       -->
-      <attribute name="ManagerClass">org.jboss.web.tomcat.tc5.session.JBossCacheManager</attribute>
+      <attribute name="ManagerClass">org.jboss.web.tomcat.service.session.JBossCacheManager</attribute>
 
       <!-- The name of the request attribute under with the authenticated JAAS
       Subject is stored on successful authentication. If null or empty then
@@ -161,7 +161,7 @@
       
       <depends>jboss:service=TransactionManager</depends>
       
-      <!-- Only needed if the org.jboss.web.tomcat.tc5.jca.CachedConnectionValve
+      <!-- Only needed if the org.jboss.web.tomcat.service.jca.CachedConnectionValve
       is enabled in the tomcat server.xml file.
       -->
       <depends>jboss.jca:service=CachedConnectionManager</depends>

Modified: branches/Branch_4_2/tomcat/src/resources/jboss-service.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/jboss-service.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/jboss-service.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -3,7 +3,7 @@
 <server> 
    
    <!-- Tomcat 5 Service-->
-   <mbean code="org.jboss.web.tomcat.tc5.Tomcat5"
+   <mbean code="org.jboss.web.tomcat.service.JBossWeb"
       name="jboss.web:service=WebServer" xmbean-dd="META-INF/webserver-xmbean.xml">
       
       <!-- You can configure a set of authenticators keyed by http-auth method used. This
@@ -73,9 +73,9 @@
 
       <!--
           Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
-          - org.jboss.web.tomcat.tc5.session.JBossCacheManager
+          - org.jboss.web.tomcat.service.session.JBossCacheManager
       -->
-      <attribute name="ManagerClass">org.jboss.web.tomcat.tc5.session.JBossCacheManager</attribute>
+      <attribute name="ManagerClass">org.jboss.web.tomcat.service.session.JBossCacheManager</attribute>
 
       <!-- The name of the request attribute under with the authenticated JAAS
       Subject is stored on successful authentication. If null or empty then
@@ -165,7 +165,7 @@
       
       <depends>jboss:service=TransactionManager</depends>
       
-      <!-- Only needed if the org.jboss.web.tomcat.tc5.jca.CachedConnectionValve
+      <!-- Only needed if the org.jboss.web.tomcat.service.jca.CachedConnectionValve
       is enabled in the tomcat server.xml file.
       -->
       <depends>jboss.jca:service=CachedConnectionManager</depends>

Added: branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml	                        (rev 0)
+++ branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ===================================================================== -->
+<!--                                                                       -->
+<!--  Customized TreeCache Service Configuration for Tomcat 5 Clustering   -->
+<!--                                                                       -->
+<!-- ===================================================================== -->
+
+<server>
+
+    <!-- ==================================================================== -->
+    <!-- Defines TreeCache configuration                                      -->
+    <!-- ==================================================================== -->
+
+	 <!-- Note we are using TreeCacheAop -->
+    <mbean code="org.jboss.cache.aop.TreeCacheAop"
+        name="jboss.cache:service=TomcatClusteringCache">
+
+        <depends>jboss:service=Naming</depends>
+        <depends>jboss:service=TransactionManager</depends>
+        <!-- We need the AspectDeployer to deploy our FIELD granularity aspects -->
+        <depends>jboss.aop:service=AspectDeployer</depends>
+
+        <!-- Name of cluster. Needs to be the same for all nodes in the 
+             cluster, in order to find each other
+        -->
+        <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
+        
+        <!--
+            Isolation level : SERIALIZABLE
+                              REPEATABLE_READ (default)
+                              READ_COMMITTED
+                              READ_UNCOMMITTED
+                              NONE
+        -->
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Valid modes are LOCAL, REPL_ASYNC and REPL_SYNC
+             
+             If you use REPL_SYNC and a UDP-based ClusterConfig
+             we recommend you comment out the FC (flow control)
+             protocol in the ClusterConfig section below.
+        -->
+        <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+        <!-- 
+          Indicates whether to the cache should unmarshall objects replicated
+          from other cluster nodes, or store them internally as a byte[]
+          until a web app requests them.  Must be "true" if session replication 
+          granularity "FIELD" is used in any webapp, otherwise "false" is
+          recommended.
+        -->
+		<attribute name="UseRegionBasedMarshalling">false</attribute>
+        
+        <!--  Whether or not the entire tree is inactive upon startup, only 
+          responding to replication messages after activateRegion() is 
+          called to activate one or more parts of the tree when a webapp is
+          deployed.  Must have the same value as "UseRegionBasedMarshalling".
+        -->
+		<attribute name="InactiveOnStartup">false</attribute>
+          
+        <!--  Make sure to specify BatchModeTransactionManager only! -->
+        <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</attribute>
+
+        <!-- Configures binary format of messages sent between cluster nodes.
+             Changing this allows a later version of JBoss Cache to interoperate
+             with an earlier version. You might, for example, change this
+             if you are integrating a 4.0.4 server into a cluster with
+             servers running an earlier AS version.
+             
+             Possible values:
+             
+             1.2.3     JBC 1.2.3 or earlier; bundled with AS 4.0.3.SP1 and earlier
+             1.2.4     JBC 1.2.4
+             1.2.4.SP1 JBC 1.2.4.SP1
+             1.2.4.SP2 JBC 1.2.4.SP2; bundled with AS 4.0.4
+             
+             For version 1.3.0.GA and later, use the version name.
+        
+        	 If left blank or commented out, JBoss Cache will use the default
+        	 for that release (e.g. 1.4.0 for releases in the 1.4.0 series.
+        	 
+        	 The binary format of replication version 1.4.0 is much more efficient
+        	 than earlier releases, so there is a significant performance penalty
+        	 to trying to interoperate 1.4.0 with earlier releases vs. a pure
+        	 1.4.0 cluster.
+        
+        <attribute name="ReplicationVersion">1.4.0.GA</attribute>
+        -->
+        
+		<!-- JGroups protocol stack properties. Can also be a URL,
+             e.g. file:/home/bela/default.xml
+        <attribute name="ClusterProperties"></attribute>
+        -->
+
+        <attribute name="ClusterConfig">
+            <!--
+            The default UDP stack:
+            - If you have a multihomed machine, set the UDP protocol's bind_addr attribute to the
+            appropriate NIC IP address, e.g bind_addr="192.168.0.2".
+            - On Windows machines, because of the media sense feature being broken with multicast
+            (even after disabling media sense) set the UDP protocol's loopback attribute to true
+            
+            - If your CacheMode is set to REPL_SYNC we recommend you comment 
+            out the FC (flow control) protocol 
+            -->
+            <config>
+                <UDP mcast_addr="${jboss.partition.udpGroup:230.1.2.7}" 
+                     mcast_port="45577"
+                     ucast_recv_buf_size="20000000"
+                     ucast_send_buf_size="640000"
+                     mcast_recv_buf_size="25000000" 
+                     mcast_send_buf_size="640000" 
+                     loopback="false" 
+                     max_bundle_size="64000" 
+                     max_bundle_timeout="30" 
+                     use_incoming_packet_handler="true" 
+                     use_outgoing_packet_handler="true" 
+                     ip_ttl="${jgroups.mcast.ip_ttl:2}" 
+                     down_thread="false" up_thread="false"
+                     enable_bundling="true"/>
+                <PING timeout="2000"
+                      down_thread="false" up_thread="false" num_initial_members="3"/>
+                <MERGE2 max_interval="100000"
+                        down_thread="false" up_thread="false" min_interval="20000"/>
+                <FD_SOCK down_thread="false" up_thread="false"/>
+                <FD shun="true" up_thread="false" down_thread="false"
+                        timeout="20000" max_tries="5"/>
+                <VERIFY_SUSPECT timeout="1500"
+                        up_thread="false" down_thread="false"/>
+                <pbcast.NAKACK max_xmit_size="60000"
+                               use_mcast_xmit="false" gc_lag="50" 
+                               retransmit_timeout="300,600,1200,2400,4800" 
+                               down_thread="false" up_thread="false"
+                               discard_delivered_msgs="true"/>
+                <UNICAST timeout="300,600,1200,2400,3600" 
+                         down_thread="false" up_thread="false"/>
+                <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000" 
+                               down_thread="false" up_thread="false"
+                               max_bytes="400000"/>
+                <pbcast.GMS print_local_addr="true" join_timeout="3000" 
+                            down_thread="false" up_thread="false"
+                            join_retry_timeout="2000" shun="true"/>
+                <FC max_credits="2000000" down_thread="false" up_thread="false"
+                    min_threshold="0.10"/>
+                <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
+                <pbcast.STATE_TRANSFER down_thread="false" up_thread="false"/>
+           </config>
+
+           <!-- Alternate TCP stack: customize it for your environment, change bind_addr and initial_hosts -->
+           <!--
+           <config>
+              <TCP bind_addr="thishost" start_port="7810" loopback="true" 
+                   tcp_nodelay="false" down_thread="false" up_thread="false"/>
+              <TCPPING initial_hosts="thishost[7810],otherhost[7810]" port_range="3" timeout="3500"
+                 num_initial_members="3" up_thread="false" down_thread="false"/>
+              <MERGE2 min_interval="5000" max_interval="10000"
+                 up_thread="false" down_thread="false"/>
+              <FD_SOCK down_thread="false" up_thread="false"/>
+              <FD shun="true" up_thread="false" down_thread="false"
+                 timeout="10000" max_tries="5"/>
+              <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />
+              <pbcast.NAKACK down_thread="false" up_thread="false" gc_lag="100"
+                 retransmit_timeout="3000"/>
+              <pbcast.STABLE desired_avg_gossip="20000" down_thread="false" up_thread="false" />
+              <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true"
+                 print_local_addr="true" down_thread="false" up_thread="false"/>
+              <FC max_credits="2000000" down_thread="false" up_thread="false"
+                 min_threshold="0.10"/>
+              <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
+              <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
+           </config>
+           -->
+
+        </attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">20000</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+        <!-- Buddy Replication config.
+        
+             See http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossCacheBuddyReplicationDesign
+             and the JBoss Cache docs for more on buddy replication.
+             
+             By default, buddy replication is disabled.
+             
+             Following are the configuration elements likely to be changed:
+             
+             buddyReplicationEnabled  true if you want buddy replication; false if data
+                                      should be replicated to all nodes in the cluster
+                                      
+             numBuddies               to how many backup nodes should each node replicate
+                                      its state
+                                      
+             buddyPoolName            allows logical subgrouping of nodes within the cluster;
+                                      if possible, buddies will be chosen from nodes in the 
+                                      same buddy pool
+                                      
+             Do not change the data gravitation related options.             
+        -->
+        <attribute name="BuddyReplicationConfig">
+            <config>
+                <buddyReplicationEnabled>false</buddyReplicationEnabled>
+                <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
+                <buddyLocatorProperties>
+                    numBuddies = 1
+                    ignoreColocatedBuddies = true
+                </buddyLocatorProperties>
+
+                <buddyPoolName>default</buddyPoolName>
+                <buddyCommunicationTimeout>2000</buddyCommunicationTimeout>
+
+                <autoDataGravitation>false</autoDataGravitation>
+                <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
+                <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
+
+            </config>
+        </attribute>
+		   
+    </mbean>
+
+</server>


Property changes on: branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: branches/Branch_4_2/tomcat/src/resources/server.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/server.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/server.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -1,38 +1,41 @@
 <Server>
 
+  <!--APR library loader. Documentation at /docs/apr.html -->
+  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
+  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
+  <Listener className="org.apache.catalina.core.JasperListener" />
+
    <!-- Use a custom version of StandardService that allows the
    connectors to be started independent of the normal lifecycle
    start to allow web apps to be deployed before starting the
    connectors.
    -->
    <Service name="jboss.web"
-      className="org.jboss.web.tomcat.tc5.StandardService">
+      className="org.jboss.web.tomcat.service.StandardService">
 
-      <!-- A HTTP/1.1 Connector on port 8080 -->
-      <Connector port="8080" address="${jboss.bind.address}"
-         maxThreads="250" strategy="ms" maxHttpHeaderSize="8192"
-         emptySessionPath="true"
-         enableLookups="false" redirectPort="8443" acceptCount="100"
-         connectionTimeout="20000" disableUploadTimeout="true"/>
+    <!-- A "Connector" represents an endpoint by which requests are received
+         and responses are returned. Documentation at :
+         Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
+         Java AJP  Connector: /docs/config/ajp.html
+         APR (HTTP/AJP) Connector: /docs/apr.html
+         Define a non-SSL HTTP/1.1 Connector on port 8080
+    -->
+    <Connector port="8080" protocol="HTTP/1.1" 
+               maxThreads="150" connectionTimeout="20000" 
+               redirectPort="8443" />
 
-      <!-- Add this option to the connector to avoid problems with 
-          .NET clients that don't implement HTTP/1.1 correctly 
-         restrictedUserAgents="^.*MS Web Services Client Protocol 1.1.4322.*$"
-      -->
-	
-      <!-- A AJP 1.3 Connector on port 8009 -->
-      <Connector port="8009" address="${jboss.bind.address}"
-         emptySessionPath="true" enableLookups="false" redirectPort="8443" 
-         protocol="AJP/1.3"/>
+    <!-- Define a SSL HTTP/1.1 Connector on port 8443
+         This connector uses the JSSE configuration, when using APR, the 
+         connector should be using the OpenSSL style configuration
+         described in the APR documentation -->
+    <!--
+    <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
+               maxThreads="150" scheme="https" secure="true"
+               clientAuth="false" sslProtocol="TLS" />
+    -->
 
-      <!-- SSL/TLS Connector configuration using the admin devl guide keystore
-      <Connector port="8443" address="${jboss.bind.address}"
-           maxThreads="100" strategy="ms" maxHttpHeaderSize="8192"
-           emptySessionPath="true"
-           scheme="https" secure="true" clientAuth="false" 
-           keystoreFile="${jboss.server.home.dir}/conf/chap8.keystore"
-           keystorePass="rmi+ssl" sslProtocol = "TLS" />
-      -->
+    <!-- Define an AJP 1.3 Connector on port 8009 -->
+    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
 
       <Engine name="jboss.web" defaultHost="localhost">
 
@@ -86,7 +89,7 @@
  
             <!-- Access logger -->
             <!--
-            <Valve className="org.apache.catalina.valves.FastCommonAccessLogValve"
+            <Valve className="org.apache.catalina.valves.AccessLogValve"
                 prefix="localhost_access_log." suffix=".log"
                 pattern="common" directory="${jboss.server.home.dir}/log" 
                 resolveHosts="false" />
@@ -123,7 +126,7 @@
                caching and replication across the cluster.  The JBossCache 
                instance must be configured separately.  By default, the valve 
                shares a JBossCache with the service that supports HttpSession 
-               replication.  See the "tc5-cluster-service.xml" file in the 
+               replication.  See the "jboss-web-cluster-service.xml" file in the 
                server/all/deploy directory for cache configuration details.
             
                Besides the attributes supported by the standard Tomcat
@@ -140,7 +143,7 @@
                               to support session replication.
             -->
             <!--
-            <Valve className="org.jboss.web.tomcat.tc5.sso.ClusteredSingleSignOn" />
+            <Valve className="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn" />
             -->
          
             <!-- Check for unclosed connections and transaction terminated checks
@@ -149,7 +152,7 @@
                  Important: The dependency on the CachedConnectionManager
                  in META-INF/jboss-service.xml must be uncommented, too
             -->
-            <Valve className="org.jboss.web.tomcat.tc5.jca.CachedConnectionValve"
+            <Valve className="org.jboss.web.tomcat.service.jca.CachedConnectionValve"
                 cachedConnectionManagerObjectName="jboss.jca:service=CachedConnectionManager"
                 transactionManagerObjectName="jboss:service=TransactionManager" />
 

Modified: branches/Branch_4_2/tomcat/src/resources/standalone-server.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/standalone-server.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/standalone-server.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -1,147 +1,88 @@
-<!-- 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.
+<!-- Note:  A "Server" is not itself a "Container", so you may not
+     define subcomponents such as "Valves" at this level.
+     Documentation at /docs/config/server.html
  -->
-
 <Server port="8005" shutdown="SHUTDOWN">
 
-  <!-- Comment these entries out to disable JMX MBeans support used for the 
-       administration web application -->
+  <!--APR library loader. Documentation at /docs/apr.html -->
+  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
+  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
+  <Listener className="org.apache.catalina.core.JasperListener" />
+  <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html -->
   <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
-  <Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>
 
-  <!-- Global JNDI resources -->
+  <!-- Global JNDI resources
+       Documentation at /docs/jndi-resources-howto.html
+  -->
   <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 -->
+         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" />
-           
+              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.
+       a single "Container" Note:  A "Service" is not itself a "Container", 
+       so you may not define subcomponents such as "Valves" at this level.
+       Documentation at /docs/config/service.html
    -->
-
-  <!-- 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.
+         and responses are returned. Documentation at :
+         Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
+         Java AJP  Connector: /docs/config/ajp.html
+         APR (HTTP/AJP) Connector: /docs/apr.html
+         Define a non-SSL HTTP/1.1 Connector on port 8080
     -->
+    <Connector port="8080" protocol="HTTP/1.1" 
+               maxThreads="150" connectionTimeout="20000" 
+               redirectPort="8443" />
 
-    <!-- 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 -->
+    <!-- Define a SSL HTTP/1.1 Connector on port 8443
+         This connector uses the JSSE configuration, when using APR, the 
+         connector should be using the OpenSSL style configuration
+         described in the APR documentation -->
     <!--
-    <Connector port="8443" maxHttpHeaderSize="8192"
-               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
-               enableLookups="false" disableUploadTimeout="true"
-               acceptCount="100" scheme="https" secure="true"
+    <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
+               maxThreads="150" 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" />
+    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
 
-    <!-- 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). -->
+         on to the appropriate Host (virtual host).
+         Documentation at /docs/config/engine.html -->
 
     <!-- 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 clustering, please take a look at documentation at:
+          /docs/cluster-howto.html  (simple how to)
+          /docs/config/cluster.html (reference documentation) -->
+      <!--
+      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
+      -->        
 
-           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. -->
+      <!-- The request dumper valve dumps useful debugging information about
+           the request and response data received and sent by Tomcat.
+           Documentation at: /docs/config/valve.html -->
       <!--
       <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
@@ -149,163 +90,27 @@
       <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" />
-      -->
-      <!--
-      <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-      -->
       <!-- 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">
-       
-        <!-- Use JBossCache for HttpSession replication across a cluster -->
-	    <Cluster className="org.jboss.web.tomcat.tc5.session.JBossCacheCluster"
-	             cacheObjectName="Catalina:service=TomcatClusteringCache"
-	             cacheConfigPath="conf/cluster-cache.xml"
-	             snapshotMode="instant"
-	             snapshotInterval="2000"
-	             useJK="false"
-	             defaultReplicationTrigger="SET_AND_NON_PRIMITIVE_GET"
-	             defaultReplicationGranularity="session"/>
+      <Host name="localhost"  appBase="webapps"
+            unpackWARs="true" autoDeploy="true"
+            xmlValidation="false" xmlNamespaceAware="false">
 
-        <!-- 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. 
-             
-             Does not provide SSO across a cluster.     
-           
-             If this valve is used, do not use the ClusteredSingleSignOn 
-             valve shown below.  
-                
-             A new configuration attribute is available beginning with
-             release 5.5.13:
-                
-             cookieDomain  configures the domain to which the SSO cookie
-                           will be scoped (i.e. the set of hosts to
-                           which the cookie will be presented).  By default
-                           the cookie is scoped to "/", meaning the host
-                           that presented it.  Set cookieDomain to a
-                           wider domain (e.g. "xyz.com") to allow an SSO
-                           to span more than one hostname.
-        -->
+        <!-- SingleSignOn valve, share authentication between web applications
+             Documentation at: /docs/config/valve.html -->
         <!--
         <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
         -->
-        
-        <!-- Uncomment to enable single sign-on across web apps
-             deployed to this host AND to all other hosts in the cluster
-             with the same virtual hostname.
-            
-             If this valve is used, do not use the standard Tomcat SingleSignOn
-             valve shown above.
-            
-             This valve uses uses JBossCache to replicate SSO credentials across
-             the cluster.  To use it, be sure the JBossCache-based Cluster 
-             element above is uncommented.
-            
-             Besides the attributes supported by the standard Tomcat
-             SingleSignOn valve (see the Tomcat docs), this version also supports 
-             the following attributes:
-            
-             cookieDomain    see above
-            
-             treeCacheName   the JMX ObjectName of the JBossCache MBean created by
-                             the Cluster element above.  This should be set to
-                             match the "cacheObjectName" attribute of the Cluster
-                             element.  If this attribute is not configured, a
-                             default of "jboss.web:service=TomcatClusteringCache"
-                             will be used, which matches the default value of the
-                             Cluster element's "cacheObjectName".
-            -->
-            <!--
-            <Valve className="org.jboss.web.tomcat.tc5.sso.ClusteredSingleSignOn" />
-            -->
 
-        <!-- 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.
-        -->
+        <!-- Access log processes all example.
+             Documentation at: /docs/config/valve.html -->
         <!--
-        <Valve className="org.apache.catalina.valves.AccessLogValve"
-                 directory="logs"  prefix="localhost_access_log." suffix=".txt"
-                 pattern="common" resolveHosts="false"/>
+        <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>

Deleted: branches/Branch_4_2/tomcat/src/resources/tc5-cluster-service.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/tc5-cluster-service.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/tc5-cluster-service.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -1,228 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ===================================================================== -->
-<!--                                                                       -->
-<!--  Customized TreeCache Service Configuration for Tomcat 5 Clustering   -->
-<!--                                                                       -->
-<!-- ===================================================================== -->
-
-<server>
-
-    <!-- ==================================================================== -->
-    <!-- Defines TreeCache configuration                                      -->
-    <!-- ==================================================================== -->
-
-	 <!-- Note we are using TreeCacheAop -->
-    <mbean code="org.jboss.cache.aop.TreeCacheAop"
-        name="jboss.cache:service=TomcatClusteringCache">
-
-        <depends>jboss:service=Naming</depends>
-        <depends>jboss:service=TransactionManager</depends>
-        <!-- We need the AspectDeployer to deploy our FIELD granularity aspects -->
-        <depends>jboss.aop:service=AspectDeployer</depends>
-
-        <!-- Name of cluster. Needs to be the same for all nodes in the 
-             cluster, in order to find each other
-        -->
-        <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
-        
-        <!--
-            Isolation level : SERIALIZABLE
-                              REPEATABLE_READ (default)
-                              READ_COMMITTED
-                              READ_UNCOMMITTED
-                              NONE
-        -->
-        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
-
-        <!-- Valid modes are LOCAL, REPL_ASYNC and REPL_SYNC
-             
-             If you use REPL_SYNC and a UDP-based ClusterConfig
-             we recommend you comment out the FC (flow control)
-             protocol in the ClusterConfig section below.
-        -->
-        <attribute name="CacheMode">REPL_ASYNC</attribute>
-
-        <!-- 
-          Indicates whether to the cache should unmarshall objects replicated
-          from other cluster nodes, or store them internally as a byte[]
-          until a web app requests them.  Must be "true" if session replication 
-          granularity "FIELD" is used in any webapp, otherwise "false" is
-          recommended.
-        -->
-		<attribute name="UseRegionBasedMarshalling">false</attribute>
-        
-        <!--  Whether or not the entire tree is inactive upon startup, only 
-          responding to replication messages after activateRegion() is 
-          called to activate one or more parts of the tree when a webapp is
-          deployed.  Must have the same value as "UseRegionBasedMarshalling".
-        -->
-		<attribute name="InactiveOnStartup">false</attribute>
-          
-        <!--  Make sure to specify BatchModeTransactionManager only! -->
-        <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</attribute>
-
-        <!-- Configures binary format of messages sent between cluster nodes.
-             Changing this allows a later version of JBoss Cache to interoperate
-             with an earlier version. You might, for example, change this
-             if you are integrating a 4.0.4 server into a cluster with
-             servers running an earlier AS version.
-             
-             Possible values:
-             
-             1.2.3     JBC 1.2.3 or earlier; bundled with AS 4.0.3.SP1 and earlier
-             1.2.4     JBC 1.2.4
-             1.2.4.SP1 JBC 1.2.4.SP1
-             1.2.4.SP2 JBC 1.2.4.SP2; bundled with AS 4.0.4
-             
-             For version 1.3.0.GA and later, use the version name.
-        
-        	 If left blank or commented out, JBoss Cache will use the default
-        	 for that release (e.g. 1.4.0 for releases in the 1.4.0 series.
-        	 
-        	 The binary format of replication version 1.4.0 is much more efficient
-        	 than earlier releases, so there is a significant performance penalty
-        	 to trying to interoperate 1.4.0 with earlier releases vs. a pure
-        	 1.4.0 cluster.
-        
-        <attribute name="ReplicationVersion">1.4.0.GA</attribute>
-        -->
-        
-		<!-- JGroups protocol stack properties. Can also be a URL,
-             e.g. file:/home/bela/default.xml
-        <attribute name="ClusterProperties"></attribute>
-        -->
-
-        <attribute name="ClusterConfig">
-            <!--
-            The default UDP stack:
-            - If you have a multihomed machine, set the UDP protocol's bind_addr attribute to the
-            appropriate NIC IP address, e.g bind_addr="192.168.0.2".
-            - On Windows machines, because of the media sense feature being broken with multicast
-            (even after disabling media sense) set the UDP protocol's loopback attribute to true
-            
-            - If your CacheMode is set to REPL_SYNC we recommend you comment 
-            out the FC (flow control) protocol 
-            -->
-            <config>
-                <UDP mcast_addr="${jboss.partition.udpGroup:230.1.2.7}" 
-                     mcast_port="45577"
-                     ucast_recv_buf_size="20000000"
-                     ucast_send_buf_size="640000"
-                     mcast_recv_buf_size="25000000" 
-                     mcast_send_buf_size="640000" 
-                     loopback="false" 
-                     max_bundle_size="64000" 
-                     max_bundle_timeout="30" 
-                     use_incoming_packet_handler="true" 
-                     use_outgoing_packet_handler="true" 
-                     ip_ttl="${jgroups.mcast.ip_ttl:2}" 
-                     down_thread="false" up_thread="false"
-                     enable_bundling="true"/>
-                <PING timeout="2000"
-                      down_thread="false" up_thread="false" num_initial_members="3"/>
-                <MERGE2 max_interval="100000"
-                        down_thread="false" up_thread="false" min_interval="20000"/>
-                <FD_SOCK down_thread="false" up_thread="false"/>
-                <FD shun="true" up_thread="false" down_thread="false"
-                        timeout="20000" max_tries="5"/>
-                <VERIFY_SUSPECT timeout="1500"
-                        up_thread="false" down_thread="false"/>
-                <pbcast.NAKACK max_xmit_size="60000"
-                               use_mcast_xmit="false" gc_lag="50" 
-                               retransmit_timeout="300,600,1200,2400,4800" 
-                               down_thread="false" up_thread="false"
-                               discard_delivered_msgs="true"/>
-                <UNICAST timeout="300,600,1200,2400,3600" 
-                         down_thread="false" up_thread="false"/>
-                <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000" 
-                               down_thread="false" up_thread="false"
-                               max_bytes="400000"/>
-                <pbcast.GMS print_local_addr="true" join_timeout="3000" 
-                            down_thread="false" up_thread="false"
-                            join_retry_timeout="2000" shun="true"/>
-                <FC max_credits="2000000" down_thread="false" up_thread="false"
-                    min_threshold="0.10"/>
-                <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
-                <pbcast.STATE_TRANSFER down_thread="false" up_thread="false"/>
-           </config>
-
-           <!-- Alternate TCP stack: customize it for your environment, change bind_addr and initial_hosts -->
-           <!--
-           <config>
-              <TCP bind_addr="thishost" start_port="7810" loopback="true" 
-                   tcp_nodelay="false" down_thread="false" up_thread="false"/>
-              <TCPPING initial_hosts="thishost[7810],otherhost[7810]" port_range="3" timeout="3500"
-                 num_initial_members="3" up_thread="false" down_thread="false"/>
-              <MERGE2 min_interval="5000" max_interval="10000"
-                 up_thread="false" down_thread="false"/>
-              <FD_SOCK down_thread="false" up_thread="false"/>
-              <FD shun="true" up_thread="false" down_thread="false"
-                 timeout="10000" max_tries="5"/>
-              <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />
-              <pbcast.NAKACK down_thread="false" up_thread="false" gc_lag="100"
-                 retransmit_timeout="3000"/>
-              <pbcast.STABLE desired_avg_gossip="20000" down_thread="false" up_thread="false" />
-              <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true"
-                 print_local_addr="true" down_thread="false" up_thread="false"/>
-              <FC max_credits="2000000" down_thread="false" up_thread="false"
-                 min_threshold="0.10"/>
-              <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
-              <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
-           </config>
-           -->
-
-        </attribute>
-
-        <!--
-            Number of milliseconds to wait until all responses for a
-            synchronous call have been received.
-        -->
-        <attribute name="SyncReplTimeout">20000</attribute>
-
-        <!-- Max number of milliseconds to wait for a lock acquisition -->
-        <attribute name="LockAcquisitionTimeout">15000</attribute>
-
-        <!-- Buddy Replication config.
-        
-             See http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossCacheBuddyReplicationDesign
-             and the JBoss Cache docs for more on buddy replication.
-             
-             By default, buddy replication is disabled.
-             
-             Following are the configuration elements likely to be changed:
-             
-             buddyReplicationEnabled  true if you want buddy replication; false if data
-                                      should be replicated to all nodes in the cluster
-                                      
-             numBuddies               to how many backup nodes should each node replicate
-                                      its state
-                                      
-             buddyPoolName            allows logical subgrouping of nodes within the cluster;
-                                      if possible, buddies will be chosen from nodes in the 
-                                      same buddy pool
-                                      
-             Do not change the data gravitation related options.             
-        -->
-        <attribute name="BuddyReplicationConfig">
-            <config>
-                <buddyReplicationEnabled>false</buddyReplicationEnabled>
-                <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
-                <buddyLocatorProperties>
-                    numBuddies = 1
-                    ignoreColocatedBuddies = true
-                </buddyLocatorProperties>
-
-                <buddyPoolName>default</buddyPoolName>
-                <buddyCommunicationTimeout>2000</buddyCommunicationTimeout>
-
-                <autoDataGravitation>false</autoDataGravitation>
-                <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
-                <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
-
-            </config>
-        </attribute>
-		   
-    </mbean>
-
-</server>

Modified: branches/Branch_4_2/tomcat/src/resources/web.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/web.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/web.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -1,18 +1,18 @@
 <?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">
+    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.   -->
+  <!-- ======================== 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.   -->
 
    <!-- ================== Common filter Configuration ==================== -->
    <filter>
@@ -34,962 +34,1150 @@
       <listener-class>org.jboss.web.tomcat.security.SecurityFlushSessionListener</listener-class>
    </listener>
 
-   <!-- ================== Built In Servlet Definitions ==================== -->
+  <!-- ================== 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]                          -->
-   <!--                                                                      -->
-   <!--   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]                               -->
-   <!--                                                                      -->
-   <!--  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]                  -->
-   <!--                                                                      -->
-   <!--                                                                      -->
+  <!-- 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?  [false] -->
+  <!--                       WARNING: Listings for directories with many    -->
+  <!--                       entries can be slow and may consume            -->
+  <!--                       significant proportions of server resources.   -->
+  <!--                                                                      -->
+  <!--   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>
+    <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>false</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]                          -->
+  <!-- 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>
-   -->
+<!--
+    <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:                       -->
-   <!--   Set the init parameter "compiler" to "jikes".  Define              -->
-   <!--   the property "-Dbuild.compiler.emacs=true" when starting Tomcat    -->
-   <!--   by adding the above to your CATALINA_OPTS environment variable.    -->
-   <!--   If you get an error reporting that jikes can't use UTF8 encoding,  -->
-   <!--   try setting the init parameter "javaEncoding" to "ISO-8859-1".     -->
+  <!-- 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]  -->
+  <!--                                                                      -->
+  <!--   compilerTargetVM    Compiler target VM                             -->  
+  <!--                       default is System.properties                   -->
+  <!--                        java.specification.version > 1.4              -->
+  <!--                        [1.5] else [1.4]                              -->
+  <!--                                                                      -->
+  <!--   compilerSourceVM    Compiler source VM                             -->
+  <!--                       default is System.properties                   -->
+  <!--                        java.specification.version > 1.4              -->
+  <!--                        [1.5] else [1.4]                              -->
+  <!--                                                                      -->
+  <!-- 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>
-      <!-- Uncomment to use jdk1.5 features in jsp pages
-      <init-param>
-         <param-name>compilerSourceVM</param-name>
-         <param-value>1.5</param-value>
-      </init-param>
-      -->
-      <!-- Use a custom options class to allow the shared tag lib descriptors
-       to be loaded from jars in the tomcat sar conf/tlds directory. The
-       standard options implementation can only find taglibs based on the
-       class loader classpath.
-      -->
-      <init-param>
-         <param-name>engineOptionsClass</param-name>
-         <param-value>org.jboss.web.tomcat.tc5.jasper.JspServletOptions</param-value>
-      </init-param>
-      <!-- Specify the jars relative to the jbossweb-tomcat55.sar that should
-        be scanned for common tag lib descriptors to include in every war
-        deployment.
-      -->
-      <init-param>
-         <description>MyFaces tlds</description>
-         <param-name>tagLibJar0</param-name>
-         <param-value>jsf-libs/myfaces-impl.jar</param-value>
-      </init-param>
+    <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>
 
-      <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             -->
 
-   <!-- 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]        -->
-   <!--                                                                      -->
-   <!--                                                                      -->
-   <!-- 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>
+-->
 
-   <!--
-       <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             -->
 
-   <!-- 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>0</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>
+-->
 
-   <!--
-       <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 ========================= -->
 
-   <!-- ================ 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 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 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 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>
 
-   <!-- 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>
 
-   <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 SSI servlet -->
-   <!--
-       <servlet-mapping>
-           <servlet-name>ssi</servlet-name>
-           <url-pattern>*.shtml</url-pattern>
-       </servlet-mapping>
-   -->
+    <!-- The mapping for the CGI Gateway servlet -->
 
-   <!-- The mapping for the CGI Gateway servlet -->
+<!--
+    <servlet-mapping>
+        <servlet-name>cgi</servlet-name>
+        <url-pattern>/cgi-bin/*</url-pattern>
+    </servlet-mapping>
+-->
 
-   <!--
-       <servlet-mapping>
-           <servlet-name>cgi</servlet-name>
-           <url-pattern>/cgi-bin/*</url-pattern>
-       </servlet-mapping>
-   -->
 
+  <!-- ================== Built In Filter Definitions ===================== -->
 
-   <!-- ==================== Default Session Configuration ================= -->
-   <!-- You can set the default session timeout (in minutes) for all newly   -->
-   <!-- created sessions by modifying the value below.                       -->
+  <!-- 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. For   -->
+  <!-- this to work you will need to uncomment the .shtml mime type         -->
+  <!-- definition towards the bottom of this file.                          -->
+  <!-- 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             -->
 
-   <session-config>
-      <session-timeout>30</session-timeout>
-   </session-config>
+<!--
+    <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>
+-->
 
 
-   <!-- ===================== 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.                                               -->
+  <!-- ==================== Built In Filter Mappings ====================== -->
 
-   <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>text/plain</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>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>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>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>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>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>xbm</extension>
-      <mime-type>image/x-xbitmap</mime-type>
-   </mime-mapping>
-   <mime-mapping>
-      <extension>xht</extension>
-      <mime-type>application/xhtml</mime-type>
-   </mime-mapping>
-   <mime-mapping>
-      <extension>xhtml</extension>
-      <mime-type>application/xhtml</mime-type>
-   </mime-mapping>
-   <mime-mapping>
-      <extension>xml</extension>
-      <mime-type>text/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>text/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>
+  <!-- The mapping for the SSI Filter -->
+<!--
+    <filter-mapping>
+        <filter-name>ssi</filter-name>
+        <url-pattern>*.shtml</url-pattern>
+    </filter-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.                                                 -->
+  <!-- ==================== Default Session Configuration ================= -->
+  <!-- You can set the default session timeout (in minutes) for all newly   -->
+  <!-- created sessions by modifying the value below.                       -->
 
-   <welcome-file-list>
-      <welcome-file>index.html</welcome-file>
-      <welcome-file>index.htm</welcome-file>
-      <welcome-file>index.jsp</welcome-file>
-   </welcome-file-list>
+    <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>
+        <!-- OpenDocument Database -->
+        <extension>odb</extension>
+        <mime-type>application/vnd.oasis.opendocument.database</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Chart -->
+        <extension>odc</extension>
+        <mime-type>application/vnd.oasis.opendocument.chart</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Formula -->
+        <extension>odf</extension>
+        <mime-type>application/vnd.oasis.opendocument.formula</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Drawing -->
+        <extension>odg</extension>
+        <mime-type>application/vnd.oasis.opendocument.graphics</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Image -->
+        <extension>odi</extension>
+        <mime-type>application/vnd.oasis.opendocument.image</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Master Document -->
+        <extension>odm</extension>
+        <mime-type>application/vnd.oasis.opendocument.text-master</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Presentation -->
+        <extension>odp</extension>
+        <mime-type>application/vnd.oasis.opendocument.presentation</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Spreadsheet -->
+        <extension>ods</extension>
+        <mime-type>application/vnd.oasis.opendocument.spreadsheet</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Text -->
+        <extension>odt</extension>
+        <mime-type>application/vnd.oasis.opendocument.text</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ogg</extension>
+        <mime-type>application/ogg</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Drawing Template -->
+        <extension>otg </extension>
+        <mime-type>application/vnd.oasis.opendocument.graphics-template</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- HTML Document Template -->
+        <extension>oth</extension>
+        <mime-type>application/vnd.oasis.opendocument.text-web</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Presentation Template -->
+        <extension>otp</extension>
+        <mime-type>application/vnd.oasis.opendocument.presentation-template</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Spreadsheet Template -->
+        <extension>ots</extension>
+        <mime-type>application/vnd.oasis.opendocument.spreadsheet-template </mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <!-- OpenDocument Text Template -->
+        <extension>ott</extension>
+        <mime-type>application/vnd.oasis.opendocument.text-template</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>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+xml</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>svgz</extension>
+        <mime-type>image/svg+xml</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>
+    <mime-mapping>
+        <extension>xls</extension>
+        <mime-type>application/vnd.ms-excel</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>doc</extension>
+        <mime-type>application/vnd.ms-word</mime-type>
+    </mime-mapping>
+    <mime-mapping>
+        <extension>ppt</extension>
+        <mime-type>application/vnd.ms-powerpoint</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>

Modified: branches/Branch_4_2/tomcat/src/resources/webserver-xmbean.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/webserver-xmbean.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/webserver-xmbean.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -3,21 +3,21 @@
    "-//JBoss//DTD JBOSS XMBEAN 1.2//EN"
    "http://www.jboss.org/j2ee/dtd/jboss_xmbean_1_2.dtd">
 <!--
-   Tomcat XMBean descriptor
+   Jboss Web XMBean descriptor
    
    $Id$
 -->
 <mbean>
-   <description>The Tomcat Deployer responsible for war deployment</description>
+   <description>The JBoss Web Deployer responsible for war deployment</description>
    <descriptors>
      <interceptors>
        <!-- comment out DynamicInterceptor to disable dynamic addition of interceptors -->
        <interceptor code="org.jboss.mx.interceptor.DynamicInterceptor"/>
      </interceptors>
    </descriptors>
-   <class>org.jboss.web.tomcat.tc5.Tomcat5</class>
+   <class>org.jboss.web.tomcat.service.JBossWeb</class>
    
-  <!-- Tomcat5MBean attributes -->
+  <!-- JBossWebMBean attributes -->
   
    <attribute access="read-write" getMethod="getAuthenticators" setMethod="setAuthenticators">
      <name>Authenticators</name>
@@ -162,7 +162,7 @@
    &deployerAttributes;
    &defaultAttributes;
 
-  <!-- Tomcat5MBean operations -->
+  <!-- JBossWebMBean operations -->
    <operation>
      <description>See if a war is deployed</description>
      <name>isDeployed</name>

Modified: branches/Branch_4_2/tomcat/src/webapps/ROOT.war/WEB-INF/web.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/webapps/ROOT.war/WEB-INF/web.xml	2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/webapps/ROOT.war/WEB-INF/web.xml	2007-01-18 02:36:30 UTC (rev 59745)
@@ -11,7 +11,7 @@
   </description>
   <servlet>
     <servlet-name>Status Servlet</servlet-name>
-    <servlet-class>org.jboss.web.tomcat.tc5.StatusServlet</servlet-class>
+    <servlet-class>org.jboss.web.tomcat.service.StatusServlet</servlet-class>
   </servlet>
   <servlet-mapping>
     <servlet-name>Status Servlet</servlet-name>




More information about the jboss-cvs-commits mailing list