[jboss-cvs] JBossAS SVN: r61355 - in trunk/tomcat: src/main/org/jboss/web/tomcat and 8 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Thu Mar 15 22:23:54 EDT 2007


Author: remy.maucherat at jboss.com
Date: 2007-03-15 22:23:53 -0400 (Thu, 15 Mar 2007)
New Revision: 61355

Added:
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/TomcatInjectionContainer.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/WarEjbResolver.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/DeployerConfig.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployer.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InvalidateSessionPolicy.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ReplicationGranularity.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
   trunk/tomcat/src/resources/jboss-web-cluster-beans.xml
   trunk/tomcat/src/resources/jboss-web-cluster-service.xml
Removed:
   trunk/tomcat/src/main/org/jboss/web/tomcat/tc5/
   trunk/tomcat/src/main/org/jboss/web/tomcat/tc6/
   trunk/tomcat/src/resources/tc5-cluster-service.xml
   trunk/tomcat/src/resources/tc6-cluster-beans.xml
   trunk/tomcat/src/resources/tc6-cluster-service.xml
Modified:
   trunk/tomcat/build.xml
   trunk/tomcat/src/resources/META-INF/jboss-aop.xml
   trunk/tomcat/src/resources/server.xml
   trunk/tomcat/src/resources/war-deployers-all-beans.xml
   trunk/tomcat/src/resources/war-deployers-beans.xml
   trunk/tomcat/src/resources/web.xml
   trunk/tomcat/src/resources/webserver-xmbean.xml
Log:
- Port package renaming from JBoss 4.2.
- Switch deployment2 with deployment (the deployment2 class is now the old deployment class).
- Move the AOP marker to the new package (incompatible change).

Modified: trunk/tomcat/build.xml
===================================================================
--- trunk/tomcat/build.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/build.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -218,10 +218,10 @@
     <call target="compile"/>
 
     <mkdir dir="${build.lib}"/>
-    <mkdir dir="${build.deploy}/jbossweb.deployer/META-INF"/>
+    <mkdir dir="${build.deploy}/jboss-web.deployer/META-INF"/>
 
-    <!-- Build the jbossweb.deployer -->
-  	 <copy todir="${build.deploy}/jbossweb.deployer/META-INF">
+    <!-- Build the jboss-web.deployer -->
+  	 <copy todir="${build.deploy}/jboss-web.deployer/META-INF">
   	 	<fileset dir="${build.resources}">
   	 		<include name="jboss-structure.xml" />
   	 		<include name="war-deployers-beans.xml" />
@@ -229,47 +229,47 @@
   	 	</fileset>
   	 </copy>	
     <copy file="${build.resources}/context.xml"
-      todir="${build.deploy}/jbossweb.deployer"/>
+      todir="${build.deploy}/jboss-web.deployer"/>
     <copy file="${build.resources}/web.xml"
-      tofile="${build.deploy}/jbossweb.deployer/conf/web.xml"/>
+      tofile="${build.deploy}/jboss-web.deployer/conf/web.xml"/>
     <copy file="${build.resources}/server.xml"
-      tofile="${build.deploy}/jbossweb.deployer/server.xml"/>
-    <copy todir="${build.deploy}/jbossweb.deployer">
+      tofile="${build.deploy}/jboss-web.deployer/server.xml"/>
+    <copy todir="${build.deploy}/jboss-web.deployer">
       <fileset dir="${jboss.web.lib}">
         <include name="*.jar"/>
         <exclude name="annotations-api.jar"/>
       </fileset>
     </copy>
     
-    <copy todir="${build.deploy}/jbossweb.deployer"
+    <copy todir="${build.deploy}/jboss-web.deployer"
        file="${sun.jstl.lib}/jstl.jar" />        
 
-    <mkdir dir="${build.deploy}/jbossweb.deployer/jsf-libs"/>
+    <mkdir dir="${build.deploy}/jboss-web.deployer/jsf-libs"/>
    
-    <copy todir="${build.deploy}/jbossweb.deployer/jsf-libs">        
+    <copy todir="${build.deploy}/jboss-web.deployer/jsf-libs">        
        <fileset dir="${sun.jsf.lib}">           
           <include name="*.jar" />
        </fileset>
     </copy>
 
     <!-- jar for jsf integration classes -->
-    <jar jarfile="${build.deploy}/jbossweb.deployer/jsf-libs/jboss-faces.jar">
+    <jar jarfile="${build.deploy}/jboss-web.deployer/jsf-libs/jboss-faces.jar">
       <fileset dir="${build.classes}">
          <include name="org/jboss/web/jsf/integration/**"/>
       </fileset>
     </jar>
 
-    <jar jarfile="${build.deploy}/jbossweb.deployer/jbossweb-service.jar"
+    <jar jarfile="${build.deploy}/jboss-web.deployer/jboss-web-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/tc5/session/InstanceOfAopMarker*"/>
+         <exclude name="org/jboss/web/tomcat/service/session/AopMarker*"/>
+         <exclude name="org/jboss/web/tomcat/service/session/InstanceOfAopMarker*"/>
         <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/tc6/**"/>
+        <include name="org/jboss/web/tomcat/service/**"/>
         <include name="org/jboss/web/tomcat/security/**"/>
         <include name="org/jboss/web/tomcat/statistics/**"/>
       </fileset>
@@ -281,10 +281,10 @@
 
      <!-- jar for fine grained aop -->
      <!-- package in .aop. This is used for fine-grained replication -->
-     <jar destfile="${build.lib}/tc6-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"/>
@@ -292,18 +292,18 @@
      </jar>
      
     <!-- jboss-web-metamodel.jar -->
-    <jar jarfile="${build.deploy}/jbossweb.deployer/jboss-web-metamodel.jar">
+    <jar jarfile="${build.deploy}/jboss-web.deployer/jboss-web-metamodel.jar">
       <fileset dir="${build.classes}">
         <include name="org/jboss/web/metamodel/**"/>
       </fileset>    
     </jar>
 
    <!-- Cluster service sar and aop descriptor used for fine-grained replication -->
-   <mkdir dir="${build.deploy}/tc6-cluster"/>
-   <copy todir="${build.deploy}/tc6-cluster"
-         file="${build.lib}/tc6-cluster.aop"/>
-  	<copy tofile="${build.deploy}/tc6-cluster/tc6-cluster-beans.xml"
-  	      file="${build.resources}/tc6-cluster-beans.xml"/>
+   <mkdir dir="${build.deploy}/jboss-web-cluster"/>
+   <copy todir="${build.deploy}/jboss-web-cluster"
+         file="${build.lib}/jboss-web-cluster.aop"/>
+  	<copy tofile="${build.deploy}/jboss-web-cluster/jboss-web-cluster-beans.xml"
+  	      file="${build.resources}/jboss-web-cluster-beans.xml"/>
 
     <!-- Update the build marker to allow bypassing -->
     <touch file="${build-bypass.marker}"/>

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,352 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56125 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,204 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 45726 $
+ * @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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,749 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * 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: 56603 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -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: 56603 $ $Date: 2006-09-06 15:57:20 +0200 (mer., 06 sept. 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 enumeration = threadPools.elements();
+         while (enumeration.hasMoreElements())
+         {
+            ObjectName objectName = (ObjectName) enumeration.nextElement();
+            String name = objectName.getKeyProperty("name");
+            // use StatusTransformer to output status
+            StatusTransformer.writeConnectorState
+               (writer, objectName,
+                  name, mBeanServer, globalRequestProcessors,
+                  requestProcessors, mode);
+         }
+
+         if (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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/TomcatInjectionContainer.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/TomcatInjectionContainer.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/TomcatInjectionContainer.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,470 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt 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.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.AnnotationProcessor;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.ejb3.Container;
+import org.jboss.ejb3.DependencyPolicy;
+import org.jboss.ejb3.DeploymentScope;
+import org.jboss.ejb3.JmxDependencyPolicy;
+import org.jboss.ejb3.enc.DeploymentPersistenceUnitResolver;
+import org.jboss.ejb3.entity.PersistenceUnitDeployment;
+import org.jboss.injection.DependsHandler;
+import org.jboss.injection.EJBHandler;
+import org.jboss.injection.EncInjector;
+import org.jboss.injection.InjectionContainer;
+import org.jboss.injection.InjectionHandler;
+import org.jboss.injection.InjectionUtil;
+import org.jboss.injection.Injector;
+import org.jboss.injection.JndiInjectHandler;
+import org.jboss.injection.PersistenceContextHandler;
+import org.jboss.injection.PersistenceUnitHandler;
+import org.jboss.injection.ResourceHandler;
+import org.jboss.injection.WebServiceRefHandler;
+import org.jboss.logging.Logger;
+import org.jboss.metamodel.descriptor.EnvironmentRefGroup;
+import org.jboss.metamodel.descriptor.Listener;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.WebApplication;
+import org.jboss.web.metamodel.descriptor.Filter;
+import org.jboss.web.metamodel.descriptor.JBossWebDDObjectFactory;
+import org.jboss.web.metamodel.descriptor.Servlet;
+import org.jboss.web.metamodel.descriptor.WebDD;
+import org.jboss.web.metamodel.descriptor.WebDDObjectFactory;
+import org.jboss.xb.binding.JBossXBException;
+
+/**
+ * Comment
+ *
+ * @author <a href="mailto:bill at jboss.org">Bill Burke</a>
+ * @version $Revision: 1.17 $
+ */
+public class TomcatInjectionContainer implements InjectionContainer, AnnotationProcessor
+{
+   private static final Logger log = Logger.getLogger(TomcatInjectionContainer.class);
+
+   private static class EncMap extends HashMap<String, EncInjector>
+   {
+      private HashMap<String, EncInjector> added;
+
+      public void recordAdded()
+      {
+         added = new HashMap<String, EncInjector>();
+      }
+
+      public void clearAdded()
+      {
+         added = null;
+      }
+
+      public Map<String, EncInjector> getAdded()
+      {
+         return added;
+      }
+
+      @Override
+      public EncInjector put(String key, EncInjector value)
+      {
+         if (added != null)
+            added.put(key, value);
+         return super.put(key, value);
+      }
+
+      @Override
+      public void putAll(Map<? extends String, ? extends EncInjector> m)
+      {
+         if (added != null)
+            added.putAll(m);
+         super.putAll(m);
+      }
+   }
+
+   protected EncMap encInjectors = new EncMap();
+   protected Map<String, Map<AccessibleObject, Injector>> encInjections = new HashMap<String, Map<AccessibleObject, Injector>>();
+   protected Map<String, Map<AccessibleObject, Injector>> resolvedClassInjections = new HashMap<String, Map<AccessibleObject, Injector>>();
+
+   protected List<PersistenceUnitDeployment> persistenceUnitDeployments = new ArrayList<PersistenceUnitDeployment>();
+   protected LinkedHashMap ejbContainers = new LinkedHashMap(); // will always be empty.
+   protected DeploymentPersistenceUnitResolver persistenceUnitResolver;
+   protected WarEjbResolver ejbResolver;
+   protected DependencyPolicy dependencyPolicy = new JmxDependencyPolicy();
+   protected Collection<InjectionHandler> handlers;
+   protected DeploymentUnit unit;
+   protected ClassLoader webLoader;
+   protected WebApplication appInfo;
+   protected WebDD webDD;
+
+   public TomcatInjectionContainer(WebApplication appInfo, DeploymentUnit unit)
+   {
+      this.unit = unit;
+      DeploymentScope deploymentScope = null;
+      this.appInfo = appInfo;
+
+      persistenceUnitResolver = new DeploymentPersistenceUnitResolver(persistenceUnitDeployments, deploymentScope, ejbContainers);
+      ejbResolver = new WarEjbResolver(deploymentScope, unit.getSimpleName());
+   }
+
+   public EnvironmentRefGroup getEnvironmentRefGroup()
+   {
+      return webDD;
+   }
+
+   public void postConstruct(Object object) throws IllegalAccessException, InvocationTargetException
+   {
+      // ignore for now
+   }
+
+   public void preDestroy(Object object) throws IllegalAccessException, InvocationTargetException
+   {
+      // ignore for now
+   }
+
+   /**
+    * When we get here, we are assuming that any XML defined ENC has been set up.  We will set up more here
+    * if the class being processed is a JSP
+    *
+    *
+    * @param object
+    * @throws IllegalAccessException
+    * @throws InvocationTargetException
+    * @throws NamingException
+    */
+   public void processAnnotations(Object object) throws IllegalAccessException, InvocationTargetException, NamingException
+   {
+      log.debug("**************** Processing annotations for: " + object.getClass().getName());
+      Map<AccessibleObject, Injector> injectors = resolvedClassInjections.get(object.getClass().getName());
+      if (injectors == null)
+      {
+         log.debug("-- there was no prior annotation preprocessing done");
+         encInjectors.recordAdded();
+         // let's assume this is a JSP or some other artifact that cannot be found within XML
+         injectors = InjectionUtil.processAnnotations(this, handlers, object.getClass());
+         resolvedClassInjections.put(object.getClass().getName(), injectors);
+
+         // only execute injectors that were added additionally
+         if (encInjectors.getAdded().size() > 0)
+         {
+            for (EncInjector encInjector : encInjectors.getAdded().values())
+            {
+               encInjector.inject(this);
+            }
+            encInjectors.clearAdded();
+         }
+      }
+      if (injectors == null || injectors.size() == 0)
+      {
+         log.debug("-- no injectors found: " + injectors);
+         return;
+      }
+
+      log.debug("-- doing injections");
+      for (Injector injector : injectors.values())
+      {
+         injector.inject(object);
+      }
+   }
+
+   public void populateEnc(ClassLoader loader)
+   {
+      for (EncInjector injector : encInjectors.values())
+      {
+         injector.inject(this);
+      }
+   }
+
+   private InputStream[] getInputStreams()
+   {
+      InputStream jbossWebIS = null;
+      InputStream webIS = null;
+
+      try
+      {
+         VirtualFile webDD = unit.getMetaDataFile("web.xml");
+         if (webDD != null)
+            webIS = webDD.openStream();
+      }
+      catch (IOException e)
+      {
+         log.debug("Failed to find web.xml");
+      }
+      try
+      {
+         VirtualFile webDD = unit.getMetaDataFile("jboss-web.xml");
+         if (webDD != null)
+            jbossWebIS = webDD.openStream();
+      }
+      catch (IOException e)
+      {
+         log.debug("Failed to find jboss-web.xml");
+      }
+
+      InputStream[] streams = { webIS, jbossWebIS };
+      return streams;
+   }
+
+   /**
+    * introspects EJB container to find all dependencies
+    * and initialize any extra metadata.
+    * <p/>
+    * This must be called before container is registered with any microcontainer
+    *
+    * @param dependencyPolicy
+    */
+   public void processMetadata()
+   {
+      InputStream[] streams = getInputStreams();
+      if (streams[0] != null)
+      {
+         try
+         {
+            webDD = WebDDObjectFactory.parse(streams[0]);
+            if (streams[1] != null)
+            {
+               webDD = JBossWebDDObjectFactory.parse(webDD, streams[1]);
+            }
+         }
+         catch (JBossXBException e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      else
+      {
+         throw new RuntimeException("web.xml is required");
+      }
+
+      // XML must be done first so that any annotation overrides are initialized
+
+      // todo injection handlers should be pluggable from XML
+      handlers = new ArrayList<InjectionHandler>();
+      handlers.add(new EJBHandler());
+      handlers.add(new DependsHandler());
+      handlers.add(new JndiInjectHandler());
+      handlers.add(new PersistenceContextHandler());
+      handlers.add(new PersistenceUnitHandler());
+      handlers.add(new ResourceHandler());
+      handlers.add(new WebServiceRefHandler());
+
+      ClassLoader old = Thread.currentThread().getContextClassLoader();
+      ClassLoader webLoader = getClassloader();
+      Thread.currentThread().setContextClassLoader(webLoader);
+      try
+      {
+         for (InjectionHandler handler : handlers)
+            handler.loadXml(webDD, this);
+
+         for (Object obj : webDD.getServlets())
+         {
+            Servlet servlet = (Servlet)obj;
+            try
+            {
+               if (servlet.getServletClass() == null)
+                  continue; // jsp
+               if (resolvedClassInjections.containsKey(servlet.getServletClass()))
+                  continue;
+               Class servletClass = webLoader.loadClass(servlet.getServletClass());
+               Map<AccessibleObject, Injector> tmp = InjectionUtil.processAnnotations(this, handlers, servletClass);
+               resolvedClassInjections.put(servlet.getServletClass(), tmp);
+            }
+            catch (ClassNotFoundException e)
+            {
+               log.warn("could not find servlet class " + servlet.getServletClass() + " in classpath when processing annotations.");
+            }
+
+         }
+         for (Object obj : webDD.getFilters())
+         {
+            Filter filter = (Filter)obj;
+            try
+            {
+               if (resolvedClassInjections.containsKey(filter.getFilterClass()))
+                  continue;
+               Class servletClass = webLoader.loadClass(filter.getFilterClass());
+               Map<AccessibleObject, Injector> tmp = InjectionUtil.processAnnotations(this, handlers, servletClass);
+               resolvedClassInjections.put(filter.getFilterClass(), tmp);
+            }
+            catch (ClassNotFoundException e)
+            {
+               throw new RuntimeException("could not find filter class in classpath", e);
+            }
+         }
+         for (Object obj : webDD.getListeners())
+         {
+            Listener listener = (Listener)obj;
+            try
+            {
+               if (resolvedClassInjections.containsKey(listener.getListenerClass()))
+                  continue;
+               Class servletClass = webLoader.loadClass(listener.getListenerClass());
+               Map<AccessibleObject, Injector> tmp = InjectionUtil.processAnnotations(this, handlers, servletClass);
+               resolvedClassInjections.put(listener.getListenerClass(), tmp);
+            }
+            catch (ClassNotFoundException e)
+            {
+               throw new RuntimeException("could not find listener class in classpath", e);
+            }
+         }
+      }
+      finally
+      {
+         Thread.currentThread().setContextClassLoader(old);
+      }
+   }
+
+   public Map<String, EncInjector> getEncInjectors()
+   {
+      return encInjectors;
+   }
+
+   public Map<String, Map<AccessibleObject, Injector>> getEncInjections()
+   {
+      return encInjections;
+   }
+
+   // EncInjectors/Handlers may need to add extra instance injectors
+   public List<Injector> getInjectors()
+   {
+      return new ArrayList<Injector>(); // no equivalent in WAR
+   }
+
+   public VirtualFile getRootFile()
+   {
+      return unit.getDeploymentContext().getRoot();
+   }
+
+   public String getIdentifier()
+   {
+      return unit.getSimpleName();
+   }
+
+   public String getDeploymentDescriptorType()
+   {
+      return "web.xml";
+   }
+
+   public ClassLoader getClassloader()
+   {
+      return webLoader;
+   }
+
+   public void setClassLoader(ClassLoader loader)
+   {
+      this.webLoader = loader;
+   }
+
+   public Context getEnc()
+   {
+      ClassLoader old = Thread.currentThread().getContextClassLoader();
+      try
+      {
+         Thread.currentThread().setContextClassLoader(getClassloader());
+         try
+         {
+            return (Context)new InitialContext().lookup("java:comp");
+         }
+         catch (NamingException e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      finally
+      {
+         Thread.currentThread().setContextClassLoader(old);
+      }
+   }
+
+   public PersistenceUnitDeployment getPersistenceUnitDeployment(String unitName) throws NameNotFoundException
+   {
+      return persistenceUnitResolver.getPersistenceUnitDeployment(unitName);
+   }
+
+   public Container resolveEjbContainer(String link, Class businessIntf)
+   {
+      return ejbResolver.getEjbContainer(link, businessIntf);
+   }
+
+   public Container resolveEjbContainer(Class businessIntf) throws NameNotFoundException
+   {
+      return ejbResolver.getEjbContainer(businessIntf);
+   }
+
+   public String getEjbJndiName(Class businessInterface) throws NameNotFoundException
+   {
+      return ejbResolver.getEjbJndiName(businessInterface);
+   }
+
+   public String getEjbJndiName(String link, Class businessInterface)
+   {
+      return ejbResolver.getEjbJndiName(link, businessInterface);
+   }
+
+   public <T extends Annotation> T getAnnotation(Class<T> annotationType, Class<?> clazz)
+   {
+      return clazz.getAnnotation(annotationType);
+   }
+
+   public <T extends Annotation> T getAnnotation(Class<T> annotationType, Class<?> clazz, Method method)
+   {
+      return method.getAnnotation(annotationType);
+   }
+
+   public <T extends Annotation> T getAnnotation(Class<T> annotationType, Method method)
+   {
+      return method.getAnnotation(annotationType);
+   }
+
+   public <T extends Annotation> T getAnnotation(Class<T> annotationType, Class<?> clazz, Field field)
+   {
+      return field.getAnnotation(annotationType);
+   }
+
+   public <T extends Annotation> T getAnnotation(Class<T> annotationType, Field field)
+   {
+      return field.getAnnotation(annotationType);
+   }
+
+   public DependencyPolicy getDependencyPolicy()
+   {
+      return dependencyPolicy;
+   }
+
+}
\ No newline at end of file


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WarEjbResolver.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/WarEjbResolver.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/WarEjbResolver.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,52 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.ejb3.enc.DeploymentEjbResolver;
+import org.jboss.ejb3.EJBContainer;
+import org.jboss.ejb3.DeploymentScope;
+
+import javax.naming.NameNotFoundException;
+
+/**
+ * Comment
+ *
+ * @author <a href="mailto:bill at jboss.org">Bill Burke</a>
+ * @version $Revision: 1.17 $
+ */
+public class WarEjbResolver extends DeploymentEjbResolver
+{
+   public WarEjbResolver(DeploymentScope deploymentScope, String errorName)
+   {
+      super(deploymentScope, errorName);
+   }
+
+   protected EJBContainer searchDeploymentInternally(String ejbLink, Class businessIntf)
+   {
+      return null;
+   }
+
+   protected EJBContainer searchForEjbContainerInternally(Class businessIntf) throws NameNotFoundException
+   {
+      return null;
+   }
+}


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,96 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.net.URL;
+
+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: 58290 $
+ */
+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;
+   }
+
+   @Override
+   public void addURL(URL url)
+   {
+      super.addURL(url);
+   }
+
+   /**
+    * 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,107 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.net.URL;
+import java.util.List;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.loader.WebappLoader;
+import org.jboss.logging.Logger;
+
+/**
+ * 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: 58290 $
+ */
+public class WebAppLoader extends WebappLoader
+{
+   private static final Logger log = Logger.getLogger(WebAppLoader.class);
+
+   private String[] filteredPackages = {
+           "org.apache.commons.logging"
+   };
+   private List<URL> classpath;
+
+   private TomcatInjectionContainer injectionContainer;
+
+   public WebAppLoader()
+   {
+      super();
+      setLoaderClass(WebAppClassLoader.class.getName());
+   }
+
+   public WebAppLoader(ClassLoader parent, String[] filteredPackages)
+   {
+      this(parent, filteredPackages, null);
+   }
+   public WebAppLoader(ClassLoader parent, String[] filteredPackages, TomcatInjectionContainer container)
+   {
+      super(parent);
+      setLoaderClass(WebAppClassLoader.class.getName());
+      this.filteredPackages = filteredPackages;
+      injectionContainer = container;
+   }
+
+   /**
+    * Use an explicit classpath
+    * 
+    * @param classpath
+    */
+   public void setClasspath(List<URL> classpath)
+   {
+      this.classpath = classpath;
+   }
+
+   /**
+    * 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);
+         if( classpath != null )
+         {
+            for(URL url : classpath)
+            {
+               webLoader.addURL(url);
+            }
+         }
+      }
+      if (injectionContainer != null)
+      {
+         log.debug("injectionContainer enabled and processing beginning with Tomcat WebAppLoader");
+         // we need to do this because the classloader is initialize by the web container and
+         // the injection container needs the classloader so that it can build up Injectors and ENC populators
+         injectionContainer.setClassLoader(getClassLoader());
+         injectionContainer.processMetadata();
+      }
+   }
+}


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,357 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt 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 java.util.List;
+
+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;
+import org.jboss.logging.Logger;
+
+/**
+ * Initial version of a JBoss implementation of the Tomcat Loader.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 58290 $
+ */
+public class WebCtxLoader
+   implements Lifecycle, Loader
+{
+   private static final Logger log = Logger.getLogger(WebCtxLoader.class);
+   /**
+    * 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;
+   protected TomcatInjectionContainer injectionContainer;
+
+   /**
+    * 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
+    */
+   public WebCtxLoader(ClassLoader encLoader)
+   {
+      this(encLoader, null);
+   }
+   public WebCtxLoader(ClassLoader encLoader, TomcatInjectionContainer container)
+   {
+      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;
+      injectionContainer = container;
+   }
+
+   /**
+    * Use an explicit classpath
+    * 
+    * @param classpath
+    */
+   public void setClasspath(List<URL> classpath)
+   {
+      for(URL path : classpath)
+      {
+         delegate.addURL(path);
+         ctxLoader.addURLInternal(path);
+      }
+   }
+
+   /**
+    * Build the classpath from the war url WEB-INF/{classes,lib}. 
+    * 
+    * @param warURL
+    * @throws MalformedURLException
+    */
+   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();
+      if (injectionContainer != null)
+      {
+         log.debug("injectionContainer enabled and processing beginning with JBoss WebCtxLoader");
+         // we need to do this because the classloader is initialize by the web container and
+         // the injection container needs the classloader so that it can build up Injectors and ENC populators
+         injectionContainer.setClassLoader(getClassLoader());
+         injectionContainer.processMetadata();
+      }
+      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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/DeployerConfig.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/DeployerConfig.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/DeployerConfig.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,382 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.deployers; 
+
+import java.net.URL;
+
+import javax.management.ObjectName;
+ 
+import org.jboss.metadata.WebMetaData;
+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: 56125 $
+ */
+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;
+
+   /**
+    * Shared WebMetaData.
+    */
+   private WebMetaData sharedMetaData = 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 URL xacmlPolicyURL = null;
+   
+   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;
+   }
+
+   public URL getXacmlPolicyURL() 
+   {
+	  return xacmlPolicyURL;
+   }
+
+   public void setXacmlPolicyURL(URL xacmlPolicyURL) 
+   {
+	 this.xacmlPolicyURL = xacmlPolicyURL;
+   }
+
+   public WebMetaData getSharedMetaData()
+   {
+      return sharedMetaData;
+   }
+
+   public void setSharedMetaData(WebMetaData sharedMetaData)
+   {
+      this.sharedMetaData = sharedMetaData;
+   }   
+}


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,280 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt 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.deployers;
+
+import java.util.Iterator;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.ContextConfig;
+import org.jboss.metadata.Listener;
+import org.jboss.metadata.NameValuePair;
+import org.jboss.metadata.SecurityRoleRefMetaData;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.metadata.WebSecurityMetaData;
+import org.jboss.metadata.WebSecurityMetaData.WebResourceCollection;
+import org.jboss.metadata.web.ErrorPage;
+import org.jboss.metadata.web.Filter;
+import org.jboss.metadata.web.FilterMapping;
+import org.jboss.metadata.web.LocaleEncodingMapping;
+import org.jboss.metadata.web.LoginConfig;
+import org.jboss.metadata.web.MimeMapping;
+import org.jboss.metadata.web.ParamValue;
+import org.jboss.metadata.web.Servlet;
+import org.jboss.metadata.web.ServletMapping;
+import org.jboss.metadata.web.SessionConfig;
+import org.jboss.metadata.web.Taglib;
+
+public class JBossContextConfig extends ContextConfig {
+
+	public static ThreadLocal<WebMetaData> metaDataLocal = new ThreadLocal<WebMetaData>();
+   public static ThreadLocal<WebMetaData> metaDataShared = new ThreadLocal<WebMetaData>();
+
+    @Override
+	protected void applicationWebConfig() {
+      processWebMetaData(metaDataLocal.get());
+   }
+    
+    
+   @Override
+   protected void defaultWebConfig()
+   {
+      processWebMetaData(metaDataShared.get());
+   }
+
+
+   protected void processWebMetaData(WebMetaData metaData) {   
+   
+      if (context instanceof StandardContext) {
+         ((StandardContext) context).setReplaceWelcomeFiles(true);
+      }
+
+      // Metadata complete
+      context.setIgnoreAnnotations(metaData.isMetadataComplete());
+    	
+    	// Context params
+    	Iterator<ParamValue> iterator = metaData.getContextParams().iterator();
+    	while (iterator.hasNext()) {
+    		ParamValue paramValue = iterator.next();
+    		context.addParameter(paramValue.getName(), paramValue.getValue());
+    	}
+    	
+    	// Display name
+    	context.setDisplayName(metaData.getDisplayName());
+    	
+    	// Distributable
+    	context.setDistributable(metaData.getDistributable());
+    	
+    	// Error pages
+    	Iterator iterator2 = metaData.getErrorPages().iterator();
+    	while (iterator2.hasNext()) {
+    		ErrorPage value = (ErrorPage) iterator2.next();
+    		org.apache.catalina.deploy.ErrorPage errorPage =
+    			new org.apache.catalina.deploy.ErrorPage();
+    		errorPage.setErrorCode(value.getErrorCode());
+         errorPage.setExceptionType(value.getExceptionType());
+    		errorPage.setLocation(value.getLocation());
+    		context.addErrorPage(errorPage);
+    	}
+    	
+    	// Filter definitions
+    	Iterator<Filter> iterator3 = metaData.getFilters().iterator();
+    	while (iterator3.hasNext()) {
+    		Filter value = iterator3.next();
+    		org.apache.catalina.deploy.FilterDef filterDef =
+    			new org.apache.catalina.deploy.FilterDef();
+    		filterDef.setFilterName(value.getName());
+    		filterDef.setFilterClass(value.getFilterClass());
+    		Iterator params = value.getInitParams().iterator();
+    		while (params.hasNext()) {
+    			NameValuePair nvp = (NameValuePair) params.next();
+    			filterDef.addInitParameter(nvp.getName(), nvp.getValue());
+    		}
+    		context.addFilterDef(filterDef);
+    	}
+    	
+    	// Filter mappings
+    	Iterator<FilterMapping> iterator4 = metaData.getFilterMappings().iterator();
+    	while (iterator4.hasNext()) {
+    		FilterMapping value = iterator4.next();
+    		org.apache.catalina.deploy.FilterMap filterMap =
+    			new org.apache.catalina.deploy.FilterMap();
+    		filterMap.setFilterName(value.getFilterName());
+         Iterator<String> servletNames = value.getServletNames().iterator();
+         while (servletNames.hasNext()) {
+            filterMap.addServletName(servletNames.next());
+         }
+         Iterator<String> urlPatterns = value.getUrlPatterns().iterator();
+         while (urlPatterns.hasNext()) {
+            filterMap.addURLPattern(urlPatterns.next());
+         }
+         Iterator<String> dispatchers = value.getDispatchers().iterator();
+         while (dispatchers.hasNext()) {
+            filterMap.setDispatcher(dispatchers.next());
+         }
+    		context.addFilterMap(filterMap);
+    	}
+    	
+    	// Listeners
+    	Iterator iterator5 = metaData.getListeners().iterator();
+    	while (iterator5.hasNext()) {
+    		Listener value = (Listener) iterator5.next();
+    		context.addApplicationListener(value.getListenerClass());
+    	}
+    	
+    	// Login configuration
+    	LoginConfig loginConfig = metaData.getLoginConfig();
+    	if (loginConfig != null) {
+    		org.apache.catalina.deploy.LoginConfig loginConfig2 =
+    			new org.apache.catalina.deploy.LoginConfig();
+    		loginConfig2.setAuthMethod(loginConfig.getAuthMethod());
+    		loginConfig2.setRealmName(loginConfig.getRealmName());
+    		if (loginConfig.getFormLoginConfig() != null) {
+    			loginConfig2.setLoginPage(loginConfig.getFormLoginConfig().getLoginPage());
+    			loginConfig2.setErrorPage(loginConfig.getFormLoginConfig().getErrorPage());
+    		}
+    		context.setLoginConfig(loginConfig2);
+    	}
+    	
+      // MIME mappings
+      Iterator<MimeMapping> iterator11 = metaData.getMimeMappings().iterator();
+      while (iterator11.hasNext()) {
+         MimeMapping value = iterator11.next();
+         context.addMimeMapping(value.getExtension(), value.getMimeType());
+      }
+    	
+    	// Security constraints
+    	Iterator<WebSecurityMetaData> iterator6 = metaData.getSecurityContraints();
+    	while (iterator6.hasNext()) {
+    		WebSecurityMetaData value = iterator6.next();
+    		org.apache.catalina.deploy.SecurityConstraint constraint =
+    			new org.apache.catalina.deploy.SecurityConstraint();
+    		constraint.setUserConstraint(value.getTransportGuarantee());
+    		constraint.setAuthConstraint(value.getAuthConstraint() != null);
+    		if (value.getAuthConstraint() != null) {
+    			Iterator<String> roles = value.getAuthConstraint().getRoleNames().iterator();
+    			while (roles.hasNext()) {
+    				constraint.addAuthRole(roles.next());
+    			}
+    		}
+    		Iterator<WebResourceCollection> collections = value.getWebResources().values().iterator();
+    		while (collections.hasNext()) {
+    			WebResourceCollection collection = collections.next();
+    			org.apache.catalina.deploy.SecurityCollection collection2 = 
+    				new org.apache.catalina.deploy.SecurityCollection();
+    			collection2.setName(collection.getName());
+    			String[] methods = collection.getHttpMethods();
+    			for (int i = 0; i < methods.length; i++) {
+    				collection2.addMethod(methods[i]);
+    			}
+    			String[] urlPatterns = collection.getUrlPatterns();
+    			for (int i = 0; i < urlPatterns.length; i++) {
+    				collection2.addPattern(urlPatterns[i]);
+    			}
+    			constraint.addCollection(collection2);
+    		}
+    		context.addConstraint(constraint);
+    	}
+    	
+    	// Security roles
+    	Iterator iterator9 = metaData.getSecurityRoleNames().iterator();
+    	while (iterator9.hasNext()) {
+    		context.addSecurityRole((String) iterator9.next());
+    	}
+    	
+    	// Servlet
+    	Iterator<Servlet> iterator7 = metaData.getServlets().iterator();
+    	while (iterator7.hasNext()) {
+    		Servlet value = iterator7.next();
+    		org.apache.catalina.Wrapper wrapper = context.createWrapper();
+    		wrapper.setName(value.getName());
+    		wrapper.setServletClass(value.getServletClass());
+    		if (value.getJspFile() != null) {
+    		   wrapper.setJspFile(value.getJspFile());
+    		}
+    		wrapper.setLoadOnStartup(value.getLoadOnStartup());
+    		if (value.getRunAs() != null) {
+    			wrapper.setRunAs(value.getRunAs().getRoleName());
+    		}
+    		Iterator nvps = value.getInitParams().iterator();
+    		while (nvps.hasNext()) {
+    			NameValuePair nvp = (NameValuePair) nvps.next();
+    			wrapper.addInitParameter(nvp.getName(), nvp.getValue());
+    		}
+    		Iterator securityRoleRefs = value.getSecurityRoleRefs().iterator();
+    		while (securityRoleRefs.hasNext()) {
+    			SecurityRoleRefMetaData securityRoleRef = (SecurityRoleRefMetaData) securityRoleRefs.next();
+    			wrapper.addSecurityReference(securityRoleRef.getName(), securityRoleRef.getLink());
+    		}
+    		context.addChild(wrapper);
+    	}
+    	
+    	// Servlet mapping
+    	Iterator iterator8 = metaData.getServletMappings().iterator();
+    	while (iterator8.hasNext()) {
+    		ServletMapping value = (ServletMapping) iterator8.next();
+         Iterator<String> urlPatterns = value.getUrlPatterns().iterator();
+         while (urlPatterns.hasNext()) {
+            context.addServletMapping(urlPatterns.next(), value.getName());
+         }
+    	}
+    	
+      // JSP mappings
+      Iterator<String> iterator10 = metaData.getJspMappings().iterator();
+      while (iterator10.hasNext()) {
+         String value = iterator10.next();
+         context.addJspMapping(value);
+      }
+      
+      // Locale encoding mapping
+      Iterator<LocaleEncodingMapping> iterator12 = metaData.getLocaleEncodingMappings().iterator();
+      while (iterator12.hasNext()) {
+         LocaleEncodingMapping value = iterator12.next();
+         context.addLocaleEncodingMappingParameter(value.getLocale(), value.getEncoding());
+      }
+    	
+    	// Welcome files
+      Iterator<String> iterator13 = metaData.getWelcomeFiles().iterator();
+      while (iterator13.hasNext()) {
+         String value = iterator13.next();
+         context.addWelcomeFile(value);
+      }
+
+      // Session timeout
+      Iterator iterator14 = metaData.getSessionConfigs().iterator();
+      if (iterator14.hasNext()) {
+         SessionConfig value = (SessionConfig) iterator14.next();
+         if (value.getSessionTimeout() != null) {
+            int i = Integer.decode(value.getSessionTimeout());
+            context.setSessionTimeout(i);
+         }
+      }
+
+      // Taglib
+      Iterator<Taglib> iterator15 = metaData.getTaglibs().iterator();
+      while (iterator15.hasNext()) {
+         Taglib value = iterator15.next();
+         context.addTaglib(value.getUri(), value.getLocation());
+      }
+      
+	}
+
+}


Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployer.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployer.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployer.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,805 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.deployers;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.security.jacc.PolicyContext;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.connector.Connector;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.metadata.web.WebMetaDataObjectFactory;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+import org.jboss.system.server.Server;
+import org.jboss.system.server.ServerImplMBean;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.deployers.AbstractWarDeployer;
+import org.jboss.web.deployers.AbstractWarDeployment;
+import org.jboss.web.tomcat.security.HttpServletRequestPolicyContextHandler;
+import org.jboss.web.tomcat.service.session.SessionIDGenerator;
+import org.jboss.xb.binding.Unmarshaller;
+import org.jboss.xb.binding.UnmarshallerFactory;
+
+
+/**
+ * A concrete implementation of the AbstractWarDeployer that creates
+ * {@link #TomcatDeployment} instances as the web application bean
+ * representation.
+ * 
+ * @see #getDeployment(DeploymentUnit, WebMetaData)
+ * 
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @author Wonne.Keysers at realsoftware.be
+ * @author Dimitris.Andreadis at jboss.org
+ * @version $Revision: 56809 $
+ * @see org.jboss.web.deployers.AbstractWarDeployer
+ */
+public class TomcatDeployer extends AbstractWarDeployer
+   implements NotificationListener, TomcatDeployerMBean
+{
+   // Constants -----------------------------------------------------
+   public static final String NAME = "TomcatDeployer";
+
+   /**
+    * 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";
+
+   /** The web app context implementation class */
+   private String contextClassName =
+      "org.apache.catalina.core.StandardContext";
+
+   /**
+    * Unmarshall factory used for parsing shared web.xml.
+    */
+   private static final UnmarshallerFactory factory = UnmarshallerFactory.newInstance();
+   
+   /**
+    * 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 tomcat6 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;
+   
+   /**
+    * Shared metaData.
+    */
+   private WebMetaData sharedMetaData = 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;
+   /** The AbstractWarDeployment implementation class */
+   private Class deploymentClass = TomcatDeployment.class;
+
+   /** */
+   private String[] filteredPackages;
+
+   public TomcatDeployer()
+   {
+   }
+
+   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 Class getDeploymentClass()
+   {
+      return deploymentClass;
+   }
+   public void setDeploymentClass(Class deploymentClass)
+   {
+      this.deploymentClass = deploymentClass;
+   }
+
+   /**
+    * Start the deployer. This sets up the tomcat core.
+    */
+   public void start()
+      throws Exception
+   {
+      super.start();
+
+      log.debug("Starting tomcat deployer");
+      MBeanServer server = super.getServer();
+      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.createMBean("org.apache.tomcat.util.modeler.BaseModelMBean",
+         objectName,
+         new Object[]{"org.apache.catalina.startup.Catalina"},
+         new String[]{"java.lang.String"});
+         */
+
+      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(catalinaDomain + ":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));
+            }
+         }
+      }
+      
+      //Set up the authenticators in JNDI such that they can be configured for web apps
+      InitialContext ic = new InitialContext();
+      try
+      {
+        ic.bind("TomcatAuthenticators", this.authenticators); 
+      }
+      catch(NamingException ne)
+      {
+        if(log.isTraceEnabled())
+           log.trace("Binding Authenticators to JNDI failed",ne);
+      }
+      finally
+      { 
+          try{ ic.close();}catch(NamingException nee){}
+      }
+
+      // Register the web container JACC PolicyContextHandlers
+      HttpServletRequestPolicyContextHandler handler = new HttpServletRequestPolicyContextHandler();
+      PolicyContext.registerHandler(HttpServletRequestPolicyContextHandler.WEB_REQUEST_KEY,
+         handler, true);
+
+      // Parse shared web.xml
+      Unmarshaller unmarshaller = factory.newUnmarshaller();
+      URL webXml = this.getClass().getClassLoader().getResource("web.xml");
+      if (webXml == null) {
+         webXml = this.getClass().getClassLoader().getResource("conf/web.xml");;
+      }
+      sharedMetaData = (WebMetaData)
+         unmarshaller.unmarshal(webXml.toString(), new WebMetaDataObjectFactory(), null);
+      
+      // 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 stop()
+      throws Exception
+   {
+      MBeanServer server = super.getServer();
+      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;
+
+      // 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);
+      }
+      super.stop();
+   }
+
+   /**
+    * Start the tomcat service connectors.
+    */
+   public void startConnectors() throws Exception
+   {
+      MBeanServer server = super.getServer();
+      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();
+      }
+      /* TODO:
+      // Notify listeners that connectors have started processing requests
+      sendNotification(new Notification(TOMCAT_CONNECTORS_STARTED,
+            this, getNextNotificationSequenceNumber()));
+      */
+   }
+
+   /**
+    * Stop the tomcat service connectors.
+    */
+   public void stopConnectors() throws Exception
+   {
+      MBeanServer server = super.getServer();
+      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();
+      }
+   }
+
+   /**
+    * Used to receive notification of the server start msg so the tomcat
+    * connectors can be started after all web apps are deployed.
+    */
+   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);
+         }
+      }
+   }
+
+   /**
+    * 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 boolean parentFirst;
+
+      ENCLoader(URL[] urls, ClassLoader parent, boolean parentFirst)
+      {
+         super(urls, parent);
+         this.parentFirst = parentFirst;
+      }
+
+      protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
+      {
+         if (parentFirst) return super.loadClass(name, resolve);
+         try
+         {
+            Class clazz = findClass(name);
+            if (resolve) {
+                resolveClass(clazz);
+            }
+            return clazz;
+         }
+         catch (ClassNotFoundException e)
+         {
+            return getParent().loadClass(name);
+         }
+      }
+
+   }
+
+   public static ClassLoader getTmpLoader(URL warURL, ClassLoader parent, boolean parentFirst) throws Exception
+   {
+
+      String path = warURL.getFile();
+      ArrayList<URL> list = new ArrayList<URL>();
+      File classesDir = new File(path, "WEB-INF/classes");
+      if (classesDir.exists())
+      {
+         list.add(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"))
+            {
+               list.add(jar.toURL());
+            }
+         }
+      }
+      ENCLoader loader = new ENCLoader(list.toArray(new URL[list.size()]), parent, parentFirst);
+      return loader;
+
+   }
+
+   /**
+    * Create a tomcat war deployment bean for the deployment unit/metaData.
+    * @param unit - the current web app deployment unit
+    * @param metaData - the parsed metdata for the web app deployment
+    * @return TomcatDeployment instnace
+    */
+   @Override
+   public AbstractWarDeployment getDeployment(DeploymentUnit unit, WebMetaData metaData)
+      throws Exception
+   {
+      AbstractWarDeployment deployment = (AbstractWarDeployment) deploymentClass.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(null);
+      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);
+      config.setSharedMetaData(sharedMetaData);
+      
+      //Check if there are any xacml policy files
+      VirtualFile vf = unit.getMetaDataFile("jboss-xacml-policy.xml");
+      if(vf != null)
+        config.setXacmlPolicyURL(vf.toURL());
+      
+      deployment.setServer(super.getServer());
+      deployment.init(config);
+
+      return deployment;
+   }
+   
+   public void create() throws Exception
+   {
+      MBeanServer server = MBeanServerLocator.locateJBoss();
+      if (server != null)
+         server.registerMBean(this, OBJECT_NAME);
+   }
+
+   public void destroy() throws Exception
+   {
+      MBeanServer server = MBeanServerLocator.locateJBoss();
+      if (server != null)
+         server.unregisterMBean(OBJECT_NAME);
+   } 
+}
\ No newline at end of file


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,223 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.deployers;
+
+import javax.management.ObjectName;
+
+import org.jboss.mx.util.ObjectNameFactory;
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+import org.jboss.web.AbstractWebContainerMBean;
+import org.jboss.web.deployers.AbstractWarDeployerMBean;
+
+/**
+ * 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: 45726 $
+ * @see AbstractWebContainerMBean
+ */
+public interface TomcatDeployerMBean extends AbstractWarDeployerMBean
+{
+   // default object name
+   public static final ObjectName OBJECT_NAME = ObjectNameFactory.create("jboss.web:service=WebServer");
+   
+   /** 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.service.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.service.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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,702 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt 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.deployers;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.security.CodeSource;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+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.MBeanServer;
+import javax.management.ObjectName;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.LinkRef;
+
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.core.StandardContext;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.metadata.serviceref.ServiceRefDelegate;
+import org.jboss.metadata.serviceref.VirtualFileAdaptor;
+import org.jboss.metamodel.descriptor.EnvironmentRefGroup;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.naming.NonSerializableFactory;
+import org.jboss.security.AuthorizationManager;
+import org.jboss.security.authorization.PolicyRegistration;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.WebApplication;
+import org.jboss.web.deployers.AbstractWarDeployment;
+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.TomcatInjectionContainer;
+import org.jboss.web.tomcat.service.WebAppLoader;
+import org.jboss.web.tomcat.service.WebCtxLoader;
+import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+import org.jboss.web.tomcat.service.session.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.JBossCacheManager;
+import org.jboss.ws.integration.ServiceRefMetaData;
+import org.jboss.ws.integration.UnifiedVirtualFile;
+import org.omg.CORBA.ORB;
+
+/**
+ * A tomcat web application deployment.
+ * 
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @version $Revision: 56605 $
+ */
+public class TomcatDeployment extends AbstractWarDeployment
+{
+   private static final Logger log = Logger.getLogger(TomcatDeployment.class);
+
+   /**
+    * 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();
+   private ORB orb = null;
+   private TomcatInjectionContainer injectionContainer;
+
+   public ORB getORB()
+   {
+      return orb;
+   }
+
+   public void setORB(ORB orb)
+   {
+      this.orb = orb;
+   }
+
+   @Override
+   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());
+   }
+
+   @Override
+   protected void performDeploy(WebApplication webApp, String warUrl) throws Exception
+   {
+      // Decode the URL as tomcat can't deal with paths with escape chars
+      warUrl = URLDecoder.decode(warUrl, "UTF-8");
+      webApp.setDomain(config.getCatalinaDomain());
+      WebMetaData metaData = webApp.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, webApp, warUrl);
+      while (hostNames.hasNext())
+      {
+         String additionalHostName = hostNames.next().toString();
+         performDeployInternal(additionalHostName, webApp, warUrl);
+      }
+   }
+
+   protected void performDeployInternal(String hostName, WebApplication webApp, String warUrl) throws Exception
+   {
+
+      WebMetaData metaData = webApp.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 + ", vfsUrl=" + webApp.getDeploymentUnit().getFile("").getPathName());
+
+      URL url = new URL(warUrl);
+
+      ClassLoader loader = Thread.currentThread().getContextClassLoader();
+      metaData.setContextLoader(loader);
+
+      injectionContainer = new TomcatInjectionContainer(webApp, webApp.getDeploymentUnit());
+
+      Loader webLoader = webApp.getDeploymentUnit().getAttachment(Loader.class);
+      if (webLoader == null)
+         webLoader = getWebLoader(webApp.getDeploymentUnit(), loader, url);
+
+      webApp.setName(url.getPath());
+      webApp.setClassLoader(loader);
+      webApp.setURL(url);
+
+      String objectNameS = config.getCatalinaDomain() + ":j2eeType=WebModule,name=//" + ((hostName == null) ? "localhost" : hostName) + ctxPath
+            + ",J2EEApplication=none,J2EEServer=none";
+
+      ObjectName objectName = new ObjectName(objectNameS);
+
+      StandardContext context = (StandardContext)Class.forName(config.getContextClassName()).newInstance();
+      Registry.getRegistry().registerComponent(context, objectName, config.getContextClassName());
+
+      // Find and set warInfo file on the context
+      // If WAR is packed, expand warInfo file to temp folder
+      String ctxConfig = null;
+      try
+      {
+         // TODO: this should be input metadata
+         ctxConfig = findConfig(url);
+      }
+      catch (IOException e)
+      {
+         log.debug("No " + CONTEXT_CONFIG_FILE + " in " + url, e);
+      }
+
+      VFSDirContext resources = new VFSDirContext();
+      resources.setVirtualFile(webApp.getDeploymentUnit().getFile(""));
+      context.setResources(resources);
+
+      context.setAnnotationProcessor(injectionContainer);
+      context.setDocBase(url.getFile());
+      context.setConfigFile(ctxConfig);
+      context.setDefaultContextXml("context.xml");
+      context.setDefaultWebXml("conf/web.xml");
+      // If there is an alt-dd set it
+      if (metaData.getAltDDPath() != null)
+      {
+         log.debug("Setting altDDName to: " + metaData.getAltDDPath());
+         context.setAltDDName(metaData.getAltDDPath());
+      }
+      context.setJavaVMs(javaVMs);
+      context.setServer(serverName);
+      context.setSaveConfig(false);
+
+      if (webLoader != null)
+      {
+         context.setLoader(webLoader);
+      }
+      else
+      {
+         context.setParentClassLoader(loader);
+      }
+      context.setDelegate(webApp.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);
+      }
+
+      context.setCompilerClasspath(classpath.toString());
+
+      // Set the session cookies flag according to metadata
+      switch (metaData.getSessionCookies())
+      {
+         case WebMetaData.SESSION_COOKIES_ENABLED:
+            context.setCookies(true);
+            log.debug("Enabling session cookies");
+            break;
+         case WebMetaData.SESSION_COOKIES_DISABLED:
+            context.setCookies(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, cs);
+      context.addValve(jaccValve);
+
+      // Set listener
+      context.setConfigClass("org.jboss.web.tomcat.service.deployers.JBossContextConfig");
+      context.addLifecycleListener(new EncListener(loader, webLoader, webApp));
+
+      // Pass the metadata to the RunAsListener via a thread local
+      RunAsListener.metaDataLocal.set(metaData);
+      JBossContextConfig.metaDataLocal.set(metaData);
+      JBossContextConfig.metaDataShared.set(config.getSharedMetaData());
+      try
+      {
+         // Init the container; this will also start it
+         context.init();
+         // Start it
+         context.start();
+         // Build the ENC
+      }
+      finally
+      {
+         RunAsListener.metaDataLocal.set(null);
+         JBossContextConfig.metaDataLocal.set(null);
+         JBossContextConfig.metaDataShared.set(null);
+      }
+
+      // 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();
+            String name = "//" + ((hostName == null) ? "localhost" : hostName) + ctxPath;
+            manager.init(name, metaData, config.isUseJK(), config.isUseLocalCache());
+
+            if (manager instanceof JBossCacheManager)
+            {
+               String snapshotMode = config.getSnapshotMode();
+               int snapshotInterval = config.getSnapshotInterval();
+               JBossCacheManager jbcm = (JBossCacheManager)manager;
+               jbcm.setSnapshotMode(snapshotMode);
+               jbcm.setSnapshotInterval(snapshotInterval);
+            }
+
+            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" });
+
+      /* TODO: 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");
+       }
+       */
+
+      webApp.setAppData(objectName);
+
+      /*
+       * TODO: Create mbeans for the servlets 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()); }
+       */
+
+      if (metaData.getSecurityDomain() != null)
+      {
+         String secDomain = org.jboss.security.Util.unprefixSecurityDomain(metaData.getSecurityDomain());
+         // Associate the Context Id with the Security Domain
+         String contextID = metaData.getJaccContextID();
+
+         //Check if an xacml policy file is present
+         URL xacmlPolicyFile = this.config.getXacmlPolicyURL();
+         if (xacmlPolicyFile != null)
+         {
+            AuthorizationManager authzmgr = org.jboss.security.Util.getAuthorizationManager(secDomain);
+            if (authzmgr instanceof PolicyRegistration)
+            {
+               PolicyRegistration xam = (PolicyRegistration)authzmgr;
+               xam.registerPolicy(contextID, xacmlPolicyFile);
+            }
+         }
+      }
+
+      log.debug("Initialized: " + webApp + " " + objectName);
+   }
+
+   public class EncListener implements LifecycleListener
+   {
+      protected ClassLoader loader;
+      protected Loader webLoader;
+      protected WebApplication webApp;
+      protected WebMetaData metaData;
+      protected DeploymentUnit unit;
+
+      public EncListener(ClassLoader loader, Loader webLoader, WebApplication webApp)
+      {
+         this.loader = loader;
+         this.webLoader = webLoader;
+         this.webApp = webApp;
+         this.metaData = webApp.getMetaData();
+         this.unit = webApp.getDeploymentUnit();
+      }
+
+      public void lifecycleEvent(LifecycleEvent event)
+      {
+
+         if (event.getType().equals(StandardContext.AFTER_START_EVENT))
+         {
+
+            // make the context class loader known to the WebMetaData, ws4ee needs it
+            // to instanciate service endpoint pojos that live in this webapp
+            metaData.setContextLoader(webLoader.getClassLoader());
+
+            Thread currentThread = Thread.currentThread();
+            ClassLoader currentLoader = currentThread.getContextClassLoader();
+            try
+            {
+               // Create a java:comp/env environment unique for the web application
+               log.debug("Creating ENC using ClassLoader: " + loader);
+               ClassLoader parent = loader.getParent();
+               while (parent != null)
+               {
+                  log.debug(".." + parent);
+                  parent = parent.getParent();
+               }
+               // TODO: The enc should be an input?
+               currentThread.setContextClassLoader(webLoader.getClassLoader());
+               metaData.setENCLoader(webLoader.getClassLoader());
+               InitialContext iniCtx = new InitialContext();
+               Context envCtx = (Context)iniCtx.lookup("java:comp");
+               // Add ORB/UserTransaction
+               ORB orb = null;
+               try
+               {
+                  ObjectName ORB_NAME = new ObjectName("jboss:service=CorbaORB");
+                  orb = (ORB)server.getAttribute(ORB_NAME, "ORB");
+                  // Bind the orb
+                  if (orb != null)
+                  {
+                     NonSerializableFactory.rebind(envCtx, "ORB", orb);
+                     log.debug("Bound java:comp/ORB");
+                  }
+               }
+               catch (Throwable t)
+               {
+                  log.debug("Unable to retrieve orb", t);
+               }
+
+               // TODO: injection, Add a link to the global transaction manager
+               envCtx.bind("UserTransaction", new LinkRef("UserTransaction"));
+               log.debug("Linked java:comp/UserTransaction to JNDI name: UserTransaction");
+               envCtx = envCtx.createSubcontext("env");
+               injectionContainer.populateEnc(webLoader.getClassLoader());
+
+               // TODO: this should be bindings in the metadata
+               currentThread.setContextClassLoader(webLoader.getClassLoader());
+               String securityDomain = metaData.getSecurityDomain();
+               log.debug("linkSecurityDomain");
+               linkSecurityDomain(securityDomain, envCtx);
+
+               // Bind <service-ref> elements
+               UnifiedVirtualFile vfsRoot = new VirtualFileAdaptor(unit.getDeploymentContext().getRoot());
+               EnvironmentRefGroup envRefGroup = injectionContainer.getEnvironmentRefGroup();
+               for (ServiceRefMetaData sref : metaData.getServiceRefs().values())
+               {
+                  String refName = sref.getServiceRefName();
+                  ServiceRefMetaData injectedRef = envRefGroup.getServiceRef(refName);
+                  if (injectedRef == null || injectedRef.isProcessed() == false)
+                  {
+                     new ServiceRefDelegate().bindServiceRef(envCtx, refName, vfsRoot, sref);
+                  }
+               }
+            }
+            catch (Throwable t)
+            {
+               log.error("ENC setup failed", t);
+            }
+            finally
+            {
+               currentThread.setContextClassLoader(currentLoader);
+            }
+
+            log.debug("injectionContainer enabled and processing beginning");
+            // we need to do this because the classloader is initialize by the web container and
+            // the injection container needs the classloader so that it can build up Injectors and ENC populators
+            injectionContainer.setClassLoader(webLoader.getClassLoader());
+            injectionContainer.processMetadata();
+
+         }
+      }
+
+   }
+
+   public Loader getWebLoader(DeploymentUnit unit, ClassLoader loader, URL url) throws MalformedURLException
+   {
+      Loader webLoader = null;
+
+      /*
+       * 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.
+       */
+      ArrayList<URL> classpath = (ArrayList<URL>)unit.getAttachment("org.jboss.web.expandedWarClasspath");
+      if (classpath == null)
+      {
+         try
+         {
+            VirtualFile classes = unit.getFile("WEB-INF/classes");
+            // Tomcat can't handle the vfs urls yet
+            URL vfsURL = classes.toURL();
+            String vfsurl = vfsURL.toString();
+            if (vfsurl.startsWith("vfs"))
+               vfsURL = new URL(vfsurl.substring(3));
+            classpath = new ArrayList<URL>();
+            classpath.add(classes.toURL());
+         }
+         catch (Exception e)
+         {
+
+         }
+      }
+
+      if (config.isUseJBossWebLoader())
+      {
+         WebCtxLoader jbossLoader = new WebCtxLoader(loader, injectionContainer);
+         if (classpath != null)
+            jbossLoader.setClasspath(classpath);
+         webLoader = jbossLoader;
+      }
+      else
+      {
+         String[] pkgs = config.getFilteredPackages();
+         WebAppLoader jbossLoader = new WebAppLoader(loader, pkgs, injectionContainer);
+         jbossLoader.setDelegate(getJava2ClassLoadingCompliance());
+         if (classpath != null)
+            jbossLoader.setClasspath(classpath);
+         webLoader = jbossLoader;
+      }
+      return webLoader;
+   }
+
+   public void setInjectionContainer(TomcatInjectionContainer container)
+   {
+      this.injectionContainer = container;
+   }
+
+   /**
+    * Called as part of the undeploy() method template to ask the subclass for
+    * perform the web container specific undeployment steps.
+    */
+   protected void performUndeploy(WebApplication warInfo, String warUrl) throws Exception
+   {
+      if (warInfo == null)
+      {
+         log.debug("performUndeploy, no WebApplication found for URL " + warUrl);
+         return;
+      }
+
+      log.info("undeploy, ctxPath=" + warInfo.getMetaData().getContextRoot() + ", vfsUrl=" + warInfo.getDeploymentUnit().getFile("").getPathName());
+
+      WebMetaData metaData = warInfo.getMetaData();
+      String hostName = null;
+      Iterator hostNames = metaData.getVirtualHosts();
+      if (hostNames.hasNext())
+      {
+         hostName = hostNames.next().toString();
+      }
+      performUndeployInternal(hostName, warUrl, warInfo);
+      while (hostNames.hasNext())
+      {
+         String additionalHostName = hostNames.next().toString();
+         performUndeployInternal(additionalHostName, warUrl, warInfo);
+      }
+
+   }
+
+   protected void performUndeployInternal(String hostName, String warUrl, WebApplication warInfo) throws Exception
+   {
+
+      WebMetaData metaData = warInfo.getMetaData();
+      String ctxPath = metaData.getContextRoot();
+
+      // TODO: Need to remove the dependency on MBeanServer
+      MBeanServer server = MBeanServerLocator.locateJBoss();
+      // 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;
+   }
+
+}


Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,670 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt 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.deployers;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.security.CodeSource;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+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.MBeanServer;
+import javax.management.ObjectName;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.LinkRef;
+
+import org.apache.catalina.Loader;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.metadata.serviceref.ServiceRefDelegate;
+import org.jboss.metadata.serviceref.VirtualFileAdaptor;
+import org.jboss.metamodel.descriptor.EnvironmentRefGroup;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.naming.NonSerializableFactory;
+import org.jboss.security.AuthorizationManager;
+import org.jboss.security.authorization.PolicyRegistration;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.WebApplication;
+import org.jboss.web.deployers.AbstractWarDeployment;
+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.TomcatInjectionContainer;
+import org.jboss.web.tomcat.service.WebAppLoader;
+import org.jboss.web.tomcat.service.WebCtxLoader;
+import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+import org.jboss.web.tomcat.service.session.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.JBossCacheManager;
+import org.jboss.ws.integration.ServiceRefMetaData;
+import org.jboss.ws.integration.UnifiedVirtualFile;
+import org.omg.CORBA.ORB;
+
+/**
+ * A tomcat web application deployment.
+ * 
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @version $Revision: 56605 $
+ */
+public class TomcatDeployment2 extends AbstractWarDeployment
+{
+   private static final Logger log = Logger.getLogger(TomcatDeployment2.class);
+
+   /**
+    * 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();
+   private ORB orb = null;
+   private TomcatInjectionContainer injectionContainer;
+
+   public ORB getORB()
+   {
+      return orb;
+   }
+
+   public void setORB(ORB orb)
+   {
+      this.orb = orb;
+   }
+
+   @Override
+   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());
+   }
+
+   @Override
+   protected void performDeploy(WebApplication webApp, String warUrl) throws Exception
+   {
+      // Decode the URL as tomcat can't deal with paths with escape chars
+      warUrl = URLDecoder.decode(warUrl, "UTF-8");
+      webApp.setDomain(config.getCatalinaDomain());
+      WebMetaData metaData = webApp.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, webApp, warUrl);
+      while (hostNames.hasNext())
+      {
+         String additionalHostName = hostNames.next().toString();
+         performDeployInternal(additionalHostName, webApp, warUrl);
+      }
+   }
+
+   protected void performDeployInternal(String hostName, WebApplication webApp, String warUrl) throws Exception
+   {
+      DeploymentUnit unit = webApp.getDeploymentUnit();
+      WebMetaData metaData = webApp.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();
+      metaData.setContextLoader(loader);
+
+      injectionContainer = new TomcatInjectionContainer(webApp, webApp.getDeploymentUnit());
+
+      Loader webLoader = webApp.getDeploymentUnit().getAttachment(Loader.class);
+      if (webLoader == null)
+         webLoader = getWebLoader(webApp.getDeploymentUnit(), loader, url);
+
+      webApp.setName(url.getPath());
+      webApp.setClassLoader(loader);
+      webApp.setURL(url);
+
+      String objectNameS = config.getCatalinaDomain() + ":j2eeType=WebModule,name=//" + ((hostName == null) ? "localhost" : hostName) + ctxPath
+            + ",J2EEApplication=none,J2EEServer=none";
+
+      ObjectName objectName = new ObjectName(objectNameS);
+
+      String contextClassName = config.getContextClassName();
+      Registry.getRegistry().registerComponent(Class.forName(contextClassName).newInstance(), objectName, contextClassName);
+
+      // Find and set warInfo file on the context
+      // If WAR is packed, expand warInfo file to temp folder
+      String ctxConfig = null;
+      try
+      {
+         // TODO: this should be input metadata
+         ctxConfig = findConfig(url);
+      }
+      catch (IOException e)
+      {
+         log.debug("No " + CONTEXT_CONFIG_FILE + " in " + url, e);
+      }
+
+      if (injectionContainer != null)
+      {
+         server.setAttribute(objectName, new Attribute("annotationProcessor", injectionContainer));
+      }
+
+      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"));
+      // If there is an alt-dd set it
+      if (metaData.getAltDDPath() != null)
+      {
+         String altPath = metaData.getAltDDPath();
+         log.debug("Setting altDDName to: " + altPath);
+         server.setAttribute(objectName, new Attribute("altDDName", altPath));
+      }
+
+      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(webApp.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, 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());
+
+      // Start it
+      server.invoke(objectName, "start", new Object[] {}, new String[] {});
+      // Build the ENC
+
+      if (injectionContainer == null)
+      {
+         super.processEnc(webLoader.getClassLoader(), webApp);
+      }
+      else
+      {
+         Thread currentThread = Thread.currentThread();
+         ClassLoader currentLoader = loader;
+         try
+         {
+            // Create a java:comp/env environment unique for the web application
+            log.debug("Creating ENC using ClassLoader: " + loader);
+            ClassLoader parent = loader.getParent();
+            while (parent != null)
+            {
+               log.debug(".." + parent);
+               parent = parent.getParent();
+            }
+            // TODO: The enc should be an input?
+            currentThread.setContextClassLoader(webLoader.getClassLoader());
+            metaData.setENCLoader(webLoader.getClassLoader());
+            InitialContext iniCtx = new InitialContext();
+            Context envCtx = (Context)iniCtx.lookup("java:comp");
+            // Add ORB/UserTransaction
+            ORB orb = null;
+            try
+            {
+               ObjectName ORB_NAME = new ObjectName("jboss:service=CorbaORB");
+               orb = (ORB)server.getAttribute(ORB_NAME, "ORB");
+               // Bind the orb
+               if (orb != null)
+               {
+                  NonSerializableFactory.rebind(envCtx, "ORB", orb);
+                  log.debug("Bound java:comp/ORB");
+               }
+            }
+            catch (Throwable t)
+            {
+               log.debug("Unable to retrieve orb" + t.toString());
+            }
+
+            // TODO: injection, Add a link to the global transaction manager
+            envCtx.bind("UserTransaction", new LinkRef("UserTransaction"));
+            log.debug("Linked java:comp/UserTransaction to JNDI name: UserTransaction");
+
+            envCtx = envCtx.createSubcontext("env");
+            injectionContainer.populateEnc(webLoader.getClassLoader());
+
+            // TODO: this should be bindings in the metadata
+            currentThread.setContextClassLoader(webLoader.getClassLoader());
+            String securityDomain = metaData.getSecurityDomain();
+            log.debug("linkSecurityDomain");
+            linkSecurityDomain(securityDomain, envCtx);
+            
+            // Bind <service-ref> elements
+            UnifiedVirtualFile vfsRoot = new VirtualFileAdaptor(unit.getDeploymentContext().getRoot());
+            EnvironmentRefGroup envRefGroup = injectionContainer.getEnvironmentRefGroup();
+            for (ServiceRefMetaData sref : metaData.getServiceRefs().values())
+            {
+               String refName = sref.getServiceRefName();
+               ServiceRefMetaData injectedRef = envRefGroup.getServiceRef(refName);
+               if (injectedRef == null || injectedRef.isProcessed() == false)
+               {
+                  new ServiceRefDelegate().bindServiceRef(envCtx, refName, vfsRoot, sref);
+               }
+            }
+         }
+         finally
+         {
+            currentThread.setContextClassLoader(currentLoader);
+         }
+      }
+
+      // 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();
+            String name = "//" + ((hostName == null) ? "localhost" : hostName) + ctxPath;
+            manager.init(name, metaData, config.isUseJK(), config.isUseLocalCache());
+
+            if (manager instanceof JBossCacheManager)
+            {
+               String snapshotMode = config.getSnapshotMode();
+               int snapshotInterval = config.getSnapshotInterval();
+               JBossCacheManager jbcm = (JBossCacheManager)manager;
+               jbcm.setSnapshotMode(snapshotMode);
+               jbcm.setSnapshotInterval(snapshotInterval);
+            }
+
+            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" });
+
+      /* TODO: 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");
+       }
+       */
+
+      webApp.setAppData(objectName);
+
+      /*
+       * TODO: Create mbeans for the servlets 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()); }
+       */
+
+      if (metaData.getSecurityDomain() != null)
+      {
+         String secDomain = org.jboss.security.Util.unprefixSecurityDomain(metaData.getSecurityDomain());
+         // Associate the Context Id with the Security Domain
+         String contextID = metaData.getJaccContextID();
+
+         //Check if an xacml policy file is present
+         URL xacmlPolicyFile = this.config.getXacmlPolicyURL();
+         if (xacmlPolicyFile != null)
+         {
+            AuthorizationManager authzmgr = org.jboss.security.Util.getAuthorizationManager(secDomain);
+            if (authzmgr instanceof PolicyRegistration)
+            {
+               PolicyRegistration xam = (PolicyRegistration)authzmgr;
+               xam.registerPolicy(contextID, xacmlPolicyFile);
+            }
+         }
+      }
+
+      log.debug("Initialized: " + webApp + " " + objectName);
+   }
+
+   public Loader getWebLoader(DeploymentUnit unit, ClassLoader loader, URL url) throws MalformedURLException
+   {
+      Loader webLoader = null;
+
+      /*
+       * 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.
+       */
+      ArrayList<URL> classpath = (ArrayList<URL>)unit.getAttachment("org.jboss.web.expandedWarClasspath");
+      if (classpath == null)
+      {
+         try
+         {
+            VirtualFile classes = unit.getFile("WEB-INF/classes");
+            // Tomcat can't handle the vfs urls yet
+            URL vfsURL = classes.toURL();
+            String vfsurl = vfsURL.toString();
+            if (vfsurl.startsWith("vfs"))
+               vfsURL = new URL(vfsurl.substring(3));
+            classpath = new ArrayList<URL>();
+            classpath.add(classes.toURL());
+         }
+         catch (Exception e)
+         {
+
+         }
+      }
+
+      if (config.isUseJBossWebLoader())
+      {
+         WebCtxLoader jbossLoader = new WebCtxLoader(loader, injectionContainer);
+         if (classpath != null)
+            jbossLoader.setClasspath(classpath);
+         webLoader = jbossLoader;
+      }
+      else
+      {
+         String[] pkgs = config.getFilteredPackages();
+         WebAppLoader jbossLoader = new WebAppLoader(loader, pkgs, injectionContainer);
+         jbossLoader.setDelegate(getJava2ClassLoadingCompliance());
+         if (classpath != null)
+            jbossLoader.setClasspath(classpath);
+         webLoader = jbossLoader;
+      }
+      return webLoader;
+   }
+
+   public void setInjectionContainer(TomcatInjectionContainer container)
+   {
+      this.injectionContainer = container;
+   }
+
+   /**
+    * Called as part of the undeploy() method template to ask the subclass for
+    * perform the web container specific undeployment steps.
+    */
+   protected void performUndeploy(WebApplication warInfo, String warUrl) throws Exception
+   {
+      if (warInfo == null)
+      {
+         log.debug("performUndeploy, no WebApplication found for URL " + warUrl);
+         return;
+      }
+
+      log.info("undeploy, ctxPath=" + warInfo.getMetaData().getContextRoot() + ", warUrl=" + shortWarUrlFromServerHome(warUrl));
+
+      WebMetaData metaData = warInfo.getMetaData();
+      String hostName = null;
+      Iterator hostNames = metaData.getVirtualHosts();
+      if (hostNames.hasNext())
+      {
+         hostName = hostNames.next().toString();
+      }
+      performUndeployInternal(hostName, warUrl, warInfo);
+      while (hostNames.hasNext())
+      {
+         String additionalHostName = hostNames.next().toString();
+         performUndeployInternal(additionalHostName, warUrl, warInfo);
+      }
+
+   }
+
+   protected void performUndeployInternal(String hostName, String warUrl, WebApplication warInfo) throws Exception
+   {
+
+      WebMetaData metaData = warInfo.getMetaData();
+      String ctxPath = metaData.getContextRoot();
+
+      // TODO: Need to remove the dependency on MBeanServer
+      MBeanServer server = MBeanServerLocator.locateJBoss();
+      // 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;
+   }
+
+}


Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,854 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.deployers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import javax.naming.CompositeName;
+import javax.naming.Name;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NotContextException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.AttributeModificationException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributesException;
+import javax.naming.directory.InvalidSearchControlsException;
+import javax.naming.directory.InvalidSearchFilterException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+import org.apache.naming.NamingContextBindingsEnumeration;
+import org.apache.naming.NamingContextEnumeration;
+import org.apache.naming.NamingEntry;
+import org.apache.naming.resources.BaseDirContext;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.ResourceAttributes;
+import org.jboss.logging.Logger;
+import org.jboss.virtual.VirtualFile;
+
+/**
+ * VFS Directory Context implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
+ */
+
+public class VFSDirContext extends BaseDirContext {
+
+	   private static final Logger log = Logger.getLogger(VFSDirContext.class);
+    
+    // ----------------------------------------------------------- Constructors
+
+
+	   /**
+	    * Builds a WAR directory context using the given environment.
+	    */
+	   public VFSDirContext() {
+		   super();
+	   }
+
+
+	   /**
+	    * Builds a WAR directory context using the given environment.
+	    */
+	   public VFSDirContext(Hashtable env) {
+		   super(env);
+	   }
+
+
+	   /**
+	    * Constructor used for returning fake subcontexts.
+	    */
+	   protected VFSDirContext(VirtualFile base) {
+		   this.base = base;
+	   }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The virtual file.
+     */
+    protected VirtualFile base = null;
+
+
+    // ------------------------------------------------------------- Properties
+    
+    
+    /**
+     * Set the virtual file root.
+     */
+    public void setVirtualFile(VirtualFile base) {
+        this.base = base;
+    }
+    
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Release any resources allocated for this directory context.
+     */
+    public void release() {
+
+        if (base != null) {
+            base.close();
+        }
+        base = null;
+        super.release();
+
+    }
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Retrieves the named object.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(String name)
+        throws NamingException {
+        return lookup(new CompositeName(name));
+    }
+
+
+    /**
+     * Retrieves the named object. If name is empty, returns a new instance 
+     * of this context (which represents the same naming context as this 
+     * context, but its environment may be modified independently and it may 
+     * be accessed concurrently).
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(Name name)
+        throws NamingException {
+        if (name.isEmpty())
+            return this;
+        VirtualFile entry = null;
+        try {
+            entry = treeLookup(name);
+        } catch (IOException e) {
+            NamingException ex = new NamingException
+                (sm.getString("resources.notFound", name));
+            ex.initCause(e);
+            throw ex;
+        }
+        if (entry == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+        
+        try {
+            if (!isFile(entry)) {
+                return new VFSDirContext(entry);
+            } else {
+                return new VFSResource(entry);
+            }
+        } catch (IOException e) {
+            NamingException ex = new NamingException
+                (sm.getString("resources.notFound", name));
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name. Both names are relative to this context. Any attributes 
+     * associated with the old name become associated with the new name. 
+     * Intermediate contexts of the old name are not changed.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(String oldName, String newName)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(String name)
+        throws NamingException {
+        return list(new CompositeName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name name)
+        throws NamingException {
+        if (name.isEmpty()) {
+            try {
+                return new NamingContextEnumeration(list(base).iterator());
+            } catch (IOException e) {
+                NamingException ex = new NamingException
+                    (sm.getString("resources.notFound", name));
+                ex.initCause(e);
+                throw ex;
+            }
+        }
+        VirtualFile entry = null;
+        try {
+            entry = treeLookup(name);
+        } catch (IOException e) {
+            NamingException ex = new NamingException
+                (sm.getString("resources.notFound", name));
+            ex.initCause(e);
+            throw ex;
+        }
+        if (entry == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+        try {
+            return new NamingContextEnumeration(list(entry).iterator());
+        } catch (IOException e) {
+            NamingException ex = new NamingException
+                (sm.getString("resources.notFound", name));
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(String name)
+        throws NamingException {
+        return listBindings(new CompositeName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+        throws NamingException {
+        if (name.isEmpty()) {
+            try {
+                return new NamingContextBindingsEnumeration(list(base).iterator(),
+                        this);
+            } catch (IOException e) {
+                NamingException ex = new NamingException
+                    (sm.getString("resources.notFound", name));
+                ex.initCause(e);
+                throw ex;
+            }
+        }
+        VirtualFile entry = null;
+        try {
+            entry = treeLookup(name);
+        } catch (IOException e) {
+            NamingException ex = new NamingException
+                (sm.getString("resources.notFound", name));
+            ex.initCause(e);
+            throw ex;
+        }
+        if (entry == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+        try {
+            return new NamingContextBindingsEnumeration(list(entry).iterator(),
+                    this);
+        } catch (IOException e) {
+            NamingException ex = new NamingException
+                (sm.getString("resources.notFound", name));
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any 
+     * attributes associated with the name are also removed. Intermediate 
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * In a federated naming system, a context from one naming system may be 
+     * bound to a name in another. One can subsequently look up and perform 
+     * operations on the foreign context using a composite name. However, an 
+     * attempt destroy the context using this composite name will fail with 
+     * NotContextException, because the foreign context is not a "subcontext" 
+     * of the context in which it is bound. Instead, use unbind() to remove 
+     * the binding of the foreign context. Destroying the foreign context 
+     * requires that the destroySubcontext() be performed on a context from 
+     * the foreign context's "native" naming system.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name. If the object bound to name is not a 
+     * link, returns the object itself.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(String name)
+        throws NamingException {
+        // Note : Links are not supported
+        return lookup(name);
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in 
+     * their respective namespaces. For example, an LDAP entry has a 
+     * distinguished name, and a DNS record has a fully qualified name. This 
+     * method allows the client application to retrieve this name. The string 
+     * returned by this method is not a JNDI composite name and should not be 
+     * passed directly to context methods. In naming systems for which the 
+     * notion of full name does not make sense, 
+     * OperationNotSupportedException is thrown.
+     * 
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does 
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+        throws NamingException {
+        return docBase;
+    }
+
+
+    // ----------------------------------------------------- DirContext Methods
+
+
+    /**
+     * Retrieves selected attributes associated with a named object. 
+     * See the class description regarding attribute models, attribute type 
+     * names, and operational attributes.
+     * 
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null 
+     * indicates that all attributes should be retrieved; an empty array 
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(String name, String[] attrIds)
+        throws NamingException {
+        return getAttributes(new CompositeName(name), attrIds);
+    }
+
+
+    /**
+     * Retrieves all of the attributes associated with a named object. 
+     * 
+     * @return the set of attributes associated with name. 
+     * Returns an empty attribute set if name has no attributes; never null.
+     * @param name the name of the object from which to retrieve attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name name, String[] attrIds)
+        throws NamingException {
+        
+        VirtualFile entry = null;
+        if (name.isEmpty()) {
+            entry = base;
+        } else {
+            try {
+                entry = treeLookup(name);
+            } catch (IOException e) {
+                NamingException ex = new NamingException
+                    (sm.getString("resources.notFound", name));
+                ex.initCause(e);
+                throw ex;
+            }
+        }
+        if (entry == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+        
+        ResourceAttributes attrs = new ResourceAttributes();
+        try {
+            attrs.setCreationDate(new Date(entry.getLastModified()));
+            attrs.setName(entry.getName());
+            if (isFile(entry))
+                attrs.setResourceType("");
+            attrs.setContentLength(entry.getSize());
+            attrs.setLastModified(entry.getLastModified());
+        } catch (IOException e) {
+            NamingException ex = new NamingException
+            (sm.getString("resources.notFound", name));
+            ex.initCause(e);
+            throw ex;
+        }
+
+        return attrs;
+        
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object. The order of 
+     * the modifications is not specified. Where possible, the modifications 
+     * are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE, 
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not 
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, int mod_op, Attributes attrs)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an 
+     * ordered list of modifications. The modifications are performed in the 
+     * order specified. Each modification specifies a modification operation 
+     * code and an attribute on which to operate. Where possible, the 
+     * modifications are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may 
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, ModificationItem[] mods)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes. If attrs 
+     * is null, the resulting binding will have the attributes associated 
+     * with obj if obj is a DirContext, and no attributes otherwise. If attrs 
+     * is non-null, the resulting binding will have attrs as its attributes; 
+     * any attributes associated with obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes, 
+     * overwriting any existing binding. If attrs is null and obj is a 
+     * DirContext, the attributes from obj are used. If attrs is null and obj 
+     * is not a DirContext, any existing attributes associated with the object
+     * already bound in the directory remain unchanged. If attrs is non-null, 
+     * any existing attributes associated with the object already bound in 
+     * the directory are removed and attrs is associated with the named 
+     * object. If obj is a DirContext and attrs is non-null, the attributes 
+     * of obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes. 
+     * This method creates a new subcontext with the given name, binds it in 
+     * the target context (that named by all but terminal atomic component of 
+     * the name), and associates the supplied attributes with the newly 
+     * created object. All intermediate and target contexts must already 
+     * exist. If attrs is null, this method is equivalent to 
+     * Context.createSubcontext().
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all 
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(String name, Attributes attrs)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object. The schema 
+     * describes rules regarding the structure of the namespace and the 
+     * attributes stored within it. The schema specifies what types of 
+     * objects can be added to the directory and where they can be added; 
+     * what mandatory and optional attributes an object can have. The range 
+     * of support for schemas is directory-specific.
+     * 
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named 
+     * object's class definitions.
+     * 
+     * @param name the name of the object whose object class definition is to 
+     * be retrieved
+     * @return the DirContext containing the named object's class 
+     * definitions; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(String name)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes, and retrieves selected attributes. The search is 
+     * performed using the default SearchControls settings.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates 
+     * that all attributes are to be returned; an empty array indicates that 
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes. This method returns all the attributes of such objects. 
+     * It is equivalent to supplying null as the atributesToReturn parameter 
+     * to the method search(Name, Attributes, String[]).
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be 
+     * null
+     * @param cons the search controls that control the search. If null, 
+     * the default search controls are used (equivalent to 
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy 
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter specified 
+     * is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls 
+     * contain invalid settings
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filter, 
+                                    SearchControls cons)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search. 
+     * The expression may contain variables of the form "{i}" where i is a 
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the 
+     * variables in filterExpr. The value of filterArgs[i] will replace each 
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the 
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the 
+     * filter; never null
+     * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i} 
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid 
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs 
+     * represents an invalid search filter
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filterExpr, 
+                                    Object[] filterArgs, SearchControls cons)
+        throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+    
+    /**
+     * Check if a VirtualFile is a regular file or a folder.
+     */
+    protected boolean isFile(VirtualFile file)
+       throws IOException {
+       return (file.isLeaf() || (file.getSize() > 0));
+    }
+    
+
+    /**
+     * Entry tree lookup.
+     */
+    protected VirtualFile treeLookup(Name name)
+        throws IOException {
+        if (base == null)
+            return null;
+        if (name.isEmpty())
+            return base;
+        VirtualFile currentFile = base;
+        for (int i = 0; i < name.size(); i++) {
+            if (name.get(i).length() == 0)
+                continue;
+            currentFile = currentFile.findChild(name.get(i));
+            if (currentFile == null)
+                return null;
+        }
+        return currentFile;
+    }
+
+
+    /**
+     * List children as objects.
+     */
+    protected ArrayList<NamingEntry> list(VirtualFile entry)
+        throws IOException {
+        
+        ArrayList<NamingEntry> entries = new ArrayList<NamingEntry>();
+        if (!isFile(entry)) {
+            Iterator<VirtualFile> children = entry.getChildren().iterator();
+            NamingEntry namingEntry = null;
+
+            while (children.hasNext()) {
+                VirtualFile current = children.next();
+                Object object = null;
+                if (!isFile(current)) {
+                    object = new VFSDirContext(current);
+                } else {
+                    object = new VFSResource(current);
+                }
+                namingEntry = new NamingEntry
+                (current.getName(), object, NamingEntry.ENTRY);
+                entries.add(namingEntry);
+            }
+        }
+        
+        return entries;
+        
+    }
+
+
+    // ------------------------------------------------ WARResource Inner Class
+
+
+    /**
+     * This specialized resource implementation avoids opening the IputStream
+     * to the WAR right away.
+     */
+    protected class VFSResource extends Resource {
+        
+        
+        // -------------------------------------------------------- Constructor
+        
+        
+        public VFSResource(VirtualFile entry) {
+            this.entry = entry;
+        }
+        
+        
+        // --------------------------------------------------- Member Variables
+        
+        
+        protected VirtualFile entry;
+        
+        
+        // ----------------------------------------------------- Public Methods
+        
+        
+        /**
+         * Content accessor.
+         * 
+         * @return InputStream
+         */
+        public InputStream streamContent()
+            throws IOException {
+            if (binaryContent == null) {
+                inputStream = entry.openStream();
+            }
+            return super.streamContent();
+        }
+        
+        
+    }
+
+
+}
+


Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,190 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.deployers;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.catalina.Loader;
+import org.jboss.deployers.plugins.deployer.AbstractSimpleDeployer;
+import org.jboss.deployers.spi.DeploymentException;
+import org.jboss.deployers.spi.classloader.ClassLoaderFactory;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.deployers.spi.structure.DeploymentContext;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.tomcat.service.TomcatInjectionContainer;
+import org.jboss.web.tomcat.service.WebAppLoader;
+import org.jboss.web.tomcat.service.WebCtxLoader;
+
+/**
+ * A class loader deployer for wars that handles installing a war first class
+ * loader unless the WebMetaData indicates otherwise.
+ * AbstractClassLoaderDeployer
+ * 
+ * @author Scott.Stark at jboss.org
+ * @version $Revision:$
+ */
+public class WarClassLoaderDeployer extends AbstractSimpleDeployer
+   implements ClassLoaderFactory
+{
+   /** The parent class loader first model flag */
+   private boolean java2ClassLoadingCompliance = false;
+   private boolean useJBossWebLoader;
+   /** Package names that should be ignored for class loading */
+   private String[] filteredPackages;
+
+   public WarClassLoaderDeployer()
+   {
+      // Should be after the default class loading deployer to pickup parent cl
+      super.setRelativeOrder(CLASSLOADER_DEPLOYER - 1);
+   }
+
+   public boolean isJava2ClassLoadingCompliance()
+   {
+      return java2ClassLoadingCompliance;
+   }
+   public void setJava2ClassLoadingCompliance(boolean flag)
+   {
+      this.java2ClassLoadingCompliance = flag;
+   }
+
+   public String[] getFilteredPackages()
+   {
+      return filteredPackages;
+   }
+   public void setFilteredPackages(String[] pkgs)
+   {
+      this.filteredPackages = pkgs;
+   }
+
+   public boolean isUseJBossWebLoader()
+   {
+      return useJBossWebLoader;
+   }
+   public void setUseJBossWebLoader(boolean useJBossWebLoader)
+   {
+      this.useJBossWebLoader = useJBossWebLoader;
+   }
+
+   
+   @Override
+   public void deploy(DeploymentUnit unit) throws DeploymentException
+   {
+      WebMetaData metaData = unit.getAttachment(WebMetaData.class);
+      if( metaData != null )
+      {
+         unit.addAttachment(ClassLoaderFactory.class, this);
+      }
+   }
+
+   public ClassLoader createClassLoader(DeploymentContext context) throws Exception
+   {
+      ClassLoader loader = null;
+      try
+      {
+         DeploymentUnit unit = context.getDeploymentUnit();
+         WebMetaData metaData = unit.getAttachment(WebMetaData.class);
+         loader = createClassLoader(metaData, context);
+         if( loader != null )
+            context.setClassLoader(loader);
+      }
+      catch(Exception e)
+      {
+         log.warn("", e);
+      }
+      return loader;
+   }
+
+   public void removeClassLoader(DeploymentContext context) throws Exception
+   {
+      
+   }
+
+   /**
+    * 
+    */
+   protected ClassLoader createClassLoader(WebMetaData metaData, DeploymentContext context)
+         throws Exception
+   {
+      ClassLoader loader = null;
+      if (metaData != null)
+      {
+         ClassLoader parent = context.getClassLoader();
+         if( parent == null )
+            parent = context.getTopLevel().getClassLoader();
+         if( parent == null )
+            parent = Thread.currentThread().getContextClassLoader();
+         Loader webLoader = getWebLoader(parent, metaData, context.getClassPath());
+         context.getDeploymentUnit().addAttachment(Loader.class, webLoader);
+         loader = webLoader.getClassLoader();
+      }
+      return loader;
+   }
+
+   /**
+    * Create the web app class loader.
+    * 
+    * @param loader
+    * @param metaData
+    * @param classpath
+    * @return
+    * @throws MalformedURLException
+    */
+   protected Loader getWebLoader(ClassLoader loader, WebMetaData metaData,
+         List<VirtualFile> classpath)
+         throws Exception
+   {
+      Loader webLoader = null;
+      TomcatInjectionContainer injectionContainer = null;
+      if (useJBossWebLoader)
+      {
+         WebCtxLoader jbossLoader = new WebCtxLoader(loader, injectionContainer);
+         ArrayList<URL> cp = new ArrayList<URL>();
+         for(VirtualFile vf : classpath)
+         {
+            try
+            {
+               URL path = vf.toURL();
+               cp.add(path);
+            }
+            catch(Exception e)
+            {
+               log.debug("Ignoring path element: "+vf, e);
+            }
+         }
+         jbossLoader.setClasspath(cp);
+         webLoader = jbossLoader;
+      }
+      else
+      {
+         WebAppLoader jbossLoader = new WebAppLoader(loader, filteredPackages, injectionContainer);
+         jbossLoader.setDelegate(java2ClassLoadingCompliance);
+         jbossLoader.start();
+         webLoader = jbossLoader;
+      }
+      return webLoader;
+   }
+
+}


Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,821 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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: 58265 $
+ */
+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 we include a source fragment in exception messages, which could be displayed
+    * to the developer ?
+    */
+   private boolean displaySourceFragment = 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";
+
+   /**
+    * 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;
+
+   private boolean ignoreAnnotations;
+
+
+
+   /**
+    * Is generation of X-Powered-By response header enabled/disabled?
+    */
+   private boolean xpoweredBy;
+
+
+   public boolean getIgnoreAnnotations()
+   {
+      return ignoreAnnotations;
+   }
+
+   public void setIgnoreAnnotations(boolean ignoreAnnotations)
+   {
+      this.ignoreAnnotations = ignoreAnnotations;
+   }
+
+   public String getProperty(String name)
+   {
+      return settings.getProperty(name);
+   }
+
+   public void setProperty(String name, String value)
+   {
+      if (name != null && value != null)
+      {
+         settings.setProperty(name, value);
+      }
+   }
+
+   /**
+    * Are we keeping generated code around?
+    */
+   public boolean getKeepGenerated()
+   {
+      return keepGenerated;
+   }
+
+   /**
+    * Should white spaces between directives or actions be trimmed?
+    */
+   public boolean getTrimSpaces()
+   {
+      return trimSpaces;
+   }
+
+   public boolean isPoolingEnabled()
+   {
+      return isPoolingEnabled;
+   }
+
+   /**
+    * Are we supporting HTML mapped servlets?
+    */
+   public boolean getMappedFile()
+   {
+      return mappedFile;
+   }
+
+   /**
+    * Should errors be sent to client or thrown into stderr?
+    */
+   public boolean getSendErrorToClient()
+   {
+      return sendErrorToClient;
+   }
+
+   /**
+    * Should class files be compiled with debug information?
+    */
+   public boolean getClassDebugInfo()
+   {
+      return classDebugInfo;
+   }
+
+   /**
+    * Background JSP compile thread check intervall
+    */
+   public int getCheckInterval()
+   {
+      return checkInterval;
+   }
+
+   /**
+    * Modification test interval.
+    */
+   public int getModificationTestInterval()
+   {
+      return modificationTestInterval;
+   }
+
+   /**
+    * Is Jasper being used in development mode?
+    */
+   public boolean getDevelopment()
+   {
+      return development;
+   }
+
+   public boolean getDisplaySourceFragment()
+   {
+      // TODO Auto-generated method stub
+      return displaySourceFragment;
+   }
+
+   /**
+    * Is the generation of SMAP info for JSR45 debuggin suppressed?
+    */
+   public boolean isSmapSuppressed()
+   {
+      return isSmapSuppressed;
+   }
+
+   /**
+    * Should SMAP info for JSR45 debugging be dumped to a file?
+    */
+   public boolean isSmapDumped()
+   {
+      return isSmapDumped;
+   }
+
+   /**
+    * Are Text strings to be generated as char arrays?
+    */
+   public boolean genStringAsCharArray()
+   {
+      return this.genStringAsCharArray;
+   }
+
+   /**
+    * Class ID for use in the plugin tag when the browser is IE.
+    */
+   public String getIeClassId()
+   {
+      return ieClassId;
+   }
+
+   /**
+    * What is my scratch dir?
+    */
+   public File getScratchDir()
+   {
+      return scratchDir;
+   }
+
+   /**
+    * What classpath should I use while compiling the servlets generated from
+    * JSP files?
+    */
+   public String getClassPath()
+   {
+      return classpath;
+   }
+
+   /**
+    * Is generation of X-Powered-By response header enabled/disabled?
+    */
+   public boolean isXpoweredBy()
+   {
+      return xpoweredBy;
+   }
+
+   /**
+    * Compiler to use.
+    */
+   public String getCompiler()
+   {
+      return compiler;
+   }
+
+   /**
+    * @see Options#getCompilerClassName
+    */
+   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 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 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 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 compilerClassName = config.getInitParameter("compilerClassName");
+      if(compilerClassName != null)
+      {
+         this.compilerClassName = compilerClassName;
+      }
+      
+      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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -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: 45726 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,264 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56732 $
+ */
+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("Please 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,81 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 58583 $
+ */
+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.
+    * @return the invalidation policy constant
+    */ 
+   public InvalidateSessionPolicy 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,31 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,278 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56542 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,156 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 60079 $
+ */
+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)
+         {
+            log_.error("Caught exception rolling back transaction", exn);
+         }
+         
+         // We will need to alert Tomcat of this exception.
+         if (e instanceof IOException)
+            throw (IOException) e;
+         if (e instanceof ServletException)
+            throw (ServletException) e;
+         if (e instanceof RuntimeException)
+            throw (RuntimeException) e;
+         throw new RuntimeException(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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,294 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.Map;
+
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.AbstractCacheListener;
+import org.jboss.cache.CacheListener.ModificationType;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.logging.Logger;
+import org.jgroups.View;
+
+
+public class CacheListener extends AbstractCacheListener
+{
+   // 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 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;
+      ReplicationGranularity granularity = manager_.getReplicationGranularity();
+      fieldBased_ = (granularity == ReplicationGranularity.FIELD);
+      // TODO decide if disdaining local activity is always good for REPL_ASYNC
+      disdainLocalActivity_ = (granularity == ReplicationGranularity.SESSION);; // for now
+   }
+
+   // --------------- TreeCacheListener methods ------------------------------------
+
+   @Override
+   public void nodeRemoved(Fqn fqn, boolean pre, boolean isLocal, Map data)
+   {
+      if (pre || isLocal)
+         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))
+      {
+         // A session has been invalidated from another node;
+         // need to inform manager
+         String sessId = getIdFromFqn(fqn, isBuddy);
+         manager_.processRemoteInvalidation(sessId);
+      }
+   }
+   
+   @Override
+   public void nodeModified(Fqn fqn, boolean pre, boolean isLocal, ModificationType type, Map data)
+   {
+      if (pre || isLocal)
+         return;
+      
+//      // 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.
+      // TODO get the key from the passed in data map!!
+//      Integer version = (Integer)cacheWrapper_.get(fqn, JBossCacheService.VERSION_KEY);
+      Integer version = (Integer)data.get(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)
+         {
+            log_.warn("Possible concurrency problem: Replicated version id " + 
+                      version + " matches in-memory version for session " + realId); 
+         }
+         /*else 
+         {
+            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
+      {
+//         log_.warn("No VERSION_KEY attribute found in " + fqn);
+      }
+   }
+
+   @Override
+   public void cacheStarted(CacheSPI cache)
+   {
+      // TODO will need to synchronize this with local sessions
+   }
+
+   @Override
+   public void cacheStopped(CacheSPI cache)
+   {
+      // TODO will need to synchronize this with local sessions
+   }
+
+   @Override
+   public void viewChange(View new_view)
+   {
+      // We don't care for this event.
+   }
+   
+   @Override
+   public void nodeActivated(Fqn fqn, boolean pre)
+   {
+      // we don't need to handle node activation notification since the 
+      // session manager will notify any servlet API listeners upon 
+      // loading the session from the distrubted store.
+   }
+   
+   // FIXME why would there be a notification of passivation on another node?
+   @Override
+   public void nodePassivated(Fqn fqn, boolean pre)
+   {
+      if (!pre || SessionReplicationContext.isLocallyActive())
+         return;
+      
+      boolean isBuddy = isBuddyFqn(fqn);
+      
+      // We only deal with events for the root node of a session,
+      // so skip all others
+      if(isFqnSessionRootSized(fqn, isBuddy) 
+            && isFqnForOurWebapp(fqn, isBuddy))
+      {
+         // A session has been passivated on another node;
+         // need to inform the manager
+         String realId = getIdFromFqn(fqn, isBuddy);
+         String owner = isBuddy ? getBuddyOwner(fqn) : null;
+         manager_.processSessionPassivation(realId, owner);
+      }
+           
+   }
+   
+   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);     
+   }
+}


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1524 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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;
+
+/**
+ * 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: 58585 $
+ */
+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 InvalidateSessionPolicy 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 == InvalidateSessionPolicy.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 (canAttributeBeReplicated(value) == false)
+      {
+         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);
+               }
+            }
+         }
+         accessCount = null;
+
+         // 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 = InvalidateSessionPolicy.ACCESS;
+      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  = InvalidateSessionPolicy.fromInt(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.ordinal());
+         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;      
+   }
+   
+   /**
+    * 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)
+    */
+   protected void update(ClusteredSession replicated) 
+   {
+      synchronized (this)
+      {
+         //    From StandardSession
+         id                  = replicated.id;
+         creationTime        = replicated.creationTime;
+         lastAccessedTime    = replicated.lastAccessedTime;
+         maxInactiveInterval = replicated.maxInactiveInterval;
+         isNew               = replicated.isNew;
+         isValid             = replicated.isValid;
+         thisAccessedTime    = replicated.thisAccessedTime;
+         
+         // From ClusteredSession
+         invalidationPolicy  = replicated.invalidationPolicy;
+         version             = replicated.version;
+   
+         // Get our id without any jvmRoute appended
+         parseRealId(id);
+         
+         // We no longer know if we have an activationListener
+         hasActivationListener = null;
+         
+         // 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);
+//         }
+      }
+   }
+   
+   
+   // -------------------------------------- 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()
+   {
+      if (!sessionAttributesDirty && log.isTraceEnabled())
+         log.trace("Marking session attributes dirty" + id);
+      
+      sessionAttributesDirty = true;
+   }
+   
+   protected boolean getSessionAttributesDirty()
+   {
+      return sessionAttributesDirty;
+   }
+   
+   protected void sessionMetadataDirty()
+   {
+      if (!sessionMetadataDirty && !isNew && log.isTraceEnabled())
+         log.trace("Marking session metadata dirty " + id);
+      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 SET_AND_GET:
+            result = true;
+            break;
+         case 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,151 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56542 $
+ */
+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
+   private 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,46 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56007 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,363 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.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: 58586 $
+ */
+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 (attribute == null || 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,31 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,65 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56542 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,200 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56542 $
+ */
+public class IntervalSnapshotManager extends SnapshotManager implements Runnable
+{
+   static Logger log = Logger.getLogger(IntervalSnapshotManager.class);
+
+   // the interval in ms
+   private int interval = 1000;
+
+   // the modified sessions
+   private Set sessions = new LinkedHashSet();
+
+   // the distribute thread
+   private Thread thread = null;
+
+   // Is session processing allowed?
+   private boolean processingAllowed = false;
+   
+   // has the thread finished?
+   private 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InvalidateSessionPolicy.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InvalidateSessionPolicy.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InvalidateSessionPolicy.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,51 @@
+/*
+ * 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 enum InvalidateSessionPolicy
+{
+   ACCESS, SET_AND_GET, SET_AND_NON_PRIMITIVE_GET, SET;
+   
+   public static InvalidateSessionPolicy fromString(String policy)
+   {
+      return (policy == null ? InvalidateSessionPolicy.SET_AND_NON_PRIMITIVE_GET 
+                             : Enum.valueOf(InvalidateSessionPolicy.class, policy));   
+   }
+   
+   public static InvalidateSessionPolicy fromInt(int ordinal)
+   {
+      switch (ordinal)
+      {
+         case 2:
+            return SET_AND_NON_PRIMITIVE_GET;
+         case 3:
+            return SET;
+         case 1:
+            return SET_AND_GET;
+         case 0:
+            return ACCESS;
+         default:
+            throw new IllegalArgumentException("Unknown ordinal " + ordinal);
+      }
+   }
+}
\ No newline at end of file


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,962 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.config.Configuration;
+import org.jboss.cache.factories.XmlConfigurationParser;
+import org.jboss.cache.pojo.PojoCacheFactory;
+import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper;
+import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean;
+import org.jboss.cache.transaction.BatchModeTransactionManagerLookup;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.mx.util.MBeanServerLocator;
+
+/**
+ * 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: 61125 $
+ */
+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 PojoCacheJmxWrapperMBean pojoCache = null;
+   
+   /** Name under which our TreeCache is registered in JMX */
+   private String pojoCacheObjectName = "jboss.cache:service=TomcatClusteringCache";
+   
+   /** Did we create the tree cache, or was it already registered in JMX? */
+   private boolean pojoCacheLocal = 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 Tomcat6.DEFAULT_CACHE_NAME the default name used in 
+    * embedded JBoss/Tomcat}.
+    * </p>
+    * 
+    * @jmx.managed-attribute
+    */
+   public String getCacheObjectName()
+   {
+      return pojoCacheObjectName;
+   }
+
+   /**
+    * 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.pojoCacheObjectName = 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(pojoCacheObjectName);
+      
+      // 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
+         PojoCacheJmxWrapperMBean cache = getTreeCache();
+
+         registerMBeans();
+         
+         if (pojoCacheLocal)
+         {
+            cache.create();
+            cache.start();
+         }
+
+         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 (pojoCacheLocal)
+      {
+         pojoCache.stop();
+         pojoCache.destroy();
+      }
+
+      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 PojoCacheJmxWrapperMBean getTreeCache() throws Exception
+   {
+      if (pojoCache == null) {
+         
+         MBeanServer server = getMBeanServer();
+         ObjectName objName = new ObjectName(pojoCacheObjectName);
+         if (server.isRegistered(objName))
+         {
+            // Get a proxy to the existing TreeCache
+            pojoCache = (PojoCacheJmxWrapperMBean) 
+                           MBeanProxyExt.create(PojoCacheJmxWrapperMBean.class, objName);
+         }
+         else
+         {
+            // See if there is an XML descriptor file to configure the cache
+            InputStream configIS = getCacheConfigStream();
+            
+            Configuration config = null;
+            
+            if (configIS != null)
+            {
+               XmlConfigurationParser parser = new XmlConfigurationParser();
+               config = parser.parseStream(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
+                  config.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.
+               config = new Configuration();
+               String channelName = (clusterName == null) ? DEFAULT_CLUSTER_NAME
+                                                          : clusterName;
+               config.setClusterName(channelName);
+               config.setIsolationLevel(DEFAULT_ISOLATION_LEVEL);
+               config.setCacheMode(DEFAULT_CACHE_MODE);
+               config.setLockAcquisitionTimeout(DEFAULT_LOCK_TIMEOUT);
+               config.setTransactionManagerLookupClass(DEFAULT_TM_LOOKUP);
+            }
+
+            pojoCache = new PojoCacheJmxWrapper(PojoCacheFactory.createCache(config, false));
+            
+            pojoCacheLocal = true;
+         }
+      }
+      return pojoCache;
+   }   
+   
+   
+
+   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 (pojoCacheLocal)
+         {
+            // Register the treeCache
+            ObjectName treeCacheName = new ObjectName(pojoCacheObjectName);
+            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 (pojoCacheLocal)
+            {
+               mserver.unregisterMBean(new ObjectName(pojoCacheObjectName));
+            }
+         }
+         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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,80 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.service.JBossCacheCluster
+ * 
+ * @author Brian Stansberry
+ * @version $Revision: 45726 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,160 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56542 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1923 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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 EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implementation of a clustered session manager for
+ * catalina using JBossCache replication.
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ * @author Hany Mesha
+ * @version $Revision: 58587 $
+ */
+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.
+    */
+   private 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_ = "jboss.cache:service=TomcatClusteringCache";
+
+   /**
+    * If set to true, will add a JvmRouteFilter to the request.
+    */
+   private 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 ReplicationGranularity 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)
+      {
+         this.replGranularityString_ = replicationGranularity_.toString();         
+      }
+      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)
+      {
+         this.replTriggerString_ = invalidateSessionPolicy_.toString();         
+      }
+      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];
+         // JBCLUSTER-15
+         // if session passivation is enabled, passivate sessions instead of expiring them which means
+         // they'll be available to the manager for activation after a restart. 
+         if (log_.isDebugEnabled())
+         {
+             log_.debug("clearSessions(): clear session by expiring or passivating: " + ses);
+         }
+         boolean notify = true;
+         boolean localCall = true;
+         boolean localOnly = true;
+         try
+         {
+            if(isPassivationEnabled() && ses.isValid())
+            {
+               
+               processSessionPassivation(ses.getRealId(), this.getContainer().getParent().getName());
+            }
+            else
+            {
+               ses.expire(notify, localCall, localOnly);               
+            }
+
+         }
+         catch (Throwable t)
+         {
+            log_.warn("clearSessions(): Caught exception expiring or passivating 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();
+         }
+      }
+
+      if(!isPassivationEnabled())
+      {
+//       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)
+   {
+      if (log_.isTraceEnabled())
+      {
+         log_.trace("createSession: active sessions = " + activeCounter_ +
+                    " , session map size = " + sessions_.size() +
+                    " and max allowed sessions = " + maxActive_);
+      }
+      // 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.
+
+      // JBCLUSTER-15
+      // first check if passivation is enabled and reached the max allowed sessions, 
+      /// then try to expire/passivate sessions to free memory 
+      if(maxActive_ != -1 && sessions_.size() >= maxActive_ && isPassivationEnabled())
+      {
+         processExpires();
+      }
+      // maxActive_ -1 is unlimited
+      if (maxActive_ != -1 && sessions_.size() >= 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.createSession(): number of " +
+                "active sessions exceeds the maximum limit: " +
+                maxActive_ + " when trying to create 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_++;
+      // JBCLUSTER-15 - if we have created a session it must be handled by this manager
+      // therefore we must increment the active counter
+      activeCounter_++;
+      
+      // 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);
+            }
+            else if (log_.isTraceEnabled())
+            {
+               log_.trace("Session " + session.getIdInternal() + 
+                          " did not require replication.");
+            }
+         }
+      }
+
+      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 ATTRIBUTE:
+         session = new AttributeBasedClusteredSession(this);
+            break;
+         case 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
+    */
+   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)
+      {
+         // JBCLUSTER-15
+         // We need to check for maxActive first before attempting to create a new session
+         if (log_.isTraceEnabled())
+         {
+            log_.trace("createSession: active sessions = " + activeCounter_ +
+                       " , session map size = " + sessions_.size() +
+                       " and max allowed sessions = " + maxActive_);
+         }
+         // first check if passivation is enabled and reached the max allowed sessions, 
+         /// then try to expire/passivate sessions to free memory 
+         if(maxActive_ != -1 && sessions_.size() >= maxActive_ && isPassivationEnabled())
+         {
+            processExpires();
+         }
+         // maxActive_ -1 is unlimited
+         if (maxActive_ != -1 && sessions_.size() >= 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 = (realId == null) ? "" : " id " + realId;
+            throw new IllegalStateException("JBossCacheManager.createSession(): number of " +
+                   "active sessions exceeds the maximum limit: " +
+                   maxActive_ + " when trying to load session" + msgEnd);
+         }
+         
+         // 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)
+            {
+               log_.error("Caught exception rolling back transaction", exn);
+            }
+            // 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_ != ReplicationGranularity.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)
+      {
+         log_.debug("processSessionRepl(): failed with exception", ex);
+         
+         try
+         {
+            //if(doTx)
+            // Let's setRollbackOnly no matter what.
+            // (except if there's no tx due to SESSION (JBAS-3840))
+            if (notSession)
+               tm.setRollbackOnly();
+         }
+         catch (Exception exn)
+         {
+            log_.error("Caught exception rolling back transaction", exn);
+         }
+         
+         // 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_.isTraceEnabled())
+      {
+         log_.trace("processExpires():max active sessions = " + maxActive_);
+         log_.trace("processExpires(): passivation mode = " + isPassivationEnabled()); 
+         log_.trace("processExpires(): 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;
+               
+               // JBCLUSTER-15
+               if (log_.isTraceEnabled())
+               {
+                  log_.trace("processExpires(): Checking passivation for session " + session.getId());
+               } 
+               // now that we have valid session, see if we need to 
+               // passivate it based on the configurable passivation min and max Idle time
+               if (isPassivationEnabled())
+               {
+                  long timeNow = System.currentTimeMillis();
+                  int timeIdle = (int) ((timeNow - session.getLastAccessedTimeInternal()) / 1000L);
+                  // if maxIdle time configured, means that we need to passivate sessions that have
+                  // exceeded the max allowed idle time
+                  if (passivationMaxIdleTime_ >= 0 && timeIdle > passivationMaxIdleTime_)
+                  {
+                     if(log_.isTraceEnabled())
+                     {
+                        log_.trace("JBossCacheManager.processExpires() passivating session " + session.getRealId());
+                     }
+                     processSessionPassivation(session.getRealId(), this.getContainer().getParent().getName());
+                  }
+                  // If the session didn't exceed the passivationMaxIdleTime_, See   
+                  // if the number of sessions managed by this manager greater than the max allowed 
+                  // active sessions, passivate the session if it exceed passivationMinIdleTime_ 
+                  else if (maxActive_ > 0 && passivationMinIdleTime_ > 0 && sessions_.size()> maxActive_)
+                  {
+                     if(timeIdle > passivationMinIdleTime_)
+                     {
+                        if(log_.isTraceEnabled())
+                        {
+                           log_.debug("JBossCacheManager.processExpires() passivating session " + session.getRealId());
+                        }
+                        processSessionPassivation(session.getRealId(), this.getContainer().getParent().getName());
+                     }
+                  }
+               }
+               
+            }
+            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);
+               }
+                              
+               // JBClUSTER-15
+               // we don't need to worry about session passivation here, since the 
+               // method processSessionPassivation() takes care of unloadedSessions_ map
+               // when it receives a notification of passivation event happened in the 
+               // distributed store from the CacheListener
+               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_--;
+      }
+   }
+   
+   public void processSessionPassivation(String realId, String dataOwner)
+   {
+      // get the session from the local map
+      ClusteredSession session = findLocalSession(realId);
+      // only remove actively managed session and add to the unloaded sessions
+      // if it's already unloaded session (session == null) don't do anything, 
+      // the evict notification will tell the server that has the session to remove it. 
+      if (session != null)
+      {
+         synchronized (session)
+         {
+            if (log_.isTraceEnabled())
+            {
+               log_.trace("Passivating session with id: " + realId);
+            }
+
+            try {
+               // Tell the proxy to ignore cache notifications we are about
+               // to generate for this session.
+               SessionReplicationContext.startCacheActivity();
+               session.passivate();
+               proxy_.evictSession(realId);
+            }
+            finally {
+               SessionReplicationContext.finishCacheActivity();
+            }
+
+            Object obj = unloadedSessions_.put(realId, 
+                  new OwnedSessionUpdate(dataOwner, session.getLastAccessedTime()));
+            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);
+               }
+            }
+            sessions_.remove(realId);
+            stats_.removeStats(realId);
+         }
+         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);
+         }
+      }
+   }
+   
+   /**
+    * Returns true if the passivation mode is set to true in JBoss-web.xml and JBoss Cache passivation
+    * has been enabled with proper configured cache loader. Otherwise, it returns false
+    * 
+    * @return
+    */
+   protected boolean isPassivationEnabled()
+   {
+      return (passivationMode_ && proxy_.isCachePassivationEnabled());
+   }
+   // ----------------------------------------------------  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;
+      }
+      
+      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
+      
+      try
+      {
+         this.invalidateSessionPolicy_ = InvalidateSessionPolicy.fromString(replTriggerString_);
+      }
+      catch (IllegalArgumentException iae)
+      {
+         throw new LifecycleException("replication-trigger value set to a " +
+                                       "non-valid value: '" + 
+                                       replTriggerString_ + 
+                                       "' (should be ['SET_AND_GET', " +
+                                       "'SET_AND_NON_PRIMITIVE_GET', 'SET'])");
+      }
+      
+      try
+      {
+      }
+      catch (IllegalArgumentException iae)
+      {         
+         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);
+         
+         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_ == ReplicationGranularity.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_== ReplicationGranularity.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_ == ReplicationGranularity.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 " +
+               "jboss-web-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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,128 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1146 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+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.Cache;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
+import org.jboss.cache.Region;
+import org.jboss.cache.pojo.PojoCache;
+import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.config.CacheLoaderConfig;
+import org.jboss.cache.transaction.BatchModeTransactionManager;
+import org.jboss.invocation.MarshalledValue;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.serial.io.MarshalledObject;
+
+/**
+ * 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 PojoCache pojoCache_;
+   private Cache plainCache_;
+   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);
+         // Get the pojo cache
+         PojoCacheJmxWrapperMBean mbean = (PojoCacheJmxWrapperMBean) MBeanProxyExt.create(PojoCacheJmxWrapperMBean.class,
+                                                                                          cacheServiceName_);
+         if (mbean != null)
+         {
+            pojoCache_ = mbean.getPojoCache();
+         }
+      }
+      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 (pojoCache_ == null)
+      {
+         String str = "Could not access TreeCache service " + 
+                     (cacheServiceName_ == null ? "<null>" : cacheServiceName_.toString()) + 
+                     " for Tomcat clustering";
+         log_.debug(str);
+         throw new ClusteringNotSupportedException(str);
+      }
+      
+      plainCache_ = pojoCache_.getCache();
+      cacheWrapper_ = new JBossCacheWrapper(pojoCache_);
+      
+      useTreeCacheMarshalling_ = plainCache_.getConfiguration().isUseRegionBasedMarshalling();
+   }
+
+   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;
+      }
+      // JBAS-3941 -- context path can be multi-level, but we don't
+      // want that turning into a multilevel Fqn, so escape it
+      // Use '?' which is illegal in a context path
+      webAppPath_ = webAppPath_.replace('/', '?');
+      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_);
+      plainCache_.addCacheListener(cacheListener_);
+
+      // register the tcl and bring over the state for the webapp
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+      Fqn pathFqn = new Fqn( objs );
+      try {
+         if(useTreeCacheMarshalling_)
+         {
+            log_.debug("UseMarshalling is true. We will register the fqn: " +
+                        pathFqn + " with class loader" +tcl +
+                        " and activate the webapp's Region");
+            Region region = plainCache_.getRegion(pathFqn, true);
+            region.registerContextClassLoader(tcl);
+            region.activate();
+         }
+      } catch (Exception ex)
+      {
+         throw new RuntimeException("Can't register class loader", ex);
+      }
+
+      // We require the cache tm to be BatchModeTransactionManager now.
+      tm = plainCache_.getConfiguration().getRuntimeConfig().getTransactionManager();
+      if( ! (tm instanceof BatchModeTransactionManager) )
+      {
+         throw new RuntimeException("JBossCacheService.start(): JBossCacheAop transaction manager is not of type BatchModeTransactionManager." +
+                 " Please check the jboss-web-cluster-service.xml TransactionManagerClassLookup field.");
+      }
+      
+      if(isCachePassivationEnabled())
+      {
+         log_.debug("JBossCache passivation is enabled");
+      }
+      else
+      {
+         log_.debug("JBossCache passivation is disabled");
+      }
+   }
+
+   public void stop()
+   {
+      plainCache_.removeCacheListener(cacheListener_);
+
+      // Construct the fqn
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+      Fqn pathFqn = new Fqn( objs );
+
+      if(useTreeCacheMarshalling_)
+      {
+         log_.debug("UseMarshalling is true. We will inactivate the fqn: " +
+                    pathFqn + " and un-register its classloader");
+            
+         try {
+            Region region = plainCache_.getRegion(pathFqn, false);
+            if (region != null)
+            {
+               region.deactivate();
+               region.unregisterContextClassLoader();
+            }
+         } 
+         catch (Exception e) 
+         {
+            log_.error("Exception during inactivation of webapp region " + pathFqn + 
+                       " or un-registration of its class loader", e);
+         }
+      }
+
+      // remove session data
+      cacheWrapper_.removeLocalSubtree(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
+      {
+         // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
+         // 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);
+         ObjectInputStream input = SessionSerializationFactory.createObjectInputStream(sessionBytes);
+         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_.removeLocalSubtree(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_.removeLocalSubtree(fqn);
+      }
+   }
+   
+      
+   public void evictSession(String realId)
+   {
+      Fqn fqn = getSessionFqn(realId);
+      if(log_.isDebugEnabled())
+      {
+         log_.debug("evictSession(): evicting session from my distributed store. Fqn: " + fqn);
+      }
+      cacheWrapper_.evictSubtree(fqn);
+      
+   }
+
+   public boolean exists(String realId)
+   {
+      Fqn fqn = getSessionFqn(realId);
+      return plainCache_.getRoot().hasChild(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_.removeLocal(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
+      {
+         Node node = plainCache_.getRoot().getChild(fqn);
+         if (node != null)
+            keys = node.getKeys();
+      }
+      catch (CacheException e)
+      {
+         log_.error("getAttributeKeys(): Exception getting keys for session " + realId, e);
+      }
+      
+      return keys;
+   }
+
+   /**
+    * Return all attributes associated with this session id.
+    *
+    * TODO use Node.getData() and just copy the map
+    * 
+    * @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();
+      
+      Node bbRoot = plainCache_.getRoot().getChild(BUDDY_BACKUP_FQN);
+      if (bbRoot != null)
+      {
+         Set owners = bbRoot.getChildren();
+         if (owners != null)
+         {
+            for (Iterator it = owners.iterator(); it.hasNext();)
+            {
+               Node owner = (Node) it.next();
+               Node webRoot = owner.getChild(getWebappFqn());
+               if (webRoot != null)
+               {
+                  Set ids = webRoot.getChildrenNames();
+                  storeSessionOwners(ids, owner, result);
+               }
+            }
+         }
+      }
+      
+      storeSessionOwners(getChildrenNames(getWebappFqn()), null, result);
+
+      return result;
+   }
+   
+   private Set getChildrenNames(Fqn fqn)
+   {
+      Node node = plainCache_.getRoot().getChild(fqn);
+      return (node == null ? null : node.getChildrenNames());
+   }
+
+   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 pojoCache_.attach(fqn.toString(), 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 pojoCache_.detach(fqn.toString());
+      } 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_.removeLocalSubtree(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_.removeLocalSubtree(fqn);
+      }
+      finally {
+         SessionReplicationContext.finishCacheActivity();
+      }
+   }
+   
+   public Set getPojoKeys(String realId)
+   {
+      Set keys = null;
+      Fqn fqn = getAttributeFqn(realId);
+      try
+      {
+         keys = 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 pojoCache_.find(fqn.toString());
+      }
+      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);
+         }
+      } 
+      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);
+         }
+      }
+
+      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);
+            }
+         }
+      }
+   }
+ 
+   public boolean isCachePassivationEnabled()
+   {
+      CacheLoaderConfig clc = plainCache_.getConfiguration().getCacheLoaderConfig();
+      if(clc != null)
+      {
+         return (clc.isPassivation() && !clc.isShared());
+      }
+      else
+      {
+         return false;
+      }
+   }
+   
+   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
+         {
+            // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
+            // to allow for switching between JBossSerialization and JavaSerialization using
+            // system property -D=session.serialization.jboss=true / false
+            // MarshalledValue mv = new MarshalledValue(value);
+            if  (SessionSerializationFactory.useJBossSerialization())
+            {
+               MarshalledObject mo = SessionSerializationFactory.createMarshalledObject(value);
+               if (log_.isTraceEnabled())
+               {
+                  log_.trace("JBoss Marshalled Object to size ");
+               }
+               return mo;
+               
+            }
+            else 
+            {
+               MarshalledValue mv = SessionSerializationFactory.createMarshalledValue(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
+         {
+            // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
+            // to allow for switching between JBossSerialization and JavaSerialization using
+            // system property -D=session.serialization.jboss=true / false
+
+            if (SessionSerializationFactory.useJBossSerialization())
+            {
+               return ((MarshalledObject)mv).get();
+            }
+            else 
+            {
+               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
+         
+         // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
+         // to allow for switching between JBossSerialization and JavaSerialization using
+         // system property -D=session.serialization.jboss=true / false
+         
+         // MarshalledValueOutputStream oos = new MarshalledValueOutputStream(baos);
+         
+         ObjectOutputStream oos = SessionSerializationFactory.createObjectOutputStream(baos);
+         session.writeExternal(oos);
+         oos.close(); // flushes bytes to baos
+         
+         byte[] bytes = baos.toByteArray();
+         
+         if (log_.isTraceEnabled())
+         {
+            log_.trace("Serialized 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,381 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.Node;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.lock.TimeoutException;
+import org.jboss.cache.pojo.PojoCache;
+
+public class JBossCacheWrapper 
+{
+   static final Option GRAVITATE_OPTION = new Option();
+   static final Option LOCAL_OPTION = new Option();
+   static
+   {
+      GRAVITATE_OPTION.setForceDataGravitation(true);
+      LOCAL_OPTION.setCacheModeLocal(true);
+   }
+   
+   private static final int RETRY = 3;
+   private PojoCache pojoCache_;
+   private Cache plainCache_;
+   
+   JBossCacheWrapper(PojoCache cache)
+   {
+      pojoCache_ = cache;
+      plainCache_ = pojoCache_.getCache();
+   }
+
+   /**
+    * 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++)
+      {
+         InvocationContext ctx = null;
+         Option existing = null;
+         try
+         {
+            
+            Object value = null;
+            if (gravitate)
+            {            
+               ctx = plainCache_.getInvocationContext();
+               existing = ctx.getOptionOverrides();
+               ctx.setOptionOverrides(GRAVITATE_OPTION);
+               value = plainCache_.get(fqn, id);
+            }
+            else
+            {
+               value = plainCache_.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);
+         }
+         finally
+         {
+            if (ctx != null)
+               ctx.setOptionOverrides(existing);
+         }
+      }
+      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
+         {
+            plainCache_.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
+         {
+            plainCache_.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 plainCache_.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
+         {
+            plainCache_.removeNode(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 removeLocal(Fqn fqn)
+   {
+      Exception ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         InvocationContext ctx = plainCache_.getInvocationContext();
+         Option existing = ctx.getOptionOverrides();
+         try
+         {
+            ctx.setOptionOverrides(LOCAL_OPTION);
+            plainCache_.removeNode(fqn);
+            return;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException("JBossCacheService: exception occurred in cache removeLocal ... ", e);
+         }
+         finally
+         {
+            ctx.setOptionOverrides(existing);
+         }
+      }
+      throw new RuntimeException("JBossCacheService: exception occurred in cache removeLocal 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
+         {
+            plainCache_.evict(fqn, false);
+            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
+//            plainCache_.evict(fqn);
+//            
+//            // next do a depth first removal; this ensure all nodes
+//            // are removed, not just their data map
+//            Set children = plainCache_.getChildrenNames(fqn);
+//            if (children != null)
+//            {
+//               for (Iterator it = children.iterator(); it.hasNext(); )
+//               {
+//                  Fqn child = new Fqn(fqn, it.next());
+//                  plainCache_.evict(child);
+//               }
+//               
+//               plainCache_.evict(fqn);
+//            }
+//            return;
+            plainCache_.evict(fqn, true);
+            
+         }
+         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);  
+   }
+   
+   void removeLocalSubtree(Fqn fqn)
+   {
+      // First remove the node itself.  If it is the root of an AOP
+      // object tree, this will do everything
+      removeLocal(fqn);
+
+      // Next, clear any children      
+      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
+            removeLocal(fqn);
+            
+            // next do a depth first removal; this ensures all nodes
+            // are removed, not just their data map
+            Node base = plainCache_.getRoot().getChild(fqn);
+            if (base != null)
+            {
+               Set children = base.getChildren();
+               if (children != null)
+               {
+                  for (Iterator it = children.iterator(); it.hasNext(); )
+                  {
+                     Node child = (Node) it.next();
+                     removeLocalSubtree(child.getFqn());
+                  }
+                  
+                  removeLocal(fqn);
+               }
+            }
+            return;
+            
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException("JBossCacheService: exception occurred in cache removeLocal ... ", e);
+         }
+      }
+      throw new RuntimeException("JBossCacheService: exception occurred in cache removeLocalSubtree after retry ... ", ex);
+   } 
+
+}


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,950 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.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;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Session;
+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.metadata.web.ReplicationConfig;
+import org.jboss.metadata.web.PassivationConfig;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.web.tomcat.statistics.ReplicationStatistics;
+
+
+/**
+ * Base abstract implementation of Tomcat manager without the concept of
+ * session operations, e.g., add, remove, etc.
+ *
+ * @author Ben Wang
+ * @author Hany Mesha
+ * @version $Revision: 60686 $
+ */
+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 InvalidateSessionPolicy invalidateSessionPolicy_ = InvalidateSessionPolicy.SET_AND_NON_PRIMITIVE_GET;
+   /**
+    * Replication granulairty.
+    */
+   protected ReplicationGranularity replicationGranularity_ = ReplicationGranularity.SESSION;
+   
+   /**
+    * Session passivation flag set in jboss-web.xml by the user.
+    * If true, then the session passivation is enabled for this web application, 
+    * otherwise, it's disabled
+    */
+   protected boolean passivationMode_ = false;
+   
+   /**
+    * Min time (seconds) the session must be idle since lastAccesstime before it's eligable for passivation
+    * This overrides maxActive_, to prevent thrashing if the there are lots of active sessions.
+    * Setting to -1 means it's ignored
+    */
+   protected int passivationMinIdleTime_ = -1;
+   
+   /**
+    * Max time (seconds) the session must be idle since lastAccesstime before it's eligable for passivation
+    * This overrides maxActive_, to prevent thrashing if the there are lots of active sessions.
+    * Setting to -1 means session should not be forced out.
+    */
+   protected int passivationMaxIdleTime_ = -1;
+   
+   /**
+    * 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
+   {
+      ReplicationConfig rpc = webMetaData.getReplicationConfig();
+      if (rpc != null)
+      {
+         replicationGranularity_ = ReplicationGranularity.fromString(rpc.getGranularity());
+         invalidateSessionPolicy_ = InvalidateSessionPolicy.fromString(rpc.getTrigger());
+      }
+      maxActive_ = webMetaData.getMaxActiveSessionsAllowed();
+      PassivationConfig pConfig = webMetaData.getPassivationConfig();
+      if (pConfig != null)
+      {
+         passivationMode_ = Boolean.valueOf(pConfig.getUseSessionPassivation()).booleanValue();
+         if (passivationMode_)
+         {
+            passivationMinIdleTime_ = Integer.valueOf(pConfig.getPassivationMinIdleTime()).intValue();
+            passivationMaxIdleTime_ = Integer.valueOf(pConfig.getPassivationMaxIdleTime()).intValue();
+         }
+      }
+      
+      useLocalCache_ = useLocalCache;
+      log_.debug("init(): replicationGranularity_ is " + replicationGranularity_ +
+         " and invalidateSessionPolicy is " + invalidateSessionPolicy_ +
+         " and maxActiveSessions allowed is " + maxActive_ +
+         " and passivationMode is " + passivationMode_);
+
+      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 InvalidateSessionPolicy 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,159 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 45726 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,237 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 58922 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties	2007-03-16 02:23:53 UTC (rev 61355)
@@ -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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties	2007-03-16 02:23:53 UTC (rev 61355)
@@ -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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ReplicationGranularity.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ReplicationGranularity.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ReplicationGranularity.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,33 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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 enum ReplicationGranularity 
+{
+   SESSION, ATTRIBUTE, FIELD;  
+
+   public static ReplicationGranularity fromString(String granularity)
+   {
+      return (granularity == null ? SESSION 
+                                  : Enum.valueOf(ReplicationGranularity.class, granularity));
+   }
+}


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,204 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56542 $
+ */
+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.service.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.service.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);
+      }   
+   
+   }
+
+   protected void update(ClusteredSession replicated)
+   {
+      SessionBasedClusteredSession other = (SessionBasedClusteredSession) replicated;
+      
+      super.update(other);
+      
+      attributes = other.attributes;
+   }
+   
+
+   /**
+    * 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,239 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,301 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,101 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.invocation.MarshalledValueInputStream;
+import org.jboss.invocation.MarshalledValueOutputStream;
+import org.jboss.invocation.MarshalledValue;
+import org.jboss.logging.Logger;
+import org.jboss.serial.io.JBossObjectInputStreamSharedTree;
+import org.jboss.serial.io.JBossObjectOutputStreamSharedTree;
+import org.jboss.serial.io.MarshalledObject;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+
+/**
+ * Factory class for creating object output and input streams, 
+ * switching between JDK Serialization and JBoss Serialization classes. 
+ * Using MarshalledValue to replace Serializable used inside different 
+ * web app class loader context. Assuming that the caller classes are already
+ * handling the switch between web app class loader context. 
+ *  
+ * 
+ * @author <a href="hmesha at novell.com">Hany Mesha</a>
+ */
+public class SessionSerializationFactory
+{
+   static Logger log_ = Logger.getLogger(SessionSerializationFactory.class);
+   static boolean useJBossSerialization = false;
+
+   static
+   {
+       String useJBossSerializationStr = System.getProperty("session.serialization.jboss", "false");
+       useJBossSerialization = Boolean.valueOf(useJBossSerializationStr).booleanValue();
+   }
+
+   public static ObjectOutputStream createObjectOutputStream(OutputStream out) throws IOException
+   {
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("createObjectOutputStream using JBossSerialization = " + useJBossSerialization);
+      }
+       return useJBossSerialization ? new JBossObjectOutputStreamSharedTree(out) : new MarshalledValueOutputStream(out);
+   }
+
+   public static ObjectInputStream createObjectInputStream(byte[] bytes) throws IOException
+   {
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("createObjectInputStream using JBossSerialization = " + useJBossSerialization);
+      }
+       ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+       return useJBossSerialization ? new JBossObjectInputStreamSharedTree(in) : new MarshalledValueInputStream(in);
+   }
+   
+   public static MarshalledValue createMarshalledValue(Object o) throws IOException
+   {
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("createMarshalledValue using JBossSerialization = " + useJBossSerialization);
+      }
+      return new MarshalledValue (o);
+   }
+   
+   public static MarshalledObject createMarshalledObject(Object o) throws IOException
+   {
+      if (log_.isDebugEnabled())
+      {
+         log_.debug("createMarshalledObject using JBossSerialization = " + useJBossSerialization);
+      }
+      return new MarshalledObject(o);
+   }
+
+   public static boolean useJBossSerialization()
+   {
+       return useJBossSerialization;
+   }
+
+}


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,85 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56542 $
+ */
+public abstract class SnapshotManager
+{
+   // The manager the snapshot manager should use
+   private AbstractJBossManager manager;
+
+   // The context-path
+   private 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,148 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 56542 $
+ */
+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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1038 @@
+/*
+ * 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 org.jboss.web.tomcat.service.session.JBossManager;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashSet;
+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.Session;
+import org.apache.catalina.Realm;
+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;
+
+
+/**
+ * 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: 58435 $ $Date: 2006-11-16 05:18:12 +0100 (jeu., 16 nov. 2006) $
+ */
+public class ClusteredSingleSignOn
+   extends org.apache.catalina.authenticator.SingleSignOn
+   implements LifecycleListener
+{
+
+   // 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 = "jboss.cache:service=TomcatClusteringCache";
+   
+   private Set activeManagers = Collections.synchronizedSet(new HashSet());
+
+   // ------------------------------------------------------------- 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);
+      }
+   }
+
+
+   // ------------------------------------------------------ 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;
+
+      // 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);
+      }
+      else
+      {
+         // The session was logged out.
+         logout(ssoId);
+      }
+
+   }
+
+   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
+      if (getContainer().getLogger().isDebugEnabled())
+          getContainer().getLogger().debug(" Checking for cached principal for " + cookie.getValue());
+      SingleSignOnEntry entry = getSingleSignOnEntry(cookie.getValue());
+      if (entry != null)
+      {
+         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 + "'");
+
+      // 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().isTraceEnabled())
+             getContainer().getLogger().trace(" Invalidating session " + sessions[i]);
+         // Remove from reverse cache first to avoid recursion
+         synchronized (reverse)
+         {
+            reverse.remove(sessions[i]);
+         }
+         // Invalidate this session
+         sessions[i].expire();
+      }
+
+      // NOTE:  Clients may still possess the old single sign on cookie,
+      // but it will be removed on the next request since it is no longer
+      // in the cache
+   }
+
+
+   /**
+    * 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);
+         }
+      }
+
+   }
+
+   
+   // -------------------------------------------------------  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());
+            }
+            ssoClusterManager = mgr;
+            clusterManagerClass = className;
+         }
+         catch (Throwable t)
+         {
+            throw new LifecycleException("Cannot create " +
+               "SSOClusterManager using " +
+               className, t);
+         }
+
+         if (started)
+         {
+            ssoClusterManager.start();
+         }
+      }
+   }
+
+}


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,121 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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: 45726 $ $Date: 2006-06-21 21:50:00 +0200 (mer., 21 juin 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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
___________________________________________________________________
Name: svn:eol-style
   + native

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -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: 45726 $ $Date: 2006-06-21 21:50:00 +0200 (mer., 21 juin 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)));
+   }
+}


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1278 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.Set;
+
+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.AbstractCacheListener;
+import org.jboss.cache.Cache;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.Region;
+import org.jboss.cache.RegionNotEmptyException;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.jmx.CacheJmxWrapperMBean;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.util.NestedRuntimeException;
+
+/**
+ * An implementation of SSOClusterManager that uses a TreeCache
+ * to share SSO information between cluster nodes.
+ *
+ * @author Brian E. Stansberry
+ * @version $Revision: 59567 $ $Date: 2007-01-12 03:39:24 +0100 (ven., 12 janv. 2007) $
+ */
+public final class TreeCacheSSOClusterManager
+   extends AbstractCacheListener
+   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 =
+      "jboss.cache:service=TomcatClusteringCache";
+
+   private static final Option GRAVITATE_OPTION = new Option();
+
+   static
+   {
+      GRAVITATE_OPTION.setForceDataGravitation(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;
+
+   /**
+    * String version of the object name to use to access the TreeCache
+    */
+   private String cacheName = null;
+   
+   /**
+    * The cache itself.
+    */
+   private Cache cache = 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;
+   
+   // ----------------------------------------------------------  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;
+      }
+
+      removeAsCacheListener();
+      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");
+         }
+      }
+   }
+   
+   // -----------------------------------------------------  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);
+         sessions.add(new SessionAddress(session.getId(), localAddress));
+         putInTreeCache(fqn, sessions);
+      }
+      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);
+         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;
+      }
+
+      if (log.isTraceEnabled())
+      {
+         log.trace("removeSession(): removing Session " + session.getId() +
+            " from cached session set for SSO " + ssoId);
+      }
+
+      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);
+         if (sessions != null)
+         {
+            sessions.remove(new SessionAddress(session.getId(), localAddress));
+            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);
+            }
+         }
+      }
+      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);
+   }
+
+   
+   // ------------------------------------------------------  CacheListener
+
+   /**
+    * 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)
+   {
+      // We are only interested in changes to the CREDENTIALS node
+      if (CREDENTIALS.equals(getTypeFromFqn(fqn)) == false)
+      {
+         return;
+      }
+
+      String ssoId = getIdFromFqn(fqn); // won't be null or above check fails
+
+      // 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);
+         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);
+      }
+   }
+
+   
+   // -------------------------------------------------------------  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);
+      }
+      
+      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) throws Exception
+   {
+      InvocationContext ctx = cache.getInvocationContext();
+      Option existing = ctx.getOptionOverrides();
+      try
+      {
+         ctx.setOptionOverrides(GRAVITATE_OPTION);
+         return cache.get(fqn, KEY);
+      }
+      finally
+      {
+         ctx.setOptionOverrides(existing);
+      }
+   }
+
+   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)
+      throws Exception
+   {
+      Set sessions = (Set) getFromTreeCache(fqn);
+      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 = cache.getConfiguration().getRuntimeConfig().getTransactionManager();
+
+      if (tm == null) 
+      {
+         throw new IllegalStateException("Cache does not have a " +
+                                         "transaction manager; please " +
+                                         "configure a valid " +
+                                         "TransactionManagerLookupClass");
+      }
+      
+      // Find out our address
+      Object address = cache.getLocalAddress();
+      // 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 Tomcat6 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) throws Exception
+   {
+      InvocationContext ctx = cache.getInvocationContext();
+      Option existing = ctx.getOptionOverrides();
+      try
+      {
+         ctx.setOptionOverrides(GRAVITATE_OPTION);
+         cache.put(fqn, KEY, data);
+      }
+      finally
+      {
+         ctx.setOptionOverrides(existing);
+      }
+   }
+
+   private void integrateWithCache() throws Exception
+   {
+      if (cache == null)
+      {
+         // Get the cache
+         CacheJmxWrapperMBean mbean = (CacheJmxWrapperMBean) MBeanProxyExt.create(CacheJmxWrapperMBean.class, 
+                                                           getCacheObjectName());
+         cache = mbean.getCache();
+         
+         // Ensure we have a transaction manager and a cluster-wide unique address
+         configureFromCache();
+         
+         // If the SSO region is inactive, activate it
+         activateCacheRegion();
+         
+         registerAsCacheListener();
+         
+         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
+   {
+      if (cache.getConfiguration().isInactiveOnStartup())
+      {
+         if (cache.getConfiguration().isUseRegionBasedMarshalling())
+         {
+            Region region =cache.getRegion(Fqn.fromString("/" + SSO), true);
+            try
+            {
+               region.activate();
+            }
+            catch (RegionNotEmptyException e)
+            {
+               log.debug(SSO + " region already active", e);
+            }
+         }
+      }
+   }
+
+   /**
+    * Invokes an operation on the JMX server to register ourself as a
+    * listener on the TreeCache service.
+    *
+    * @throws Exception
+    */
+   private void registerAsCacheListener() throws Exception
+   {
+      cache.addCacheListener(this);
+      registeredAsListener = true;
+   }
+
+
+   /**
+    * Invokes an operation on the JMX server to register ourself as a
+    * listener on the TreeCache service.
+    *
+    * @throws Exception
+    */
+   private void removeAsCacheListener() throws Exception
+   {
+      if (registeredAsListener && cache != null)
+      {
+         cache.removeCacheListener(this);
+         registeredAsListener = false;
+      }
+   }
+
+   private void removeFromTreeCache(Fqn fqn) throws Exception
+   {
+      InvocationContext ctx = cache.getInvocationContext();
+      Option existing = ctx.getOptionOverrides();
+      try
+      {
+         ctx.setOptionOverrides(GRAVITATE_OPTION);
+         cache.removeNode(fqn);
+      }
+      finally
+      {
+         ctx.setOptionOverrides(existing);
+      }
+   }
+
+   /**
+    * 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);
+      }
+      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());
+      }
+      
+      
+   }
+
+} // end TreeCacheSSOClusterManager
+


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

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,38 @@
+<?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="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.service.sso.TreeCacheSSOClusterManager"
+         type="java.lang.String"/>
+
+   </mbean>
+
+</mbeans-descriptors>


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

Modified: trunk/tomcat/src/resources/META-INF/jboss-aop.xml
===================================================================
--- trunk/tomcat/src/resources/META-INF/jboss-aop.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/META-INF/jboss-aop.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -34,14 +34,14 @@
    </introduction>
 
    <!-- 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>
@@ -50,14 +50,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>

Added: trunk/tomcat/src/resources/jboss-web-cluster-beans.xml
===================================================================
--- trunk/tomcat/src/resources/jboss-web-cluster-beans.xml	                        (rev 0)
+++ trunk/tomcat/src/resources/jboss-web-cluster-beans.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+   <!-- First we create a Configuration object for the cache -->
+   <bean name="TomcatClusteringCacheConfig" class="org.jboss.cache.config.Configuration">
+      
+      <!-- Externally injected services -->  
+      <property name="runtimeConfig">
+         <bean name="TomcatClusteringCacheRuntimeConfig" class="org.jboss.cache.config.RuntimeConfig">
+            <property name="transactionManager">
+            	<bean name="TomcatClusteringBatchManager" class="org.jboss.cache.transaction.BatchModeTransactionManager"/>
+            </property>
+            <property name="muxChannelFactory"><inject bean="jgroups.mux:name=Multiplexer"/></property>
+         </bean>
+      </property>
+      
+      <property name="multiplexerStack">${jboss.multiplexer.stack:udp}</property>
+   
+      <property name="clusterName">${jboss.partition.name:DefaultPartition}-TomcatCache</property>
+   
+      <property name="nodeLockingSchemeString" class="java.lang.String">PESSIMISTIC</property>
+      <property name="isolationLevelString" class="java.lang.String">REPEATABLE_READ</property>
+      <property name="cacheModeString">REPL_ASYNC</property>
+   
+      <property name="syncReplTimeout">20000</property>
+      <property name="lockAcquisitionTimeout">15000</property>
+   
+      <property name="useRegionBasedMarshalling">false</property>
+      <property name="inactiveOnStartup">false</property>
+        
+      <property name="exposeManagementStatistics">true</property>
+   
+      <property name="buddyReplicationConfig">
+      	<bean name="TomcatClusteringBuddyReplicationConfig" 
+      		class="org.jboss.cache.config.BuddyReplicationConfig">
+      		
+      		<property name="enabled">true</property>
+            <property name="buddyPoolName">default</property>
+            <property name="buddyCommunicationTimeout">2000</property>
+
+            <property name="autoDataGravitation">false</property>
+            <property name="dataGravitationRemoveOnFind">true</property>
+            <property name="dataGravitationSearchBackupTrees">true</property>
+            
+            <property name="buddyLocatorConfig">
+            	<bean name="TomcatClusteringBuddyLocatorConfig" class="org.jboss.cache.buddyreplication.NextMemberBuddyLocatorConfig">
+            		<property name="numBuddies">1</property>
+            		<property name="ignoreColocatedBuddies">true</property>
+                </bean>
+      		</property>
+      	</bean>
+      </property>
+      <property name="cacheLoaderConfig">
+      	<bean name="TomcatClusteringCacheLoaderConfig" 
+      		class="org.jboss.cache.config.CacheLoaderConfig">
+      			<property name="passivation">true</property>
+                <property name="preload">/</property>
+                <property name="shared">false</property>
+                <property name="individualCacheLoaderConfigs">
+                	<list>
+                		<bean name="TomcatClusteringFileCacheLoader" class="org.jboss.cache.loader.FileCacheLoaderConfig">
+                			<property name="location">${jboss.server.data.dir}${/}tomcat-cluster</property>
+                    		<property name="async">true</property>
+                    		<property name="fetchPersistentState">true</property>
+                    		<property name="ignoreModifications">false</property>
+                		</bean>
+                	</list>
+                </property>
+      	</bean>
+      </property>
+   </bean>
+    
+   <!-- Now we use the above configuration to construct the cache itself -->
+   <bean name="TomcatClusteringCache" class="org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper">
+      
+      <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.cache:service=TomcatClusteringCache", exposedInterface=org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean.class, registerDirectly=true)</annotation>
+      
+      <property name="configuration"><inject bean="TomcatClusteringCacheConfig"/></property>
+      <property name="registerPlainCache">true</property>
+      
+   </bean>
+	
+</deployment>
\ No newline at end of file


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

Added: trunk/tomcat/src/resources/jboss-web-cluster-service.xml
===================================================================
--- trunk/tomcat/src/resources/jboss-web-cluster-service.xml	                        (rev 0)
+++ trunk/tomcat/src/resources/jboss-web-cluster-service.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,201 @@
+<?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>
+
+        <!--JGroups multiplexer configuration. Allows cache to use a shared
+            JGroups channel with other clustering services.
+            If you don't want to use the multiplexer, you can comment these
+            elements out and uncomment the ClusterConfig attribute below. -->
+        <depends>jgroups.mux:name=Multiplexer</depends>
+        <attribute name="MultiplexerService">jgroups.mux:name=Multiplexer</attribute>
+        <attribute name="MultiplexerStack">${jboss.multiplexer.stack:udp}</attribute>
+        
+        <!-- Name of cluster. Needs to be the same for all clusters, in order
+             to find each other -->
+        <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
+
+        <!-- make sure to specify BatchModeTransactionManager only! -->
+        <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</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 JGroups channel
+             we recommend you comment out the FC (flow control)
+             protocol in the channel config.
+        -->
+        <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+        <!-- JGroups protocol stack config in XML format.
+		     Not needed if multiplexer is used, so commented out by default.
+		     If your CacheMode is set to REPL_SYNC we recommend you comment
+             out the FC (flow control) protocol
+		     
+        <attribute name="ClusterConfig">
+            <config>
+                <UDP mcast_addr="${jgroups.udp.mcast_addr:230.1.2.7}" 
+                     mcast_port="45578"
+                     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="false" 
+                     ip_ttl="${jgroups.udp.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>
+        </attribute>
+        -->
+        
+        <!-- JGroups protocol stack properties in String format. 
+             Not needed if multiplexer is used or ClusterConfig property is set. 
+             Can also be a URL, e.g. file:/home/bela/default.xml
+           <attribute name="ClusterProperties"></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>
+
+        <!--
+          Indicate 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 when a webapp is
+          deployed.  Must have the same value as "UseRegionBasedMarshalling".
+        -->
+		<attribute name="InactiveOnStartup">false</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>true</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>
+
+		<!-- passivation store config using jboss cache 1.3 + -->
+ 
+        <attribute name="CacheLoaderConfiguration">
+            <config>
+                <passivation>true</passivation>
+                <preload>/</preload>
+                <shared>false</shared>
+                <cacheloader>
+                    <class>org.jboss.cache.loader.FileCacheLoader</class>
+                    <properties>
+                        location=/tmp
+                    </properties>
+                    <async>true</async>
+                    <fetchPersistentState>true</fetchPersistentState>
+                    <ignoreModifications>false</ignoreModifications>
+                </cacheloader>
+            </config>
+        </attribute>
+
+         
+    </mbean>
+
+</server>


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

Modified: trunk/tomcat/src/resources/server.xml
===================================================================
--- trunk/tomcat/src/resources/server.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/server.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -10,7 +10,7 @@
    connectors.
    -->
    <Service name="jboss.web"
-      className="org.jboss.web.tomcat.tc6.StandardService">
+      className="org.jboss.web.tomcat.service.StandardService">
 
       <!-- A HTTP/1.1 Connector on port 8080 -->
       <Connector port="8080" address="${jboss.bind.address}"
@@ -125,7 +125,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 "tc6-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
@@ -142,7 +142,7 @@
                               to support session replication.
             -->
             <!--
-            <Valve className="org.jboss.web.tomcat.tc6.sso.ClusteredSingleSignOn" />
+            <Valve className="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn" />
             -->
          
             <!-- Check for unclosed connections and transaction terminated checks
@@ -151,7 +151,7 @@
                  Important: The dependency on the CachedConnectionManager
                  in META-INF/jboss-service.xml must be uncommented, too
             TODO:
-            <Valve className="org.jboss.web.tomcat.tc6.jca.CachedConnectionValve"
+            <Valve className="org.jboss.web.tomcat.service.jca.CachedConnectionValve"
                 cachedConnectionManagerObjectName="jboss.jca:service=CachedConnectionManager"
                 transactionManagerObjectName="jboss:service=TransactionManager" />
            -->

Deleted: trunk/tomcat/src/resources/tc5-cluster-service.xml
===================================================================
--- trunk/tomcat/src/resources/tc5-cluster-service.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/tc5-cluster-service.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,194 +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>
-        
-        <!--uncomment next three statements if using JGroups multiplexer -->
-        <!--  
-        <depends>jgroups.mux:name=Multiplexer</depends>
-        <attribute name="MultiplexerService">jgroups.mux:name=Multiplexer</attribute>
-        <attribute name="MultiplexerStack">fc-fast-minimalthreads</attribute>
-        -->
-
-        <!-- make sure to specify BatchModeTransactionManager only! -->
-        <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</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>
-
-        <!-- Name of cluster. Needs to be the same for all clusters, in order
-             to find each other
-        -->
-        <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
-
-        <!-- JGroups protocol stack properties. Can also be a URL,
-             e.g. file:/home/bela/default.xml
-           <attribute name="ClusterProperties"></attribute>
-        -->
-
-        <attribute name="ClusterConfig">
-            <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="false" 
-                     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="true" down_thread="true"
-	               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="100,200,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="2100000"/>
-                <pbcast.GMS print_local_addr="true" join_timeout="3000" 
-                            down_thread="false" up_thread="false"
-                            join_retry_timeout="2000" shun="true"/>
-                <!-- If your CacheMode is set to REPL_SYNC we recommend you
-                     comment out the FC (flow control) protocol -->
-                <FC max_credits="10000000" down_thread="false" up_thread="false"
-                    min_threshold="0.20"/>
-                <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
-                <pbcast.STATE_TRANSFER down_thread="false" up_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>
-
-        <!--
-          Indicate 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 when a webapp is
-          deployed.  Must have the same value as "UseRegionBasedMarshalling".
-        -->
-		<attribute name="InactiveOnStartup">false</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>true</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>
-
-		<!-- passivation store config using jboss cache 1.3 + -->
- 
-        <attribute name="CacheLoaderConfiguration">
-            <config>
-                <passivation>true</passivation>
-                <preload>/</preload>
-                <shared>false</shared>
-                <cacheloader>
-                    <class>org.jboss.cache.loader.FileCacheLoader</class>
-                    <properties>
-                        location=/tmp
-                    </properties>
-                    <async>true</async>
-                    <fetchPersistentState>true</fetchPersistentState>
-                    <ignoreModifications>false</ignoreModifications>
-                </cacheloader>
-            </config>
-        </attribute>
-    </mbean>
-
-</server>

Deleted: trunk/tomcat/src/resources/tc6-cluster-beans.xml
===================================================================
--- trunk/tomcat/src/resources/tc6-cluster-beans.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/tc6-cluster-beans.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<deployment xmlns="urn:jboss:bean-deployer:2.0">
-
-   <!-- First we create a Configuration object for the cache -->
-   <bean name="TomcatClusteringCacheConfig" class="org.jboss.cache.config.Configuration">
-      
-      <!-- Externally injected services -->  
-      <property name="runtimeConfig">
-         <bean name="TomcatClusteringCacheRuntimeConfig" class="org.jboss.cache.config.RuntimeConfig">
-            <property name="transactionManager">
-            	<bean name="TomcatClusteringBatchManager" class="org.jboss.cache.transaction.BatchModeTransactionManager"/>
-            </property>
-            <property name="muxChannelFactory"><inject bean="jgroups.mux:name=Multiplexer"/></property>
-         </bean>
-      </property>
-      
-      <property name="multiplexerStack">${jboss.multiplexer.stack:udp}</property>
-   
-      <property name="clusterName">${jboss.partition.name:DefaultPartition}-TomcatCache</property>
-   
-      <property name="nodeLockingSchemeString" class="java.lang.String">PESSIMISTIC</property>
-      <property name="isolationLevelString" class="java.lang.String">REPEATABLE_READ</property>
-      <property name="cacheModeString">REPL_ASYNC</property>
-   
-      <property name="syncReplTimeout">20000</property>
-      <property name="lockAcquisitionTimeout">15000</property>
-   
-      <property name="useRegionBasedMarshalling">false</property>
-      <property name="inactiveOnStartup">false</property>
-        
-      <property name="exposeManagementStatistics">true</property>
-   
-      <property name="buddyReplicationConfig">
-      	<bean name="TomcatClusteringBuddyReplicationConfig" 
-      		class="org.jboss.cache.config.BuddyReplicationConfig">
-      		
-      		<property name="enabled">true</property>
-            <property name="buddyPoolName">default</property>
-            <property name="buddyCommunicationTimeout">2000</property>
-
-            <property name="autoDataGravitation">false</property>
-            <property name="dataGravitationRemoveOnFind">true</property>
-            <property name="dataGravitationSearchBackupTrees">true</property>
-            
-            <property name="buddyLocatorConfig">
-            	<bean name="TomcatClusteringBuddyLocatorConfig" class="org.jboss.cache.buddyreplication.NextMemberBuddyLocatorConfig">
-            		<property name="numBuddies">1</property>
-            		<property name="ignoreColocatedBuddies">true</property>
-                </bean>
-      		</property>
-      	</bean>
-      </property>
-      <property name="cacheLoaderConfig">
-      	<bean name="TomcatClusteringCacheLoaderConfig" 
-      		class="org.jboss.cache.config.CacheLoaderConfig">
-      			<property name="passivation">true</property>
-                <property name="preload">/</property>
-                <property name="shared">false</property>
-                <property name="individualCacheLoaderConfigs">
-                	<list>
-                		<bean name="TomcatClusteringFileCacheLoader" class="org.jboss.cache.loader.FileCacheLoaderConfig">
-                			<property name="location">${jboss.server.data.dir}${/}tomcat-cluster</property>
-                    		<property name="async">true</property>
-                    		<property name="fetchPersistentState">true</property>
-                    		<property name="ignoreModifications">false</property>
-                		</bean>
-                	</list>
-                </property>
-      	</bean>
-      </property>
-   </bean>
-    
-   <!-- Now we use the above configuration to construct the cache itself -->
-   <bean name="TomcatClusteringCache" class="org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper">
-      
-      <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.cache:service=TomcatClusteringCache", exposedInterface=org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean.class, registerDirectly=true)</annotation>
-      
-      <property name="configuration"><inject bean="TomcatClusteringCacheConfig"/></property>
-      <property name="registerPlainCache">true</property>
-      
-   </bean>
-	
-</deployment>
\ No newline at end of file

Deleted: trunk/tomcat/src/resources/tc6-cluster-service.xml
===================================================================
--- trunk/tomcat/src/resources/tc6-cluster-service.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/tc6-cluster-service.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,201 +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>
-
-        <!--JGroups multiplexer configuration. Allows cache to use a shared
-            JGroups channel with other clustering services.
-            If you don't want to use the multiplexer, you can comment these
-            elements out and uncomment the ClusterConfig attribute below. -->
-        <depends>jgroups.mux:name=Multiplexer</depends>
-        <attribute name="MultiplexerService">jgroups.mux:name=Multiplexer</attribute>
-        <attribute name="MultiplexerStack">${jboss.multiplexer.stack:udp}</attribute>
-        
-        <!-- Name of cluster. Needs to be the same for all clusters, in order
-             to find each other -->
-        <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
-
-        <!-- make sure to specify BatchModeTransactionManager only! -->
-        <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</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 JGroups channel
-             we recommend you comment out the FC (flow control)
-             protocol in the channel config.
-        -->
-        <attribute name="CacheMode">REPL_ASYNC</attribute>
-
-        <!-- JGroups protocol stack config in XML format.
-		     Not needed if multiplexer is used, so commented out by default.
-		     If your CacheMode is set to REPL_SYNC we recommend you comment
-             out the FC (flow control) protocol
-		     
-        <attribute name="ClusterConfig">
-            <config>
-                <UDP mcast_addr="${jgroups.udp.mcast_addr:230.1.2.7}" 
-                     mcast_port="45578"
-                     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="false" 
-                     ip_ttl="${jgroups.udp.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>
-        </attribute>
-        -->
-        
-        <!-- JGroups protocol stack properties in String format. 
-             Not needed if multiplexer is used or ClusterConfig property is set. 
-             Can also be a URL, e.g. file:/home/bela/default.xml
-           <attribute name="ClusterProperties"></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>
-
-        <!--
-          Indicate 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 when a webapp is
-          deployed.  Must have the same value as "UseRegionBasedMarshalling".
-        -->
-		<attribute name="InactiveOnStartup">false</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>true</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>
-
-		<!-- passivation store config using jboss cache 1.3 + -->
- 
-        <attribute name="CacheLoaderConfiguration">
-            <config>
-                <passivation>true</passivation>
-                <preload>/</preload>
-                <shared>false</shared>
-                <cacheloader>
-                    <class>org.jboss.cache.loader.FileCacheLoader</class>
-                    <properties>
-                        location=/tmp
-                    </properties>
-                    <async>true</async>
-                    <fetchPersistentState>true</fetchPersistentState>
-                    <ignoreModifications>false</ignoreModifications>
-                </cacheloader>
-            </config>
-        </attribute>
-
-         
-    </mbean>
-
-</server>

Modified: trunk/tomcat/src/resources/war-deployers-all-beans.xml
===================================================================
--- trunk/tomcat/src/resources/war-deployers-all-beans.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/war-deployers-all-beans.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,208 +1,208 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-Web application deployers
-$Id:$
--->
-<deployment xmlns="urn:jboss:bean-deployer:2.0">
-
-   <!-- Tomcat
-   <bean name="VFSClassLoader" class="org.jboss.virtual.classloading.VFSClassLoader">
-      <constructor factoryClass="org.jboss.virtual.classloading.VFSClassLoaderFactory"
-                   factoryMethod="newClassLoader">
-         <parameter class="java.net.URI">${jboss.server.home.url}/deployers/jbossweb-tomcat6.deployer/</parameter>
-         <parameter>
-         	<array class="[Ljava.lang.String;" elementClass="java.lang.String">
-	         	<value>*.jar</value>
-	         	<value>jsf-libs/*.jar</value>
-         	</array>
-         	</parameter>
-      </constructor>
-   </bean>
-   -->
-
-   <!-- web.xml parsing deployer -->
-   <bean name="WebAppParsingDeployer" class="org.jboss.deployment.WebAppParsingDeployer">
-      <install bean="MainDeployer" method="addDeployer">
-         <parameter><this/></parameter>
-      </install>
-      <uninstall bean="MainDeployer" method="removeDeployer">
-         <parameter><this/></parameter>
-      </uninstall>
-      <property name="type">war</property>
-      <property name="relativeOrder">2000</property>
-   </bean>
-   <bean name="JBossWebAppParsingDeployer" class="org.jboss.deployment.JBossWebAppParsingDeployer">
-      <install bean="MainDeployer" method="addDeployer">
-         <parameter><this/></parameter>
-      </install>
-      <uninstall bean="MainDeployer" method="removeDeployer">
-         <parameter><this/></parameter>
-      </uninstall>
-      <property name="type">war</property>
-      <property name="relativeOrder">2001</property>
-   </bean>
-   
-   <bean name="WarDeployer" class="org.jboss.web.tomcat.tc6.deployers.TomcatDeployer">
-      <install bean="MainDeployer" method="addDeployer">
-         <parameter><this/></parameter>
-      </install>
-      <uninstall bean="MainDeployer" method="removeDeployer">
-         <parameter><this/></parameter>
-      </uninstall>
-
-      <property name="type">war</property>
-      <!-- You can configure a set of authenticators keyed by http-auth method
-         used. This will apply the same set of authenticators across all web
-         applications. You can override the set of authenticators at the web
-         application level by adding <authenticators> element to the respective
-         jboss-web.xml
-      -->      
-      <property name="authenticators">
-         <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String">
-            <entry>
-               <key>BASIC</key>
-               <value>org.apache.catalina.authenticator.BasicAuthenticator</value>
-            </entry>
-            <entry>
-               <key>CLIENT-CERT</key>
-               <value>org.apache.catalina.authenticator.SSLAuthenticator</value>
-            </entry>
-            <entry>
-               <key>DIGEST</key>
-               <value>org.apache.catalina.authenticator.DigestAuthenticator</value>
-            </entry>
-            <entry>
-               <key>FORM</key>
-               <value>org.apache.catalina.authenticator.FormAuthenticator</value>
-            </entry>
-            <entry>
-               <key>NONE</key>
-               <value>org.apache.catalina.authenticator.NonLoginAuthenticator</value>
-            </entry>
-         </map>         
-      </property>
-      
-      <!-- The JAAS security domain to use in the absense of an explicit
-      security-domain specification in the war WEB-INF/jboss-web.xml
-      -->
-      <property name="defaultSecurityDomain">java:/jaas/other</property>
-
-      <!-- Get the flag indicating if the normal Java2 parent first class
-           loading model should be used over the servlet 2.3 web container first
-           model.
-      -->
-      <property name="java2ClassLoadingCompliance">false</property>
-      <!-- A flag indicating if the JBoss Loader should be used. This loader
-           uses a unified class loader as the class loader rather than the tomcat
-           specific class loader.
-           The default is false to ensure that wars have isolated class loading
-           for duplicate jars and jsp files.
-      -->
-      <property name="useJBossWebLoader">false</property>
-      <!-- The list of package prefixes that should not be loaded without
-         delegating to the parent class loader before trying the web app
-         class loader. The packages listed here are those tha are used by
-         the web container implementation and cannot be overriden. The format
-         is a comma separated list of the package names. There cannot be any
-         whitespace between the package prefixes.
-         This setting only applies when UseJBossWebLoader=false.
-      -->
-      <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
-
-      <property name="lenientEjbLink">true</property>
-
-      <!--Flag to delete the Work Dir on Context Destroy -->
-      <property name="deleteWorkDirOnContextDestroy">false</property>
-
-      <!--
-          Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
-          - org.jboss.web.tomcat.tc6.session.JBossCacheManager
-      -->
-      <property name="managerClass">org.jboss.web.tomcat.tc6.session.JBossCacheManager</property>
-
-      <!-- The name of the request property under with the authenticated JAAS
-      Subject is stored on successful authentication. If null or empty then
-      the Subject will not be stored.
-      -->
-      <!--
-      <property name="subjectAttributeName">j_subject</property>
-      -->
-
-      <!-- The SessionIdAlphabet is the set of characters used to create a session Id
-           It must be made up of exactly 65 unique characters
-      <property name="sessionIdAlphabet">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*</property>
-      -->
-
-      <!--
-      *******************************************************
-      ****************** CLUSTERING *************************
-      *******************************************************
-      In order to activate HTTP Session clustering for Tomcat
-      make sure you run JBoss's "all" configuration i.e.
-      "run -c all"
-      (the default configuration doesn't contain clustering)
-
-      Furthermore, you may change SnapshotMode and
-      SnapshotInterval propertys below to indicate when to
-      synchronize changes with the other node(s).
-
-      If you use Apache+mod_jk(2) you will most probably use
-      the AJP1.3 connector below. Thus, if you so wish,
-      you may comment (i.e. deactivate) the HTTP connector
-      as it won't be used anymore.
-
-      *******************************************************
-      *******************************************************
-      *******************************************************
-      -->
-		
-      <!--
-      If you are using clustering, the following two propertys
-      define when the sessions are replicated to the other nodes.
-      The default value, "instant", synchronously replicates changes
-      to the other nodes at the end of requests. In this case, the 
-      "SnapshotInterval" property is not used.
-      The "interval" mode, in association with the "SnapshotInterval"
-      property, indicates that modified sessions will only be replicated
-      every "SnapshotInterval" milliseconds at most.	
-		
-      Note that this property is not in effect if the replication-granularity
-      is set to FIELD. If it is FIELD, it will be per http request (that is, 
-      "instant" mode.)
-      -->
-      <property name="snapshotMode">instant</property> <!-- you may switch to "interval" -->
-      <property name="snapshotInterval">2000</property>
-
-      <!--
-      Whether to use MOD_JK(2) for load balancing with sticky session 
-      combined with JvmRoute. If set to true, it will insert a JvmRouteValve 
-      to intercept every request and replace the JvmRoute if it detects a 
-      failover. In addition, you will need to set the JvmRoute inside
-      Tomcat, e.g.,
-        Engine name="jboss.web" jmvRoute="Node1" defaultHost="localhost" 
-      in server.xml.
-
-      For clustering purpose only.
-      -->
-      <property name="useJK">false</property>
-
-      <property name="domain">jboss.web</property>
-		<property name="securityManagerService">
-			<inject bean="jboss.security:service=JaasSecurityManager" />
-	   </property>
-	  <!-- TODO determine how to express this dependency 
-      <depends>jboss.cache:service=TomcatClusteringCache</depends>
-      -->
-      
-      <depends>jboss:service=TransactionManager</depends>
-      <depends>SecurityDeployer</depends>
-      
-      <!-- Only needed if the org.jboss.web.tomcat.tc6.jca.CachedConnectionValve
-TODO: injection
-      is enabled in the tomcat server.xml file.
-      <depends>jboss.jca:service=CachedConnectionManager</depends>
-      -->
-   </bean>
-
-</deployment>
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Web application deployers
+$Id:$
+-->
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+   <!-- Tomcat
+   <bean name="VFSClassLoader" class="org.jboss.virtual.classloading.VFSClassLoader">
+      <constructor factoryClass="org.jboss.virtual.classloading.VFSClassLoaderFactory"
+                   factoryMethod="newClassLoader">
+         <parameter class="java.net.URI">${jboss.server.home.url}/deployers/jbossweb-tomcat6.deployer/</parameter>
+         <parameter>
+         	<array class="[Ljava.lang.String;" elementClass="java.lang.String">
+	         	<value>*.jar</value>
+	         	<value>jsf-libs/*.jar</value>
+         	</array>
+         	</parameter>
+      </constructor>
+   </bean>
+   -->
+
+   <!-- web.xml parsing deployer -->
+   <bean name="WebAppParsingDeployer" class="org.jboss.deployment.WebAppParsingDeployer">
+      <install bean="MainDeployer" method="addDeployer">
+         <parameter><this/></parameter>
+      </install>
+      <uninstall bean="MainDeployer" method="removeDeployer">
+         <parameter><this/></parameter>
+      </uninstall>
+      <property name="type">war</property>
+      <property name="relativeOrder">2000</property>
+   </bean>
+   <bean name="JBossWebAppParsingDeployer" class="org.jboss.deployment.JBossWebAppParsingDeployer">
+      <install bean="MainDeployer" method="addDeployer">
+         <parameter><this/></parameter>
+      </install>
+      <uninstall bean="MainDeployer" method="removeDeployer">
+         <parameter><this/></parameter>
+      </uninstall>
+      <property name="type">war</property>
+      <property name="relativeOrder">2001</property>
+   </bean>
+   
+   <bean name="WarDeployer" class="org.jboss.web.tomcat.service.deployers.TomcatDeployer">
+      <install bean="MainDeployer" method="addDeployer">
+         <parameter><this/></parameter>
+      </install>
+      <uninstall bean="MainDeployer" method="removeDeployer">
+         <parameter><this/></parameter>
+      </uninstall>
+
+      <property name="type">war</property>
+      <!-- You can configure a set of authenticators keyed by http-auth method
+         used. This will apply the same set of authenticators across all web
+         applications. You can override the set of authenticators at the web
+         application level by adding <authenticators> element to the respective
+         jboss-web.xml
+      -->      
+      <property name="authenticators">
+         <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String">
+            <entry>
+               <key>BASIC</key>
+               <value>org.apache.catalina.authenticator.BasicAuthenticator</value>
+            </entry>
+            <entry>
+               <key>CLIENT-CERT</key>
+               <value>org.apache.catalina.authenticator.SSLAuthenticator</value>
+            </entry>
+            <entry>
+               <key>DIGEST</key>
+               <value>org.apache.catalina.authenticator.DigestAuthenticator</value>
+            </entry>
+            <entry>
+               <key>FORM</key>
+               <value>org.apache.catalina.authenticator.FormAuthenticator</value>
+            </entry>
+            <entry>
+               <key>NONE</key>
+               <value>org.apache.catalina.authenticator.NonLoginAuthenticator</value>
+            </entry>
+         </map>         
+      </property>
+      
+      <!-- The JAAS security domain to use in the absense of an explicit
+      security-domain specification in the war WEB-INF/jboss-web.xml
+      -->
+      <property name="defaultSecurityDomain">java:/jaas/other</property>
+
+      <!-- Get the flag indicating if the normal Java2 parent first class
+           loading model should be used over the servlet 2.3 web container first
+           model.
+      -->
+      <property name="java2ClassLoadingCompliance">false</property>
+      <!-- A flag indicating if the JBoss Loader should be used. This loader
+           uses a unified class loader as the class loader rather than the tomcat
+           specific class loader.
+           The default is false to ensure that wars have isolated class loading
+           for duplicate jars and jsp files.
+      -->
+      <property name="useJBossWebLoader">false</property>
+      <!-- The list of package prefixes that should not be loaded without
+         delegating to the parent class loader before trying the web app
+         class loader. The packages listed here are those tha are used by
+         the web container implementation and cannot be overriden. The format
+         is a comma separated list of the package names. There cannot be any
+         whitespace between the package prefixes.
+         This setting only applies when UseJBossWebLoader=false.
+      -->
+      <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
+
+      <property name="lenientEjbLink">true</property>
+
+      <!--Flag to delete the Work Dir on Context Destroy -->
+      <property name="deleteWorkDirOnContextDestroy">false</property>
+
+      <!--
+          Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
+          - org.jboss.web.tomcat.service.session.JBossCacheManager
+      -->
+      <property name="managerClass">org.jboss.web.tomcat.service.session.JBossCacheManager</property>
+
+      <!-- The name of the request property under with the authenticated JAAS
+      Subject is stored on successful authentication. If null or empty then
+      the Subject will not be stored.
+      -->
+      <!--
+      <property name="subjectAttributeName">j_subject</property>
+      -->
+
+      <!-- The SessionIdAlphabet is the set of characters used to create a session Id
+           It must be made up of exactly 65 unique characters
+      <property name="sessionIdAlphabet">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*</property>
+      -->
+
+      <!--
+      *******************************************************
+      ****************** CLUSTERING *************************
+      *******************************************************
+      In order to activate HTTP Session clustering for Tomcat
+      make sure you run JBoss's "all" configuration i.e.
+      "run -c all"
+      (the default configuration doesn't contain clustering)
+
+      Furthermore, you may change SnapshotMode and
+      SnapshotInterval propertys below to indicate when to
+      synchronize changes with the other node(s).
+
+      If you use Apache+mod_jk(2) you will most probably use
+      the AJP1.3 connector below. Thus, if you so wish,
+      you may comment (i.e. deactivate) the HTTP connector
+      as it won't be used anymore.
+
+      *******************************************************
+      *******************************************************
+      *******************************************************
+      -->
+		
+      <!--
+      If you are using clustering, the following two propertys
+      define when the sessions are replicated to the other nodes.
+      The default value, "instant", synchronously replicates changes
+      to the other nodes at the end of requests. In this case, the 
+      "SnapshotInterval" property is not used.
+      The "interval" mode, in association with the "SnapshotInterval"
+      property, indicates that modified sessions will only be replicated
+      every "SnapshotInterval" milliseconds at most.	
+		
+      Note that this property is not in effect if the replication-granularity
+      is set to FIELD. If it is FIELD, it will be per http request (that is, 
+      "instant" mode.)
+      -->
+      <property name="snapshotMode">instant</property> <!-- you may switch to "interval" -->
+      <property name="snapshotInterval">2000</property>
+
+      <!--
+      Whether to use MOD_JK(2) for load balancing with sticky session 
+      combined with JvmRoute. If set to true, it will insert a JvmRouteValve 
+      to intercept every request and replace the JvmRoute if it detects a 
+      failover. In addition, you will need to set the JvmRoute inside
+      Tomcat, e.g.,
+        Engine name="jboss.web" jmvRoute="Node1" defaultHost="localhost" 
+      in server.xml.
+
+      For clustering purpose only.
+      -->
+      <property name="useJK">false</property>
+
+      <property name="domain">jboss.web</property>
+		<property name="securityManagerService">
+			<inject bean="jboss.security:service=JaasSecurityManager" />
+	   </property>
+	  <!-- TODO determine how to express this dependency 
+      <depends>jboss.cache:service=TomcatClusteringCache</depends>
+      -->
+      
+      <depends>jboss:service=TransactionManager</depends>
+      <depends>SecurityDeployer</depends>
+      
+      <!-- Only needed if the org.jboss.web.tomcat.service.jca.CachedConnectionValve
+TODO: injection
+      is enabled in the tomcat server.xml file.
+      <depends>jboss.jca:service=CachedConnectionManager</depends>
+      -->
+   </bean>
+
+</deployment>

Modified: trunk/tomcat/src/resources/war-deployers-beans.xml
===================================================================
--- trunk/tomcat/src/resources/war-deployers-beans.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/war-deployers-beans.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,238 +1,238 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-Web application deployers
-$Id:$
--->
-<deployment xmlns="urn:jboss:bean-deployer:2.0">
-
-   <!-- Tomcat
-   <bean name="VFSClassLoader" class="org.jboss.virtual.classloading.VFSClassLoader">
-      <constructor factoryClass="org.jboss.virtual.classloading.VFSClassLoaderFactory"
-                   factoryMethod="newClassLoader">
-         <parameter class="java.net.URI">${jboss.server.home.url}/deployers/jbossweb-tomcat6.deployer/</parameter>
-         <parameter>
-         	<array class="[Ljava.lang.String;" elementClass="java.lang.String">
-	         	<value>*.jar</value>
-	         	<value>jsf-libs/*.jar</value>
-         	</array>
-         	</parameter>
-      </constructor>
-   </bean>
-   -->
-
-   <!-- web.xml parsing deployer -->
-   <bean name="WebAppParsingDeployer" class="org.jboss.deployment.WebAppParsingDeployer">
-      <install bean="MainDeployer" method="addDeployer">
-         <parameter><this/></parameter>
-      </install>
-      <uninstall bean="MainDeployer" method="removeDeployer">
-         <parameter><this/></parameter>
-      </uninstall>
-      <property name="type">war</property>
-      <property name="relativeOrder">2000</property>
-   </bean>
-   <bean name="JBossWebAppParsingDeployer" class="org.jboss.deployment.JBossWebAppParsingDeployer">
-      <install bean="MainDeployer" method="addDeployer">
-         <parameter><this/></parameter>
-      </install>
-      <uninstall bean="MainDeployer" method="removeDeployer">
-         <parameter><this/></parameter>
-      </uninstall>
-      <property name="type">war</property>
-      <property name="relativeOrder">2001</property>
-   </bean>
-
-   <!-- Allow for war local class loaders: in testing
-   <bean name="WarClassLoaderDeployer" class="org.jboss.web.tomcat.tc6.deployers.WarClassLoaderDeployer">
-      <install bean="MainDeployer" method="addDeployer">
-         <parameter><this/></parameter>
-      </install>
-      <uninstall bean="MainDeployer" method="removeDeployer">
-         <parameter><this/></parameter>
-      </uninstall>
-      <!- - CLASSLOADER_DEPLOYER - 1 - ->
-      <property name="relativeOrder">3999</property>
-      <!- - A flag indicating if the JBoss Loader should be used. This loader
-         uses a unified class loader as the class loader rather than the tomcat
-         specific class loader.
-         The default is false to ensure that wars have isolated class loading
-         for duplicate jars and jsp files.
-      - ->
-      <property name="useJBossWebLoader">false</property>
-      <!- - The list of package prefixes that should not be loaded without
-         delegating to the parent class loader before trying the web app
-         class loader. The packages listed here are those tha are used by
-         the web container implementation and cannot be overriden. The format
-         is a comma separated list of the package names. There cannot be any
-         whitespace between the package prefixes.
-         This setting only applies when UseJBossWebLoader=false.
-         - ->
-      <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
-   </bean>
-  -->
-
-   <!-- The WebMetaData to service mbean deployer -->
-   <bean name="WarDeployer" class="org.jboss.web.tomcat.tc6.deployers.TomcatDeployer">
-      <install bean="MainDeployer" method="addDeployer">
-         <parameter><this/></parameter>
-      </install>
-      <uninstall bean="MainDeployer" method="removeDeployer">
-         <parameter><this/></parameter>
-      </uninstall>
-
-      <property name="type">war</property>
-      <!-- You can configure a set of authenticators keyed by http-auth method
-      used. This will apply the same set of authenticators across all web
-      applications. You can override the set of authenticators at the web
-      application level by adding <authenticators> element to the respective
-      jboss-web.xml
-      -->      
-      <property name="authenticators">
-         <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String">
-            <entry>
-               <key>BASIC</key>
-               <value>org.apache.catalina.authenticator.BasicAuthenticator</value>
-            </entry>
-            <entry>
-               <key>CLIENT-CERT</key>
-               <value>org.apache.catalina.authenticator.SSLAuthenticator</value>
-            </entry>
-            <entry>
-               <key>DIGEST</key>
-               <value>org.apache.catalina.authenticator.DigestAuthenticator</value>
-            </entry>
-            <entry>
-               <key>FORM</key>
-               <value>org.apache.catalina.authenticator.FormAuthenticator</value>
-            </entry>
-            <entry>
-               <key>NONE</key>
-               <value>org.apache.catalina.authenticator.NonLoginAuthenticator</value>
-            </entry>
-         </map>         
-      </property>
-      
-      <!-- The JAAS security domain to use in the absense of an explicit
-      security-domain specification in the war WEB-INF/jboss-web.xml
-      -->
-      <property name="defaultSecurityDomain">java:/jaas/other</property>
-
-      <!-- Get the flag indicating if the normal Java2 parent first class
-           loading model should be used over the servlet 2.3 web container first
-           model.
-      -->
-      <property name="java2ClassLoadingCompliance">false</property>
-      <!-- A flag indicating if the JBoss Loader should be used. This loader
-           uses a unified class loader as the class loader rather than the tomcat
-           specific class loader.
-           The default is false to ensure that wars have isolated class loading
-           for duplicate jars and jsp files.
-      -->
-      <property name="useJBossWebLoader">false</property>
-      <!-- The list of package prefixes that should not be loaded without
-         delegating to the parent class loader before trying the web app
-         class loader. The packages listed here are those tha are used by
-         the web container implementation and cannot be overriden. The format
-         is a comma separated list of the package names. There cannot be any
-         whitespace between the package prefixes.
-         This setting only applies when UseJBossWebLoader=false.
-      -->
-      <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
-
-      <property name="lenientEjbLink">true</property>
-
-      <!--Flag to delete the Work Dir on Context Destroy -->
-      <property name="deleteWorkDirOnContextDestroy">false</property>
-
-      <!--
-          Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
-          - org.jboss.web.tomcat.tc6.session.JBossCacheManager
-      -->
-      <property name="managerClass">org.jboss.web.tomcat.tc6.session.JBossCacheManager</property>
-
-      <!-- The class used as the deployment wrapper -->
-      <property name="deploymentClass">org.jboss.web.tomcat.tc6.deployers.TomcatDeployment</property>
-
-      <!-- The name of the request property under with the authenticated JAAS
-      Subject is stored on successful authentication. If null or empty then
-      the Subject will not be stored.
-      -->
-      <!--
-      <property name="subjectAttributeName">j_subject</property>
-      -->
-
-      <!-- The SessionIdAlphabet is the set of characters used to create a session Id
-           It must be made up of exactly 65 unique characters
-      <property name="sessionIdAlphabet">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*</property>
-      -->
-
-      <!--
-      *******************************************************
-      ****************** CLUSTERING *************************
-      *******************************************************
-      In order to activate HTTP Session clustering for Tomcat
-      make sure you run JBoss's "all" configuration i.e.
-      "run -c all"
-      (the default configuration doesn't contain clustering)
-
-      Furthermore, you may change SnapshotMode and
-      SnapshotInterval propertys below to indicate when to
-      synchronize changes with the other node(s).
-
-      If you use Apache+mod_jk(2) you will most probably use
-      the AJP1.3 connector below. Thus, if you so wish,
-      you may comment (i.e. deactivate) the HTTP connector
-      as it won't be used anymore.
-
-      *******************************************************
-      *******************************************************
-      *******************************************************
-      -->
-		
-      <!--
-      If you are using clustering, the following two propertys
-      define when the sessions are replicated to the other nodes.
-      The default value, "instant", synchronously replicates changes
-      to the other nodes at the end of requests. In this case, the 
-      "SnapshotInterval" property is not used.
-      The "interval" mode, in association with the "SnapshotInterval"
-      property, indicates that modified sessions will only be replicated
-      every "SnapshotInterval" milliseconds at most.	
-		
-      Note that this property is not in effect if the replication-granularity
-      is set to FIELD. If it is FIELD, it will be per http request (that is, 
-      "instant" mode.)
-      -->
-      <property name="snapshotMode">instant</property> <!-- you may switch to "interval" -->
-      <property name="snapshotInterval">2000</property>
-
-      <!--
-      Whether to use MOD_JK(2) for load balancing with sticky session 
-      combined with JvmRoute. If set to true, it will insert a JvmRouteValve 
-      to intercept every request and replace the JvmRoute if it detects a 
-      failover. In addition, you will need to set the JvmRoute inside
-      Tomcat, e.g.,
-        Engine name="jboss.web" jmvRoute="Node1" defaultHost="localhost" 
-      in server.xml.
-
-      For clustering purpose only.
-      -->
-      <property name="useJK">false</property>
-
-      <property name="domain">jboss.web</property>
-		<property name="securityManagerService">
-			<inject bean="jboss.security:service=JaasSecurityManager" />
-	   </property>
-      
-      <depends>jboss:service=TransactionManager</depends>
-      <depends>SecurityDeployer</depends>
-
-      <!-- Only needed if the org.jboss.web.tomcat.tc6.jca.CachedConnectionValve
-TODO: injection
-      is enabled in the tomcat server.xml file.
-      <depends>jboss.jca:service=CachedConnectionManager</depends>
-      -->
-   </bean>
-
-</deployment>
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Web application deployers
+$Id:$
+-->
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+   <!-- Tomcat
+   <bean name="VFSClassLoader" class="org.jboss.virtual.classloading.VFSClassLoader">
+      <constructor factoryClass="org.jboss.virtual.classloading.VFSClassLoaderFactory"
+                   factoryMethod="newClassLoader">
+         <parameter class="java.net.URI">${jboss.server.home.url}/deployers/jboss-web.deployer/</parameter>
+         <parameter>
+         	<array class="[Ljava.lang.String;" elementClass="java.lang.String">
+	         	<value>*.jar</value>
+	         	<value>jsf-libs/*.jar</value>
+         	</array>
+         	</parameter>
+      </constructor>
+   </bean>
+   -->
+
+   <!-- web.xml parsing deployer -->
+   <bean name="WebAppParsingDeployer" class="org.jboss.deployment.WebAppParsingDeployer">
+      <install bean="MainDeployer" method="addDeployer">
+         <parameter><this/></parameter>
+      </install>
+      <uninstall bean="MainDeployer" method="removeDeployer">
+         <parameter><this/></parameter>
+      </uninstall>
+      <property name="type">war</property>
+      <property name="relativeOrder">2000</property>
+   </bean>
+   <bean name="JBossWebAppParsingDeployer" class="org.jboss.deployment.JBossWebAppParsingDeployer">
+      <install bean="MainDeployer" method="addDeployer">
+         <parameter><this/></parameter>
+      </install>
+      <uninstall bean="MainDeployer" method="removeDeployer">
+         <parameter><this/></parameter>
+      </uninstall>
+      <property name="type">war</property>
+      <property name="relativeOrder">2001</property>
+   </bean>
+
+   <!-- Allow for war local class loaders: in testing
+   <bean name="WarClassLoaderDeployer" class="org.jboss.web.tomcat.service.deployers.WarClassLoaderDeployer">
+      <install bean="MainDeployer" method="addDeployer">
+         <parameter><this/></parameter>
+      </install>
+      <uninstall bean="MainDeployer" method="removeDeployer">
+         <parameter><this/></parameter>
+      </uninstall>
+      <!- - CLASSLOADER_DEPLOYER - 1 - ->
+      <property name="relativeOrder">3999</property>
+      <!- - A flag indicating if the JBoss Loader should be used. This loader
+         uses a unified class loader as the class loader rather than the tomcat
+         specific class loader.
+         The default is false to ensure that wars have isolated class loading
+         for duplicate jars and jsp files.
+      - ->
+      <property name="useJBossWebLoader">false</property>
+      <!- - The list of package prefixes that should not be loaded without
+         delegating to the parent class loader before trying the web app
+         class loader. The packages listed here are those tha are used by
+         the web container implementation and cannot be overriden. The format
+         is a comma separated list of the package names. There cannot be any
+         whitespace between the package prefixes.
+         This setting only applies when UseJBossWebLoader=false.
+         - ->
+      <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
+   </bean>
+  -->
+
+   <!-- The WebMetaData to service mbean deployer -->
+   <bean name="WarDeployer" class="org.jboss.web.tomcat.service.deployers.TomcatDeployer">
+      <install bean="MainDeployer" method="addDeployer">
+         <parameter><this/></parameter>
+      </install>
+      <uninstall bean="MainDeployer" method="removeDeployer">
+         <parameter><this/></parameter>
+      </uninstall>
+
+      <property name="type">war</property>
+      <!-- You can configure a set of authenticators keyed by http-auth method
+      used. This will apply the same set of authenticators across all web
+      applications. You can override the set of authenticators at the web
+      application level by adding <authenticators> element to the respective
+      jboss-web.xml
+      -->      
+      <property name="authenticators">
+         <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String">
+            <entry>
+               <key>BASIC</key>
+               <value>org.apache.catalina.authenticator.BasicAuthenticator</value>
+            </entry>
+            <entry>
+               <key>CLIENT-CERT</key>
+               <value>org.apache.catalina.authenticator.SSLAuthenticator</value>
+            </entry>
+            <entry>
+               <key>DIGEST</key>
+               <value>org.apache.catalina.authenticator.DigestAuthenticator</value>
+            </entry>
+            <entry>
+               <key>FORM</key>
+               <value>org.apache.catalina.authenticator.FormAuthenticator</value>
+            </entry>
+            <entry>
+               <key>NONE</key>
+               <value>org.apache.catalina.authenticator.NonLoginAuthenticator</value>
+            </entry>
+         </map>         
+      </property>
+      
+      <!-- The JAAS security domain to use in the absense of an explicit
+      security-domain specification in the war WEB-INF/jboss-web.xml
+      -->
+      <property name="defaultSecurityDomain">java:/jaas/other</property>
+
+      <!-- Get the flag indicating if the normal Java2 parent first class
+           loading model should be used over the servlet 2.3 web container first
+           model.
+      -->
+      <property name="java2ClassLoadingCompliance">false</property>
+      <!-- A flag indicating if the JBoss Loader should be used. This loader
+           uses a unified class loader as the class loader rather than the tomcat
+           specific class loader.
+           The default is false to ensure that wars have isolated class loading
+           for duplicate jars and jsp files.
+      -->
+      <property name="useJBossWebLoader">false</property>
+      <!-- The list of package prefixes that should not be loaded without
+         delegating to the parent class loader before trying the web app
+         class loader. The packages listed here are those tha are used by
+         the web container implementation and cannot be overriden. The format
+         is a comma separated list of the package names. There cannot be any
+         whitespace between the package prefixes.
+         This setting only applies when UseJBossWebLoader=false.
+      -->
+      <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
+
+      <property name="lenientEjbLink">true</property>
+
+      <!--Flag to delete the Work Dir on Context Destroy -->
+      <property name="deleteWorkDirOnContextDestroy">false</property>
+
+      <!--
+          Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
+          - org.jboss.web.tomcat.service.session.JBossCacheManager
+      -->
+      <property name="managerClass">org.jboss.web.tomcat.service.session.JBossCacheManager</property>
+
+      <!-- The class used as the deployment wrapper -->
+      <property name="deploymentClass">org.jboss.web.tomcat.service.deployers.TomcatDeployment</property>
+
+      <!-- The name of the request property under with the authenticated JAAS
+      Subject is stored on successful authentication. If null or empty then
+      the Subject will not be stored.
+      -->
+      <!--
+      <property name="subjectAttributeName">j_subject</property>
+      -->
+
+      <!-- The SessionIdAlphabet is the set of characters used to create a session Id
+           It must be made up of exactly 65 unique characters
+      <property name="sessionIdAlphabet">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*</property>
+      -->
+
+      <!--
+      *******************************************************
+      ****************** CLUSTERING *************************
+      *******************************************************
+      In order to activate HTTP Session clustering for Tomcat
+      make sure you run JBoss's "all" configuration i.e.
+      "run -c all"
+      (the default configuration doesn't contain clustering)
+
+      Furthermore, you may change SnapshotMode and
+      SnapshotInterval propertys below to indicate when to
+      synchronize changes with the other node(s).
+
+      If you use Apache+mod_jk(2) you will most probably use
+      the AJP1.3 connector below. Thus, if you so wish,
+      you may comment (i.e. deactivate) the HTTP connector
+      as it won't be used anymore.
+
+      *******************************************************
+      *******************************************************
+      *******************************************************
+      -->
+		
+      <!--
+      If you are using clustering, the following two propertys
+      define when the sessions are replicated to the other nodes.
+      The default value, "instant", synchronously replicates changes
+      to the other nodes at the end of requests. In this case, the 
+      "SnapshotInterval" property is not used.
+      The "interval" mode, in association with the "SnapshotInterval"
+      property, indicates that modified sessions will only be replicated
+      every "SnapshotInterval" milliseconds at most.	
+		
+      Note that this property is not in effect if the replication-granularity
+      is set to FIELD. If it is FIELD, it will be per http request (that is, 
+      "instant" mode.)
+      -->
+      <property name="snapshotMode">instant</property> <!-- you may switch to "interval" -->
+      <property name="snapshotInterval">2000</property>
+
+      <!--
+      Whether to use MOD_JK(2) for load balancing with sticky session 
+      combined with JvmRoute. If set to true, it will insert a JvmRouteValve 
+      to intercept every request and replace the JvmRoute if it detects a 
+      failover. In addition, you will need to set the JvmRoute inside
+      Tomcat, e.g.,
+        Engine name="jboss.web" jmvRoute="Node1" defaultHost="localhost" 
+      in server.xml.
+
+      For clustering purpose only.
+      -->
+      <property name="useJK">false</property>
+
+      <property name="domain">jboss.web</property>
+		<property name="securityManagerService">
+			<inject bean="jboss.security:service=JaasSecurityManager" />
+	   </property>
+      
+      <depends>jboss:service=TransactionManager</depends>
+      <depends>SecurityDeployer</depends>
+
+      <!-- Only needed if the org.jboss.web.tomcat.service.jca.CachedConnectionValve
+TODO: injection
+      is enabled in the tomcat server.xml file.
+      <depends>jboss.jca:service=CachedConnectionManager</depends>
+      -->
+   </bean>
+
+</deployment>

Modified: trunk/tomcat/src/resources/web.xml
===================================================================
--- trunk/tomcat/src/resources/web.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/web.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -30,8 +30,7 @@
          org.jboss.web.tomcat.filters.ReplyHeaderFilter</filter-class>
       <init-param>
          <param-name>X-Powered-By</param-name>
-         <param-value>Servlet 2.4;
-            JBoss-5.0.0.Alpha (build: CVSTag=HEAD date=200606261952)/Tomcat-5.5</param-value>
+         <param-value>Servlet 2.5; JBoss-5.0/JBossWeb-2.0/Tomcat-6.0</param-value>
       </init-param>
    </filter>
 
@@ -258,7 +257,7 @@
       <init-param>
          <param-name>engineOptionsClass</param-name>
          <param-value>
-            org.jboss.web.tomcat.tc6.jasper.JspServletOptions</param-value>
+            org.jboss.web.tomcat.service.jasper.JspServletOptions</param-value>
       </init-param>
       <!-- Specify the jars relative to the jbossweb-tomcat6.sar that should
         be scanned for common tag lib descriptors to include in every war

Modified: trunk/tomcat/src/resources/webserver-xmbean.xml
===================================================================
--- trunk/tomcat/src/resources/webserver-xmbean.xml	2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/webserver-xmbean.xml	2007-03-16 02:23:53 UTC (rev 61355)
@@ -15,7 +15,7 @@
        <interceptor code="org.jboss.mx.interceptor.DynamicInterceptor"/>
      </interceptors>
    </descriptors>
-   <class>org.jboss.web.tomcat.tc6.Tomcat6</class>
+   <class>org.jboss.web.tomcat.service.deployers.TomcatDeployer</class>
    
   <!-- Tomcat6MBean attributes -->
 




More information about the jboss-cvs-commits mailing list