[jboss-cvs] JBossAS SVN: r77703 - in projects/cluster/ha-server-cache-jbc/tags: 1.0.0.CR1 and 1 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Sat Aug 30 23:27:17 EDT 2008


Author: bstansberry at jboss.com
Date: 2008-08-30 23:27:17 -0400 (Sat, 30 Aug 2008)
New Revision: 77703

Added:
   projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/
   projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/.classpath
   projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/pom.xml
   projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/src/main/java/org/jboss/web/tomcat/service/sso/jbc/JBossCacheSSOClusterManager.java
Removed:
   projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/.classpath
   projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/pom.xml
   projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/src/main/java/org/jboss/web/tomcat/service/sso/jbc/JBossCacheSSOClusterManager.java
Log:
[maven-release-plugin]  copy for tag 1.0.0.CR1

Copied: projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1 (from rev 77694, projects/cluster/ha-server-cache-jbc/trunk)

Deleted: projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/.classpath
===================================================================
--- projects/cluster/ha-server-cache-jbc/trunk/.classpath	2008-08-31 03:09:12 UTC (rev 77694)
+++ projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/.classpath	2008-08-31 03:27:17 UTC (rev 77703)
@@ -1,26 +0,0 @@
-<classpath>
-  <classpathentry kind="src" path="src/main/java"/>
-  <classpathentry kind="output" path="target/classes"/>
-  <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-  <classpathentry kind="var" path="M2_REPO/org/apache/ant/ant/1.7.0/ant-1.7.0.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/apache/ant/ant-launcher/1.7.0/ant-launcher-1.7.0.jar"/>
-  <classpathentry kind="var" path="M2_REPO/javassist/javassist/3.7.1.GA/javassist-3.7.1.GA.jar" sourcepath="M2_REPO/javassist/javassist/3.7.1.GA/javassist-3.7.1.GA-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/aop/jboss-aop/2.0.0.CR11/jboss-aop-2.0.0.CR11.jar" sourcepath="M2_REPO/org/jboss/aop/jboss-aop/2.0.0.CR11/jboss-aop-2.0.0.CR11-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/jboss-common-core/2.2.8.GA/jboss-common-core-2.2.8.GA.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/cluster/jboss-ha-client/1.1.0.CR4/jboss-ha-client-1.1.0.CR4.jar" sourcepath="M2_REPO/org/jboss/cluster/jboss-ha-client/1.1.0.CR4/jboss-ha-client-1.1.0.CR4-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/cluster/jboss-ha-server-api/1.1.0-SNAPSHOT/jboss-ha-server-api-1.1.0-SNAPSHOT.jar" sourcepath="M2_REPO/org/jboss/cluster/jboss-ha-server-api/1.1.0-SNAPSHOT/jboss-ha-server-api-1.1.0-SNAPSHOT-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/cluster/jboss-ha-server-cache-spi/1.1.0-SNAPSHOT/jboss-ha-server-cache-spi-1.1.0-SNAPSHOT.jar" sourcepath="M2_REPO/org/jboss/cluster/jboss-ha-server-cache-spi/1.1.0-SNAPSHOT/jboss-ha-server-cache-spi-1.1.0-SNAPSHOT-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/logging/jboss-logging-spi/2.0.5.GA/jboss-logging-spi-2.0.5.GA.jar" sourcepath="M2_REPO/org/jboss/logging/jboss-logging-spi/2.0.5.GA/jboss-logging-spi-2.0.5.GA-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/jboss-mdr/2.0.0.Beta15/jboss-mdr-2.0.0.Beta15.jar" sourcepath="M2_REPO/org/jboss/jboss-mdr/2.0.0.Beta15/jboss-mdr-2.0.0.Beta15-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/jboss-reflect/2.0.0.Beta12/jboss-reflect-2.0.0.Beta12.jar" sourcepath="M2_REPO/org/jboss/jboss-reflect/2.0.0.Beta12/jboss-reflect-2.0.0.Beta12-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/remoting/jboss-remoting/2.4.0.CR2/jboss-remoting-2.4.0.CR2.jar" sourcepath="M2_REPO/org/jboss/remoting/jboss-remoting/2.4.0.CR2/jboss-remoting-2.4.0.CR2-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/aspects/jboss-remoting-aspects/1.0.1.CR2/jboss-remoting-aspects-1.0.1.CR2.jar"/>
-  <classpathentry kind="var" path="M2_REPO/jboss/jboss-serialization/1.0.3.GA/jboss-serialization-1.0.3.GA.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/cache/jbosscache-core/2.2.0.GA/jbosscache-core-2.2.0.GA.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/cache/jbosscache-pojo/2.2.0.CR5/jbosscache-pojo-2.2.0.CR5.jar"/>
-  <classpathentry kind="var" path="M2_REPO/jgroups/jgroups/2.6.3.GA/jgroups-2.6.3.GA.jar"/>
-  <classpathentry kind="var" path="M2_REPO/org/jboss/naming/jnp-client/5.0.0.CR2/jnp-client-5.0.0.CR2.jar" sourcepath="M2_REPO/org/jboss/naming/jnp-client/5.0.0.CR2/jnp-client-5.0.0.CR2-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/javax/transaction/jta/1.1/jta-1.1.jar"/>
-  <classpathentry kind="var" path="M2_REPO/qdox/qdox/1.6/qdox-1.6.jar" sourcepath="M2_REPO/qdox/qdox/1.6/qdox-1.6-sources.jar"/>
-  <classpathentry kind="var" path="M2_REPO/trove/trove/2.1.1/trove-2.1.1.jar"/>
-</classpath>
\ No newline at end of file

Copied: projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/.classpath (from rev 77701, projects/cluster/ha-server-cache-jbc/trunk/.classpath)
===================================================================
--- projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/.classpath	                        (rev 0)
+++ projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/.classpath	2008-08-31 03:27:17 UTC (rev 77703)
@@ -0,0 +1,30 @@
+<classpath>
+  <classpathentry kind="src" path="src/main/java"/>
+  <classpathentry kind="output" path="target/classes"/>
+  <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+  <classpathentry kind="var" path="M2_REPO/org/apache/ant/ant/1.7.0/ant-1.7.0.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/apache/ant/ant-launcher/1.7.0/ant-launcher-1.7.0.jar"/>
+  <classpathentry kind="var" path="M2_REPO/javassist/javassist/3.7.1.GA/javassist-3.7.1.GA.jar" sourcepath="M2_REPO/javassist/javassist/3.7.1.GA/javassist-3.7.1.GA-sources.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/aop/jboss-aop/2.0.0.CR11/jboss-aop-2.0.0.CR11.jar" sourcepath="M2_REPO/org/jboss/aop/jboss-aop/2.0.0.CR11/jboss-aop-2.0.0.CR11-sources.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/jboss-common-core/2.2.8.GA/jboss-common-core-2.2.8.GA.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/cluster/jboss-ha-client/1.1.0.GA/jboss-ha-client-1.1.0.GA.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/cluster/jboss-ha-server-api/1.1.0.GA/jboss-ha-server-api-1.1.0.GA.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/cluster/jboss-ha-server-cache-spi/1.0.0.CR1/jboss-ha-server-cache-spi-1.0.0.CR1.jar" sourcepath="M2_REPO/org/jboss/cluster/jboss-ha-server-cache-spi/1.0.0.CR1/jboss-ha-server-cache-spi-1.0.0.CR1-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/home/bes/.m2/repository/org/jboss/cluster/jboss-ha-server-cache-spi/1.0.0.CR1/jboss-ha-server-cache-spi-1.0.0.CR1-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/logging/jboss-logging-spi/2.0.5.GA/jboss-logging-spi-2.0.5.GA.jar" sourcepath="M2_REPO/org/jboss/logging/jboss-logging-spi/2.0.5.GA/jboss-logging-spi-2.0.5.GA-sources.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/jboss-mdr/2.0.0.Beta15/jboss-mdr-2.0.0.Beta15.jar" sourcepath="M2_REPO/org/jboss/jboss-mdr/2.0.0.Beta15/jboss-mdr-2.0.0.Beta15-sources.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/jboss-reflect/2.0.0.Beta12/jboss-reflect-2.0.0.Beta12.jar" sourcepath="M2_REPO/org/jboss/jboss-reflect/2.0.0.Beta12/jboss-reflect-2.0.0.Beta12-sources.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/remoting/jboss-remoting/2.4.0.CR2/jboss-remoting-2.4.0.CR2.jar" sourcepath="M2_REPO/org/jboss/remoting/jboss-remoting/2.4.0.CR2/jboss-remoting-2.4.0.CR2-sources.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/aspects/jboss-remoting-aspects/1.0.1.CR2/jboss-remoting-aspects-1.0.1.CR2.jar"/>
+  <classpathentry kind="var" path="M2_REPO/jboss/jboss-serialization/1.0.3.GA/jboss-serialization-1.0.3.GA.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/cache/jbosscache-core/2.2.0.GA/jbosscache-core-2.2.0.GA.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/cache/jbosscache-pojo/2.2.0.CR5/jbosscache-pojo-2.2.0.CR5.jar"/>
+  <classpathentry kind="var" path="M2_REPO/jgroups/jgroups/2.6.3.GA/jgroups-2.6.3.GA.jar"/>
+  <classpathentry kind="var" path="M2_REPO/org/jboss/naming/jnp-client/5.0.0.CR2/jnp-client-5.0.0.CR2.jar" sourcepath="M2_REPO/org/jboss/naming/jnp-client/5.0.0.CR2/jnp-client-5.0.0.CR2-sources.jar"/>
+  <classpathentry kind="var" path="M2_REPO/javax/transaction/jta/1.1/jta-1.1.jar"/>
+  <classpathentry kind="var" path="M2_REPO/qdox/qdox/1.6/qdox-1.6.jar" sourcepath="M2_REPO/qdox/qdox/1.6/qdox-1.6-sources.jar"/>
+  <classpathentry kind="var" path="M2_REPO/trove/trove/2.1.1/trove-2.1.1.jar"/>
+</classpath>
\ No newline at end of file

Deleted: projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/pom.xml
===================================================================
--- projects/cluster/ha-server-cache-jbc/trunk/pom.xml	2008-08-31 03:09:12 UTC (rev 77694)
+++ projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/pom.xml	2008-08-31 03:27:17 UTC (rev 77703)
@@ -1,166 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <parent>
-    <groupId>org.jboss.cluster</groupId>
-    <artifactId>jboss-cluster</artifactId>
-    <version>1.1.0.GA</version>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.jboss.cluster</groupId>
-  <artifactId>jboss-ha-server-cache-jbc</artifactId>
-  <packaging>jar</packaging>
-  <version>1.0.0-SNAPSHOT</version>
-  <name>JBoss AS - JBoss Cache Integration Implementation</name>
-  <url>http://www.jboss.org</url>
-  <description>A JBoss Cache-based implementation of the SPIs defined in ha-server-cache-spi</description>
-  <licenses>
-   <license>
-      <name>lgpl</name>
-      <url>http://repository.jboss.com/licenses/lgpl.txt</url>
-   </license>
-  </licenses>
-  <organization>
-    <name>JBoss, a division of Red Hat, Inc.</name>
-    <url>http://www.jboss.org</url>
-  </organization>
-  
-  <scm>
-    <connection>scm:svn:https://svn.jboss.org/repos/jbossas/projects/cluster/ha-server-cache-jbc/tags/</connection>
-  </scm>
-  
-  <properties>
-    <version.jboss.ha.server.cache.spi>1.0.0-SNAPSHOT</version.jboss.ha.server.cache.spi>
-    <version.jboss.ha.server.api>1.1.0.GA</version.jboss.ha.server.api>
-    <version.jboss.common.core>2.2.8.GA</version.jboss.common.core>
-    <version.jboss.logging.spi>2.0.5.GA</version.jboss.logging.spi>
-    <version.jboss.cache>2.2.0.GA</version.jboss.cache>
-    <!-- This should be [2.6.2,3.0.0) but maven complains about it -->
-    <version.jgroups>2.6.3.GA</version.jgroups>
-    <version.jboss.cache.pojo>2.2.0.CR5</version.jboss.cache.pojo>
-    <version.org.jboss.naming>5.0.0.CR2</version.org.jboss.naming>
-    <version.junit>3.8.1</version.junit>
-  </properties>
-  
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>maven-release-plugin</artifactId>
-        <configuration>
-          <!-- The tagBase property is needed during the release process so that the maven release plugin
-            will create the release tag in the appropriate location. -->
-          <tagBase>https://svn.jboss.org/repos/jbossas/projects/cluster/ha-server-cache-jbc/tags</tagBase>
-        </configuration>
-      </plugin>     
-    </plugins>
-  </build>  
-  
-  <dependencies> 
-    
-    <!-- Global dependencies -->
-    <dependency>
-      <groupId>org.jboss.cluster</groupId>
-      <artifactId>jboss-ha-server-api</artifactId>
-      <version>${version.jboss.ha.server.api}</version>
-    </dependency>
-    
-    <dependency>
-      <groupId>org.jboss.cluster</groupId>
-      <artifactId>jboss-ha-server-cache-spi</artifactId>
-      <version>${version.jboss.ha.server.cache.spi}</version>
-    </dependency>
-    
-    <dependency>
-      <groupId>org.jboss</groupId>
-      <artifactId>jboss-common-core</artifactId>
-      <version>${version.jboss.common.core}</version>
-    </dependency>
-    
-    <dependency>
-      <groupId>org.jboss.logging</groupId>
-      <artifactId>jboss-logging-spi</artifactId>
-      <version>${version.jboss.logging.spi}</version>
-    </dependency> 
-      
-    <dependency>
-      <groupId>org.jboss.cache</groupId>
-      <artifactId>jbosscache-core</artifactId>
-      <version>${version.jboss.cache}</version>      
-      <optional>true</optional>
-      <exclusions>
-        <exclusion>
-          <groupId>jgroups</groupId>
-          <artifactId>jgroups</artifactId>
-        </exclusion>
-        <exclusion>
-          <groupId>org.jboss</groupId>
-          <artifactId>jboss-common-core</artifactId>
-        </exclusion>
-        <exclusion>
-          <groupId>commons-logging</groupId>
-          <artifactId>commons-logging</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    
-    <dependency>
-      <groupId>org.jboss.cache</groupId>
-      <artifactId>jbosscache-pojo</artifactId>
-      <version>${version.jboss.cache.pojo}</version>      
-      <optional>true</optional>
-      <exclusions>
-        <exclusion>
-          <groupId>org.jboss.cache</groupId>
-          <artifactId>jbosscache-core</artifactId>
-        </exclusion>      
-        <exclusion>
-          <groupId>org.jboss.aop</groupId>
-          <artifactId>jboss-aop</artifactId>
-        </exclusion>      
-        <exclusion>
-          <groupId>commons-logging</groupId>
-          <artifactId>commons-logging</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    
-    <dependency>
-      <groupId>jgroups</groupId>
-      <artifactId>jgroups</artifactId>
-      <version>${version.jgroups}</version>      
-      <optional>true</optional>
-      <exclusions>
-        <exclusion>
-          <groupId>commons-logging</groupId>
-          <artifactId>commons-logging</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-
-    <!-- For the JTA 1.1 API; consuming projects can safely
-         exclude this and replace with any valid source of this API -->
-    <dependency>
-       <groupId>javax.transaction</groupId>
-       <artifactId>jta</artifactId>
-       <version>1.1</version>
-    </dependency>
-    
-    <dependency>
-      <groupId>org.jboss.naming</groupId>
-      <artifactId>jnp-client</artifactId>
-      <version>${version.org.jboss.naming}</version>
-    </dependency>
-    
-    <!-- Test dependencies -->
-    
-    <!-- 
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>${version.junit}</version>
-      <scope>test</scope>
-    </dependency>
-    -->
-    
-  </dependencies>  
-
-</project>
-

Copied: projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/pom.xml (from rev 77702, projects/cluster/ha-server-cache-jbc/trunk/pom.xml)
===================================================================
--- projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/pom.xml	                        (rev 0)
+++ projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/pom.xml	2008-08-31 03:27:17 UTC (rev 77703)
@@ -0,0 +1,166 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.jboss.cluster</groupId>
+    <artifactId>jboss-cluster</artifactId>
+    <version>1.1.0.GA</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.jboss.cluster</groupId>
+  <artifactId>jboss-ha-server-cache-jbc</artifactId>
+  <packaging>jar</packaging>
+  <version>1.0.0.CR1</version>
+  <name>JBoss AS - JBoss Cache Integration Implementation</name>
+  <url>http://www.jboss.org</url>
+  <description>A JBoss Cache-based implementation of the SPIs defined in ha-server-cache-spi</description>
+  <licenses>
+   <license>
+      <name>lgpl</name>
+      <url>http://repository.jboss.com/licenses/lgpl.txt</url>
+   </license>
+  </licenses>
+  <organization>
+    <name>JBoss, a division of Red Hat, Inc.</name>
+    <url>http://www.jboss.org</url>
+  </organization>
+  
+  <scm>
+    <connection>scm:svn:https://svn.jboss.org/repos/jbossas/projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1</connection>
+  </scm>
+  
+  <properties>
+    <version.jboss.ha.server.cache.spi>1.0.0.CR1</version.jboss.ha.server.cache.spi>
+    <version.jboss.ha.server.api>1.1.0.GA</version.jboss.ha.server.api>
+    <version.jboss.common.core>2.2.8.GA</version.jboss.common.core>
+    <version.jboss.logging.spi>2.0.5.GA</version.jboss.logging.spi>
+    <version.jboss.cache>2.2.0.GA</version.jboss.cache>
+    <!-- This should be [2.6.2,3.0.0) but maven complains about it -->
+    <version.jgroups>2.6.3.GA</version.jgroups>
+    <version.jboss.cache.pojo>2.2.0.CR5</version.jboss.cache.pojo>
+    <version.org.jboss.naming>5.0.0.CR2</version.org.jboss.naming>
+    <version.junit>3.8.1</version.junit>
+  </properties>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-release-plugin</artifactId>
+        <configuration>
+          <!-- The tagBase property is needed during the release process so that the maven release plugin
+            will create the release tag in the appropriate location. -->
+          <tagBase>https://svn.jboss.org/repos/jbossas/projects/cluster/ha-server-cache-jbc/tags</tagBase>
+        </configuration>
+      </plugin>     
+    </plugins>
+  </build>  
+  
+  <dependencies> 
+    
+    <!-- Global dependencies -->
+    <dependency>
+      <groupId>org.jboss.cluster</groupId>
+      <artifactId>jboss-ha-server-api</artifactId>
+      <version>${version.jboss.ha.server.api}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.jboss.cluster</groupId>
+      <artifactId>jboss-ha-server-cache-spi</artifactId>
+      <version>${version.jboss.ha.server.cache.spi}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.jboss</groupId>
+      <artifactId>jboss-common-core</artifactId>
+      <version>${version.jboss.common.core}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.jboss.logging</groupId>
+      <artifactId>jboss-logging-spi</artifactId>
+      <version>${version.jboss.logging.spi}</version>
+    </dependency> 
+      
+    <dependency>
+      <groupId>org.jboss.cache</groupId>
+      <artifactId>jbosscache-core</artifactId>
+      <version>${version.jboss.cache}</version>      
+      <optional>true</optional>
+      <exclusions>
+        <exclusion>
+          <groupId>jgroups</groupId>
+          <artifactId>jgroups</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.jboss</groupId>
+          <artifactId>jboss-common-core</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>commons-logging</groupId>
+          <artifactId>commons-logging</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.jboss.cache</groupId>
+      <artifactId>jbosscache-pojo</artifactId>
+      <version>${version.jboss.cache.pojo}</version>      
+      <optional>true</optional>
+      <exclusions>
+        <exclusion>
+          <groupId>org.jboss.cache</groupId>
+          <artifactId>jbosscache-core</artifactId>
+        </exclusion>      
+        <exclusion>
+          <groupId>org.jboss.aop</groupId>
+          <artifactId>jboss-aop</artifactId>
+        </exclusion>      
+        <exclusion>
+          <groupId>commons-logging</groupId>
+          <artifactId>commons-logging</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    
+    <dependency>
+      <groupId>jgroups</groupId>
+      <artifactId>jgroups</artifactId>
+      <version>${version.jgroups}</version>      
+      <optional>true</optional>
+      <exclusions>
+        <exclusion>
+          <groupId>commons-logging</groupId>
+          <artifactId>commons-logging</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <!-- For the JTA 1.1 API; consuming projects can safely
+         exclude this and replace with any valid source of this API -->
+    <dependency>
+       <groupId>javax.transaction</groupId>
+       <artifactId>jta</artifactId>
+       <version>1.1</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.jboss.naming</groupId>
+      <artifactId>jnp-client</artifactId>
+      <version>${version.org.jboss.naming}</version>
+    </dependency>
+    
+    <!-- Test dependencies -->
+    
+    <!-- 
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${version.junit}</version>
+      <scope>test</scope>
+    </dependency>
+    -->
+    
+  </dependencies>  
+
+</project>
+

Deleted: projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/src/main/java/org/jboss/web/tomcat/service/sso/jbc/JBossCacheSSOClusterManager.java
===================================================================
--- projects/cluster/ha-server-cache-jbc/trunk/src/main/java/org/jboss/web/tomcat/service/sso/jbc/JBossCacheSSOClusterManager.java	2008-08-31 03:09:12 UTC (rev 77694)
+++ projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/src/main/java/org/jboss/web/tomcat/service/sso/jbc/JBossCacheSSOClusterManager.java	2008-08-31 03:27:17 UTC (rev 77703)
@@ -1,1501 +0,0 @@
-/*
-* 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.jbc;
-
-import java.io.Serializable;
-import java.security.AccessController;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.management.MBeanAttributeInfo;
-import javax.management.MBeanInfo;
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-import javax.transaction.Status;
-import javax.transaction.TransactionManager;
-
-import org.jboss.cache.Cache;
-import org.jboss.cache.CacheManager;
-import org.jboss.cache.CacheStatus;
-import org.jboss.cache.Fqn;
-import org.jboss.cache.InvocationContext;
-import org.jboss.cache.Node;
-import org.jboss.cache.Region;
-import org.jboss.cache.RegionNotEmptyException;
-import org.jboss.cache.config.Option;
-import org.jboss.cache.config.Configuration.CacheMode;
-import org.jboss.cache.notifications.annotation.CacheListener;
-import org.jboss.cache.notifications.annotation.NodeModified;
-import org.jboss.cache.notifications.annotation.NodeRemoved;
-import org.jboss.cache.notifications.annotation.ViewChanged;
-import org.jboss.cache.notifications.event.NodeModifiedEvent;
-import org.jboss.cache.notifications.event.NodeRemovedEvent;
-import org.jboss.cache.notifications.event.ViewChangedEvent;
-import org.jboss.cache.pojo.PojoCache;
-import org.jboss.ha.framework.server.CacheManagerLocator;
-import org.jboss.logging.Logger;
-import org.jboss.util.NestedRuntimeException;
-import org.jboss.util.loading.ContextClassLoaderSwitcher;
-import org.jboss.util.threadpool.ThreadPool;
-import org.jboss.web.tomcat.service.sso.spi.FullyQualifiedSessionId;
-import org.jboss.web.tomcat.service.sso.spi.SSOClusterManager;
-import org.jboss.web.tomcat.service.sso.spi.SSOCredentials;
-import org.jboss.web.tomcat.service.sso.spi.SSOLocalManager;
-
-/**
- * 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) $
- */
- at CacheListener
-public final class JBossCacheSSOClusterManager
-   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 threadPoolName property
-    */
-   public static final String DEFAULT_THREAD_POOL_NAME =
-      "jboss.system:service=ThreadPool";
-   
-   /** The default JBoss Cache to use for storing SSO entries */
-   public static final String DEFAULT_CACHE_NAME = "clustered-sso";
-   
-   /** The legacy name of the JBoss Cache to use for storing SSO entries */
-   public static final String LEGACY_CACHE_NAME = "jboss.cache:service=TomcatClusteringCache";
-   
-   // -------------------------------------------------------  Instance Fields
-   
-   /**
-    * SSO id which the thread is currently storing to the cache
-    */
-   private ThreadLocal<String> beingLocallyAdded = new ThreadLocal<String>();
-
-   /**
-    * SSO id which a thread is currently removing from the cache
-    */
-   private ThreadLocal<String> beingLocallyRemoved = new ThreadLocal<String>();
-
-   /**
-    * SSO id which the thread is deregistering due to removal on another node
-    */
-   private ThreadLocal<String> beingRemotelyRemoved = new ThreadLocal<String>();
-
-   /**
-    * String name to use to access the TreeCache
-    */
-   private String cacheName = null;
-
-   /**
-    * ObjectName of the TreeCache if legacy JMX integration is used
-    */
-   private ObjectName cacheObjectName = null;
-   
-   /**
-    * The cache itself.
-    */
-   private Cache cache = null;
-
-   /**
-    * Transaction Manager
-    */
-   private TransactionManager tm = null;
-   
-   private String threadPoolName = DEFAULT_THREAD_POOL_NAME;
-
-   private ThreadPool threadPool;
-
-   /**
-    * 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 SSOLocalManager 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;
-   
-   /** The members of the last view passed to viewChange() */
-   private Set currentView = new HashSet();;
-   
-   /** Mutex lock to ensure only one view change at a time is being processed */
-   private Object cleanupMutex = new Object();
-   
-   // ----------------------------------------------------------  Constructors
-
-   
-   /**
-    * Creates a new JBossCacheSSOClusterManager
-    */
-   public JBossCacheSSOClusterManager()  {}
-
-   
-   /**
-    * Creates a new JBossCacheSSOClusterManager that works with the given
-    * MBeanServer.  This constructor is only intended for use in unit testing.
-    */
-   public JBossCacheSSOClusterManager(MBeanServer server)
-   {
-      this.server = server;
-   }
-   
-   
-   // ------------------------------------------------------------  Properties
-  
-   public String getCacheName()
-   {
-      return cacheName;
-   }
-
-   public void setCacheName(String configName)
-      throws Exception
-   {
-      this.cacheName = configName;
-   }
-   
-   public String getThreadPoolName()
-   {
-      return threadPoolName;
-   }
-
-
-   public void setThreadPoolName(String threadPoolName)
-   {
-      if (started)
-      {
-         log.info("Call to setThreadPoolName() ignored; already started");
-      }
-      else
-      {
-         this.threadPoolName = threadPoolName;
-      }
-   }
-   
-   public boolean isUsingThreadPool()
-   {
-      return threadPool != null;
-   }
-   
-   // -----------------------------------------------------  SSOClusterManager
-
-   /**
-    * Notify the cluster of the addition of a Session to an SSO session.
-    *
-    * @param ssoId   the id of the SSO session
-    * @param sessionId id of the Session that has been added
-    */
-   public void addSession(String ssoId, FullyQualifiedSessionId sessionId)
-   {
-      if (ssoId == null || sessionId == null)
-      {
-         return;
-      }
-
-      if (!checkTreeCacheAvailable())
-      {
-         return;
-      }
-
-      if (log.isTraceEnabled())
-      {
-         log.trace("addSession(): adding Session " + sessionId.getSessionId() +
-            " 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();
-         
-         putInTreeCache(fqn, sessionId, null);
-      }
-      catch (Exception e)
-      {
-         try
-         {
-            if(doTx)
-               tm.setRollbackOnly();
-         }
-         catch (Exception ignored)
-         {
-         }
-         log.error("caught exception adding session " + sessionId.getSessionId() +
-            " 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 SSOLocalManager getSSOLocalManager()
-   {
-      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 localManager a <code>SingleSignOn</code> valve.
-    */
-   public void setSSOLocalManager(SSOLocalManager localManager)
-   {
-      ssoValve = localManager;
-      if (ssoValve != null)
-      {
-         if (server == null)
-         {
-            server = ssoValve.getMBeanServer();
-         }
-         String config = ssoValve.getCacheConfig();
-         if (config != null)
-         {
-            cacheName = config;
-         }
-      }
-   }
-
-
-   /**
-    * 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, false);
-      }
-      catch (Exception e)
-      {
-         log.error("Exception attempting to remove node " +
-            fqn.toString() + " from TreeCache", e);
-      }
-      finally
-      {
-         beingLocallyRemoved.set(null);
-      }
-   }
-
-
-   public SSOCredentials lookup(String ssoId)
-   {
-      if (!checkTreeCacheAvailable())
-      {
-         return null;
-      }
-
-      SSOCredentials credentials = null;
-      // Find the latest credential info from the cluster
-      Fqn fqn = getCredentialsFqn(ssoId);
-      try
-      {         
-         credentials = (SSOCredentials) getFromTreeCache(fqn, KEY);
-      }
-      catch (Exception e)
-      {
-         log.error("caught exception looking up SSOCredentials for SSO id " +
-            ssoId, e);
-      }
-      return credentials;
-   }
-
-
-   /**
-    * 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 sessionId id of the Session that has been removed
-    */
-   public void removeSession(String ssoId, FullyQualifiedSessionId sessionId)
-   {
-      if (ssoId == null || sessionId == 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 " + sessionId.getSessionId() +
-            " 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 keys = getSessionKeys(ssoId);
-         if (keys.contains(sessionId))
-         {
-            if (keys.size() == 1)
-            {
-               // This is our last session locally; remove our node (which,
-               // via nodeRemoved callback also marks the sso empty if it's 
-               // also the last session globally                  
-               removeFromTreeCache(fqn, false);                
-            }
-            else
-            {
-               // Simple removal of one our local sessions
-               removeFromTreeCache(fqn, sessionId);
-            }
-         }
-      }
-      catch (Exception e)
-      {
-         try
-         {
-            if(doTx)
-               tm.setRollbackOnly();
-         }
-         catch (Exception x)
-         {
-         }
-         
-         log.error("caught exception removing session " + sessionId.getSessionId() +
-            " 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
-    */
-   @NodeRemoved
-   public void nodeRemoved(NodeRemovedEvent event)
-   {
-      if (event.isPre())
-         return;
-      
-      Fqn fqn = event.getFqn();
-      String ssoId = getIdFromFqn(fqn);
-      
-      if (ssoId == null)
-         return;
-      
-      if (fqn.size() == 2)
-      {
-         // Entire SSO is being removed; i.e. an invalidation
-         
-         // Ignore messages generated by our own logout activity
-         if (!ssoId.equals(beingLocallyRemoved.get()))
-         {
-            handleRemoteInvalidation(ssoId);
-         }
-      }
-      else if (fqn.size() == 4)
-      {
-         // A peer is gone
-         handlePeerRemoval(ssoId);
-      }
-   }
-
-   /**
-    * If any nodes have been removed from the view, asynchronously scans
-    * all SSOs looking for and removing sessions owned by the removed node.
-    * Notifies the SSO valve if as a result any SSOs no longer have active
-    * sessions.  If the removed node is the one associated with this object,
-    * does nothing.
-    */
-   @ViewChanged
-   public synchronized void viewChange(ViewChangedEvent event)
-   {
-      if (event.isPre())
-         return;
-      
-      log.debug("Received ViewChangedEvent " + event);
-      
-      Set oldMembers = new HashSet(currentView);   
-      synchronized (currentView)
-      {
-         currentView.clear();
-         currentView.addAll(event.getNewView().getMembers());
-         
-         // If we're not in the view, just exit
-         if (localAddress == null || !currentView.contains(localAddress))
-            return;
-         
-         // Remove all the current members from the old set; any left 
-         // are the dead members
-         oldMembers.removeAll(currentView);
-      }
-      
-      if (oldMembers.size() > 0)
-      {
-         log.debug("Members have been removed; will launch cleanup task. Dead members: " + oldMembers);
-         
-         launchSSOCleaner(false);
-      }
-         
-   }
-
-
-   /**
-    * Instantiates a DeadMemberCleaner and assigns a thread
-    * to execute the cleanup task.
-    * @param notifyIfEmpty TODO
-    */
-   private void launchSSOCleaner(boolean notifyIfEmpty)
-   {
-      SSOCleanerTask cleaner = new SSOCleanerTask();
-      cleaner.setCheckForEmpty(notifyIfEmpty);
-      if (threadPool != null)
-      {
-         threadPool.run(cleaner);
-      }
-      else
-      {
-         Thread t = new Thread(cleaner, "ClusteredSSOCleaner");
-         t.setDaemon(true);
-         t.start();
-      }
-   }   
-
-
-   /**
-    * Handles the notification that an entire SSO has been removed remotely
-    * 
-    * @param ssoId id of the removed SSO
-    */
-   private void handleRemoteInvalidation(String ssoId)
-   {
-      beingRemotelyRemoved.set(ssoId);
-
-      try
-      {
-         if (log.isTraceEnabled())
-         {
-            log.trace("received a node removed message for SSO " + ssoId);
-         }
-
-         ssoValve.deregister(ssoId);
-      }
-      finally
-      {
-         beingRemotelyRemoved.set(null);
-      }
-   }
-   
-   /**
-    * Checks whether any peers remain for the given SSO; if not
-    * notifies the valve that the SSO is empty.
-    * 
-    * @param ssoId
-    */
-   private void handlePeerRemoval(String ssoId)
-   {
-      try
-      {
-         Set peers = getSSOPeers(ssoId);
-         if (peers.size() == 0)
-         {
-            ssoValve.notifySSOEmpty(ssoId);
-         }
-      }
-      catch (Exception e)
-      {
-         log.error("Caught exception checking if " +  ssoId + " is empty", e);
-      }
-   }
-
-   /**
-    * 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
-    */
-   @NodeModified
-   public void nodeModified(NodeModifiedEvent event)
-   {
-      if (event.isPre() || event.isOriginLocal())
-         return;
-      
-      Fqn fqn = event.getFqn();
-      String type = getTypeFromFqn(fqn);
-      if (CREDENTIALS.equals(type))
-      {
-         handleCredentialUpdate(getIdFromFqn(fqn), event.getData());
-      }
-      else if (SESSIONS.equals(type))
-      {
-         handleSessionSetChange(fqn);
-      }
-   }
-
-   /**  
-    * @param ssoId the id of the sso
-    * @param nodeData JBC data map assoicated with the update
-    */
-   private void handleCredentialUpdate(String ssoId, Map nodeData)
-   {
-      // Ignore invocations that come as a result of our additions
-      if (ssoId.equals(beingLocallyAdded.get()))
-      {
-         return;
-      }
-
-      if (log.isTraceEnabled())
-      {
-         log.trace("received a credentials modified message for SSO " + ssoId);
-      }
-
-      try
-      {
-         SSOCredentials data = (SSOCredentials) nodeData.get(KEY);
-         if (data != null)
-         {
-            ssoValve.remoteUpdate(ssoId, data);
-         }
-      }
-      catch (Exception e)
-      {
-         log.error("failed to update credentials for SSO " + ssoId, e);
-      }
-   }
-   
-   /**
-    *  
-    * @param fqn an Fqn that points to the SESSIONS node of an SSO or lower
-    */
-   private void handleSessionSetChange(Fqn fqn)
-   {
-      // Ignore anything not for a peer's session node
-      if (fqn.size() != 4)
-         return;
-
-      // Peers remove their entire node when it's empty, so any
-      // other modification means it's not empty      
-      ssoValve.notifySSONotEmpty(getIdFromFqn(fqn));
-   }
-
-   /**
-    * 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 Exception
-   {
-      // Validate and update our current component state
-      if (started)
-      {
-         throw new IllegalStateException("JBossCacheSSOClusterManager already Started");
-      }
-      
-      initThreadPool();
-
-      if (isCacheAvailable(true))
-      {
-         integrateWithCache();
-      }
-      
-      started = true;
-   }
-
-
-   /**
-    * Gracefully terminate the active use of the public methods of this
-    * component.  This method should be the last one called on a given
-    * instance of this component.  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 Exception
-   {
-      // Validate and update our current component state
-      if (!started)
-      {
-         throw new IllegalStateException("JBossCacheSSOClusterManager not Started");
-      }
-      
-      started = false;
-   }
-
-   
-   // -------------------------------------------------------  Public Methods
-
-   /**
-    * Gets the number of sessions associated with the given SSO. The same
-    * session active on more than one node will count twice.
-    */
-   public int getSessionCount(String ssoId) throws Exception
-   {
-      int count = 0;
-      Set peers = getSSOPeers(ssoId);
-      for (Iterator it = peers.iterator(); it.hasNext();)
-      {
-         Set ids = getSessionKeys(ssoId, (Serializable) it.next());
-         count += ids.size();
-      }
-      return count;
-   }
-   
-   // -------------------------------------------------------  Private Methods
-
-   private Object getFromTreeCache(Fqn fqn, Object key) throws Exception
-   {
-      return cache.get(fqn, key);
-   }
-   
-   private Set getSSOIds() throws Exception
-   {
-      Fqn ssoRootFqn = Fqn.fromElements(new Object[] {SSO});
-      Node ssoRoot = cache.getRoot().getChild(ssoRootFqn);
-      return ssoRoot == null ? new HashSet() : ssoRoot.getChildrenNames();
-   }
-   
-   private Set getSSOPeers(String ssoId) throws Exception
-   {
-      Fqn fqn = getSessionRootFqn(ssoId);
-      Node ssoRoot = cache.getRoot().getChild(fqn);
-      return ssoRoot == null ? new HashSet() : ssoRoot.getChildrenNames();
-   }
-
-   private Fqn getCredentialsFqn(String ssoid)
-   {
-      Object[] objs = new Object[]{SSO, ssoid, CREDENTIALS};
-      return Fqn.fromElements(objs);
-   }
-
-   private Fqn getSessionRootFqn(String ssoId)
-   {
-      Object[] objs = new Object[]{SSO, ssoId, SESSIONS };
-      return Fqn.fromElements(objs);      
-   }
-   
-   private Fqn getSessionsFqn(String ssoid)
-   {
-      return getSessionsFqn(ssoid, localAddress);
-   }
-   
-   private Fqn getSessionsFqn(String ssoid, Serializable address)
-   {
-      Object[] objs = new Object[]{SSO, ssoid, SESSIONS, address };
-      return Fqn.fromElements(objs);
-   }
-
-   private Fqn getSingleSignOnFqn(String ssoid)
-   {
-      Object[] objs = new Object[]{SSO, ssoid};
-      return Fqn.fromElements(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;
-   }
-
-   /**
-    * 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;
-   }
-   
-   private Set getSessionKeys(String ssoId)
-   {
-      return getSessionKeys(ssoId, localAddress);
-   }
-   
-   private Set getSessionKeys(String ssoId, Serializable peer)
-   {
-      Fqn fqn = getSessionsFqn(ssoId, peer);
-      Set keys = null;
-      Node sessions = cache.getRoot().getChild(fqn);
-      if (sessions != null)
-      {
-         keys = sessions.getKeys();
-      }
-      else
-      {
-          keys = new HashSet();
-      }
-      return keys;
-   }
-
-   /**
-    * 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.
-    * Confirms that the cache is not configured for buddy replication,
-    * throwing IllegalStateException if it is. Also gets 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");
-      }
-      
-      if (cache.getConfiguration().getBuddyReplicationConfig() != null
-            && cache.getConfiguration().getBuddyReplicationConfig().isEnabled())
-      {
-         throw new IllegalStateException("Underlying cache is configured for " +
-                                         "buddy replication; use of buddy " +
-                                         "replication with ClusteredSingleSignOn " +
-                                         "is not supported");
-      }
-      
-      // 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 if (address != null)
-         localAddress = address.toString();
-      else if (CacheMode.LOCAL == cache.getConfiguration().getCacheMode())
-         localAddress = "LOCAL";
-      else 
-         throw new IllegalStateException("Cannot get local address from cache");
-      
-      
-      log.debug("Local address is " + localAddress);
-      
-      // Get the currentView
-      synchronized (currentView)
-      {
-         currentView.clear();
-         List members = cache.getMembers();
-         if (members != null)
-         {
-            currentView.addAll(members);
-            
-            log.debug("Current view is " + currentView);
-         }
-      }
-   }   
-
-   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("JBossCacheSSOClusterManager.endTransaction(): ", e);
-      }
-   }
-   
-   private MBeanServer getMBeanServer()
-   {
-      if (server == null && ssoValve != null)
-      {
-         server = ssoValve.getMBeanServer();
-      }
-      return server;
-   }
-
-   /**
-    * 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 isCacheAvailable(boolean forceCheck)
-   {
-      if (forceCheck || treeCacheAvailable == false)
-      {
-         boolean available = (cacheName != null);
-         if (available)
-         {       
-            try
-            {
-               CacheManager cm = CacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
-               available = cm.getConfigurationNames().contains(cacheName);
-            }
-            catch (IllegalStateException ise)
-            {
-               log.debug("No CacheManager available");
-               available = false;
-            }
-            
-            if (!available && getMBeanServer() != null && cacheName.indexOf(':') > -1)
-            {
-               // See if there is a legacy JMX binding
-               String onameStr = cacheName;
-               if (DEFAULT_CACHE_NAME.equals(cacheName))
-                  onameStr = LEGACY_CACHE_NAME;
-               try
-               {
-                  ObjectName oname = new ObjectName(onameStr);
-                  Set s = getMBeanServer().queryMBeans(oname, null);
-                  if (s.size() > 0)
-                  {
-                     available = true;
-                     // Save the object name to tell integrateWithCache to use JMX
-                     cacheObjectName = oname;
-                     cacheName = onameStr;
-                  }
-               }
-               catch (Exception e)
-               {
-                  // no jmx
-               }
-            }
-            
-            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 " +
-                            cacheName, e);
-                  available = false;
-               }
-            }
-         }
-         treeCacheAvailable = available;
-      }
-      return treeCacheAvailable;
-   }
-   
-   private boolean checkTreeCacheAvailable()
-   {
-      boolean avail = isCacheAvailable(false);
-      if (!avail)
-         logMissingCacheError();
-      return avail;
-   }
-
-   private void putInTreeCache(Fqn fqn, Object key, Object data) throws Exception
-   {
-      cache.put(fqn, key, data);
-   }
-
-   private void integrateWithCache() throws Exception
-   {
-      if (cache == null)
-      {
-         // We are likely going to cause creation and start of a cache here;
-         // we don't want to leak the TCCL to cache/jgroups threads, so
-         // we switch it to our classloader
-         ContextClassLoaderSwitcher switcher = (ContextClassLoaderSwitcher) AccessController.doPrivileged(ContextClassLoaderSwitcher.INSTANTIATOR);
-         ContextClassLoaderSwitcher.SwitchContext switchContext = switcher.getSwitchContext(getClass().getClassLoader());
-         try
-         {
-            // Determine if our cache is a PojoCache or a plain Cache
-            if (cacheObjectName == null)
-            {
-               CacheManager cm = CacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
-               cache = cm.getCache(cacheName, true);               
-            }
-            else if (server != null)
-            {            
-               // Look in JMX
-               MBeanInfo info = server.getMBeanInfo(cacheObjectName);
-               MBeanAttributeInfo[] attrs = info.getAttributes();
-               for (MBeanAttributeInfo attr : attrs)
-               {
-                  if ("PojoCache".equals(attr.getName()))
-                  {
-                     cache = ((PojoCache) getMBeanServer().getAttribute(cacheObjectName, "PojoCache")).getCache();
-                     break;
-                  }
-                  else if ("Cache".equals(attr.getName()))
-                  {
-                     cache = (Cache) getMBeanServer().getAttribute(cacheObjectName, "Cache");
-                     break;
-                  }
-               }
-            }
-            else
-            {
-               // Shouldn't be possible or isTreeCacheAvailable would return false
-               throw new IllegalStateException("No JBoss Cache available under name " + cacheName);
-            }
-            
-            if (cache.getCacheStatus() != CacheStatus.STARTED)
-               cache.start();
-         }
-         finally
-         {
-            // Restore the TCCL
-            switchContext.reset();
-         }
-         
-         // Ensure we have a transaction manager and a cluster-wide unique address
-         configureFromCache();
-         
-         // If the SSO region is inactive, activate it
-         activateCacheRegion();
-         
-         registerAsCacheListener();
-         
-         // Scan for any SSOs with no entries; mark them for expiration
-         launchSSOCleaner(true);
-         
-         log.debug("Successfully integrated with cache service " + cacheName);
-      }
-   }
-
-
-   /**
-    * 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, boolean localOnly) throws Exception
-   {
-      if (localOnly)
-      {
-         InvocationContext ctx = cache.getInvocationContext();
-         Option option = new Option();
-         option.setCacheModeLocal(true);
-         ctx.setOptionOverrides(option);
-      }
-      cache.removeNode(fqn);
-   }
-
-   private void removeFromTreeCache(Fqn fqn, Object key) throws Exception
-   {
-      cache.remove(fqn, key);
-   }
-
-   /**
-    * 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), KEY, data);
-      }
-      catch (Exception e)
-      {
-         log.error("Exception attempting to add TreeCache nodes for SSO " +
-            ssoId, e);
-      }
-      finally
-      {
-         beingLocallyAdded.set(null);
-      }
-   }
-   
-   private void initThreadPool()
-   {      
-      if (threadPoolName != null && getMBeanServer() != null)
-      {
-         try
-         {
-            ObjectName on = new ObjectName(threadPoolName);
-            threadPool = (ThreadPool) server.getAttribute(on, "Instance");
-            log.debug("Using ThreadPool at " + threadPoolName + " to clean dead members");
-         }
-         catch (Exception e)
-         {
-            log.info("Unable to access ThreadPool at " + threadPoolName + 
-                     " -- will use individual threads for cleanup work");
-            log.debug("Failure to access ThreadPool due to: " + e);
-         }
-      }
-      else
-      {
-         log.debug("No ThreadPool configured -- will use individual threads for cleanup work");         
-      }
-   }
-
-   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
-
-   /**
-    * Runnable that's run when the removal of a node from the cluster has been detected. 
-    * Removes any SessionAddress objects associated with dead members from the
-    * session set of each SSO.  Operates locally only so each node can independently clean
-    * its SSOs without concern about replication lock conflicts.
-    */
-   private class SSOCleanerTask implements Runnable
-   {    
-      boolean checkForEmpty = false;
-      
-      
-      boolean getCheckForEmpty()
-      {
-         return checkForEmpty;
-      }
-
-      void setCheckForEmpty(boolean checkForEmpty)
-      {
-         this.checkForEmpty = checkForEmpty;
-      }
-
-      public void run()
-      {
-         synchronized (cleanupMutex)
-         {
-            try
-            {      
-               // Ensure we have a TransactionManager
-               if (tm == null)
-                  configureFromCache();
-               
-               Set ids = getSSOIds();
-               for (Iterator iter = ids.iterator(); iter.hasNext();)
-               {
-                  cleanSSO((String) iter.next());                  
-               }
-            }
-            catch (Exception e)
-            {
-               log.error("Caught exception cleaning sessions from dead cluster members from SSOs ", e);
-            }
-         }
-      }
-      
-      private void cleanSSO(String ssoId)
-      {
-         boolean doTx = false;
-         try
-         {
-            // Don't start tx if there is already one associated with this thread.
-            if(tm.getTransaction() == null)
-               doTx = true;
-
-            if(doTx)
-               tm.begin();
-            
-            Set peers = getSSOPeers(ssoId);
-            if (peers != null && peers.size() > 0)
-            {
-               for (Iterator iter = peers.iterator(); iter.hasNext(); )
-               {
-                  Serializable peer = (Serializable) iter.next();
-                  boolean alive = true;
-                  synchronized (currentView)
-                  {
-                     alive = currentView.contains(peer);
-                  }
-                  if (!alive)
-                  {
-                     if (log.isTraceEnabled())
-                     {
-                        log.trace("Removing peer " + peer + " from SSO " + ssoId);
-                     }
-                     
-                     Fqn fqn = getSessionsFqn(ssoId, peer);
-                     // Remove the peer node, but local-only
-                     // Each cache is responsible for cleaning itself
-                     removeFromTreeCache(fqn, true);
-                  }
-               }
-            }
-            else if (checkForEmpty)
-            {
-               // SSO has no peers; notify our valve so we can expire it
-               ssoValve.notifySSOEmpty(ssoId);
-            }
-         }
-         catch (Exception e)
-         {
-            try
-            {
-               if(doTx)
-                  tm.setRollbackOnly();
-            }
-            catch (Exception ignored)
-            {
-            }
-            log.error("caught exception cleaning dead members from SSO " + ssoId, e);
-         }
-         finally
-         {
-            if (doTx)
-               endTransaction();
-         }         
-      }
-   }
-
-} // end JBossCacheSSOClusterManager
-

Copied: projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/src/main/java/org/jboss/web/tomcat/service/sso/jbc/JBossCacheSSOClusterManager.java (from rev 77700, projects/cluster/ha-server-cache-jbc/trunk/src/main/java/org/jboss/web/tomcat/service/sso/jbc/JBossCacheSSOClusterManager.java)
===================================================================
--- projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/src/main/java/org/jboss/web/tomcat/service/sso/jbc/JBossCacheSSOClusterManager.java	                        (rev 0)
+++ projects/cluster/ha-server-cache-jbc/tags/1.0.0.CR1/src/main/java/org/jboss/web/tomcat/service/sso/jbc/JBossCacheSSOClusterManager.java	2008-08-31 03:27:17 UTC (rev 77703)
@@ -0,0 +1,1490 @@
+/*
+* 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.jbc;
+
+import java.io.Serializable;
+import java.security.AccessController;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheManager;
+import org.jboss.cache.CacheStatus;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.Node;
+import org.jboss.cache.Region;
+import org.jboss.cache.RegionNotEmptyException;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.config.Configuration.CacheMode;
+import org.jboss.cache.notifications.annotation.CacheListener;
+import org.jboss.cache.notifications.annotation.NodeModified;
+import org.jboss.cache.notifications.annotation.NodeRemoved;
+import org.jboss.cache.notifications.annotation.ViewChanged;
+import org.jboss.cache.notifications.event.NodeModifiedEvent;
+import org.jboss.cache.notifications.event.NodeRemovedEvent;
+import org.jboss.cache.notifications.event.ViewChangedEvent;
+import org.jboss.cache.pojo.PojoCache;
+import org.jboss.ha.framework.server.CacheManagerLocator;
+import org.jboss.logging.Logger;
+import org.jboss.util.NestedRuntimeException;
+import org.jboss.util.loading.ContextClassLoaderSwitcher;
+import org.jboss.util.threadpool.ThreadPool;
+import org.jboss.web.tomcat.service.sso.spi.FullyQualifiedSessionId;
+import org.jboss.web.tomcat.service.sso.spi.SSOClusterManager;
+import org.jboss.web.tomcat.service.sso.spi.SSOCredentials;
+import org.jboss.web.tomcat.service.sso.spi.SSOLocalManager;
+
+/**
+ * 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) $
+ */
+ at CacheListener
+public final class JBossCacheSSOClusterManager
+   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 threadPoolName property
+    */
+   public static final String DEFAULT_THREAD_POOL_NAME =
+      "jboss.system:service=ThreadPool";
+   
+   /** The default JBoss Cache to use for storing SSO entries */
+   public static final String DEFAULT_CACHE_NAME = "clustered-sso";
+   
+   /** The legacy name of the JBoss Cache to use for storing SSO entries */
+   public static final String LEGACY_CACHE_NAME = "jboss.cache:service=TomcatClusteringCache";
+   
+   // -------------------------------------------------------  Instance Fields
+   
+   /**
+    * SSO id which the thread is currently storing to the cache
+    */
+   private ThreadLocal<String> beingLocallyAdded = new ThreadLocal<String>();
+
+   /**
+    * SSO id which a thread is currently removing from the cache
+    */
+   private ThreadLocal<String> beingLocallyRemoved = new ThreadLocal<String>();
+
+   /**
+    * SSO id which the thread is deregistering due to removal on another node
+    */
+   private ThreadLocal<String> beingRemotelyRemoved = new ThreadLocal<String>();
+
+   /**
+    * String name to use to access the TreeCache
+    */
+   private String cacheName = DEFAULT_CACHE_NAME;
+
+   /**
+    * ObjectName of the TreeCache if legacy JMX integration is used
+    */
+   private ObjectName cacheObjectName = null;
+   
+   /**
+    * The cache itself.
+    */
+   private Cache cache = null;
+
+   /**
+    * Transaction Manager
+    */
+   private TransactionManager tm = null;
+   
+   private String threadPoolName = DEFAULT_THREAD_POOL_NAME;
+
+   private ThreadPool threadPool;
+
+   /**
+    * 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 SSOLocalManager 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;
+   
+   /** The members of the last view passed to viewChange() */
+   private Set currentView = new HashSet();;
+   
+   /** Mutex lock to ensure only one view change at a time is being processed */
+   private Object cleanupMutex = new Object();
+   
+   // ----------------------------------------------------------  Constructors
+
+   
+   /**
+    * Creates a new JBossCacheSSOClusterManager
+    */
+   public JBossCacheSSOClusterManager()  {}
+
+   
+   /**
+    * Creates a new JBossCacheSSOClusterManager that works with the given
+    * MBeanServer.  This constructor is only intended for use in unit testing.
+    */
+   public JBossCacheSSOClusterManager(MBeanServer server)
+   {
+      this.server = server;
+   }
+   
+   
+   // ------------------------------------------------------------  Properties
+  
+   public String getCacheName()
+   {
+      return cacheName;
+   }
+   
+   public String getThreadPoolName()
+   {
+      return threadPoolName;
+   }
+   
+   public boolean isUsingThreadPool()
+   {
+      return threadPool != null;
+   }
+   
+   // -----------------------------------------------------  SSOClusterManager
+
+   /**
+    * Notify the cluster of the addition of a Session to an SSO session.
+    *
+    * @param ssoId   the id of the SSO session
+    * @param sessionId id of the Session that has been added
+    */
+   public void addSession(String ssoId, FullyQualifiedSessionId sessionId)
+   {
+      if (ssoId == null || sessionId == null)
+      {
+         return;
+      }
+
+      if (!checkTreeCacheAvailable())
+      {
+         return;
+      }
+
+      if (log.isTraceEnabled())
+      {
+         log.trace("addSession(): adding Session " + sessionId.getSessionId() +
+            " 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();
+         
+         putInTreeCache(fqn, sessionId, null);
+      }
+      catch (Exception e)
+      {
+         try
+         {
+            if(doTx)
+               tm.setRollbackOnly();
+         }
+         catch (Exception ignored)
+         {
+         }
+         log.error("caught exception adding session " + sessionId.getSessionId() +
+            " 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 SSOLocalManager getSSOLocalManager()
+   {
+      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 localManager a <code>SingleSignOn</code> valve.
+    */
+   public void setSSOLocalManager(SSOLocalManager localManager)
+   {
+      ssoValve = localManager;
+      if (ssoValve != null)
+      {
+         if (server == null)
+         {
+            server = ssoValve.getMBeanServer();
+         }
+         String config = ssoValve.getCacheConfig();
+         if (config != null)
+         {
+            cacheName = config;
+         }
+         String poolName = ssoValve.getThreadPoolName();
+         if (poolName != null)
+         {
+            threadPoolName = poolName;
+         }
+      }
+   }
+
+
+   /**
+    * 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, false);
+      }
+      catch (Exception e)
+      {
+         log.error("Exception attempting to remove node " +
+            fqn.toString() + " from TreeCache", e);
+      }
+      finally
+      {
+         beingLocallyRemoved.set(null);
+      }
+   }
+
+
+   public SSOCredentials lookup(String ssoId)
+   {
+      if (!checkTreeCacheAvailable())
+      {
+         return null;
+      }
+
+      SSOCredentials credentials = null;
+      // Find the latest credential info from the cluster
+      Fqn fqn = getCredentialsFqn(ssoId);
+      try
+      {         
+         credentials = (SSOCredentials) getFromTreeCache(fqn, KEY);
+      }
+      catch (Exception e)
+      {
+         log.error("caught exception looking up SSOCredentials for SSO id " +
+            ssoId, e);
+      }
+      return credentials;
+   }
+
+
+   /**
+    * 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 sessionId id of the Session that has been removed
+    */
+   public void removeSession(String ssoId, FullyQualifiedSessionId sessionId)
+   {
+      if (ssoId == null || sessionId == 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 " + sessionId.getSessionId() +
+            " 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 keys = getSessionKeys(ssoId);
+         if (keys.contains(sessionId))
+         {
+            if (keys.size() == 1)
+            {
+               // This is our last session locally; remove our node (which,
+               // via nodeRemoved callback also marks the sso empty if it's 
+               // also the last session globally                  
+               removeFromTreeCache(fqn, false);                
+            }
+            else
+            {
+               // Simple removal of one our local sessions
+               removeFromTreeCache(fqn, sessionId);
+            }
+         }
+      }
+      catch (Exception e)
+      {
+         try
+         {
+            if(doTx)
+               tm.setRollbackOnly();
+         }
+         catch (Exception x)
+         {
+         }
+         
+         log.error("caught exception removing session " + sessionId.getSessionId() +
+            " 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
+    */
+   @NodeRemoved
+   public void nodeRemoved(NodeRemovedEvent event)
+   {
+      if (event.isPre())
+         return;
+      
+      Fqn fqn = event.getFqn();
+      String ssoId = getIdFromFqn(fqn);
+      
+      if (ssoId == null)
+         return;
+      
+      if (fqn.size() == 2)
+      {
+         // Entire SSO is being removed; i.e. an invalidation
+         
+         // Ignore messages generated by our own logout activity
+         if (!ssoId.equals(beingLocallyRemoved.get()))
+         {
+            handleRemoteInvalidation(ssoId);
+         }
+      }
+      else if (fqn.size() == 4)
+      {
+         // A peer is gone
+         handlePeerRemoval(ssoId);
+      }
+   }
+
+   /**
+    * If any nodes have been removed from the view, asynchronously scans
+    * all SSOs looking for and removing sessions owned by the removed node.
+    * Notifies the SSO valve if as a result any SSOs no longer have active
+    * sessions.  If the removed node is the one associated with this object,
+    * does nothing.
+    */
+   @ViewChanged
+   public synchronized void viewChange(ViewChangedEvent event)
+   {
+      if (event.isPre())
+         return;
+      
+      log.debug("Received ViewChangedEvent " + event);
+      
+      Set oldMembers = new HashSet(currentView);   
+      synchronized (currentView)
+      {
+         currentView.clear();
+         currentView.addAll(event.getNewView().getMembers());
+         
+         // If we're not in the view, just exit
+         if (localAddress == null || !currentView.contains(localAddress))
+            return;
+         
+         // Remove all the current members from the old set; any left 
+         // are the dead members
+         oldMembers.removeAll(currentView);
+      }
+      
+      if (oldMembers.size() > 0)
+      {
+         log.debug("Members have been removed; will launch cleanup task. Dead members: " + oldMembers);
+         
+         launchSSOCleaner(false);
+      }
+         
+   }
+
+
+   /**
+    * Instantiates a DeadMemberCleaner and assigns a thread
+    * to execute the cleanup task.
+    * @param notifyIfEmpty TODO
+    */
+   private void launchSSOCleaner(boolean notifyIfEmpty)
+   {
+      SSOCleanerTask cleaner = new SSOCleanerTask();
+      cleaner.setCheckForEmpty(notifyIfEmpty);
+      if (threadPool != null)
+      {
+         threadPool.run(cleaner);
+      }
+      else
+      {
+         Thread t = new Thread(cleaner, "ClusteredSSOCleaner");
+         t.setDaemon(true);
+         t.start();
+      }
+   }   
+
+
+   /**
+    * Handles the notification that an entire SSO has been removed remotely
+    * 
+    * @param ssoId id of the removed SSO
+    */
+   private void handleRemoteInvalidation(String ssoId)
+   {
+      beingRemotelyRemoved.set(ssoId);
+
+      try
+      {
+         if (log.isTraceEnabled())
+         {
+            log.trace("received a node removed message for SSO " + ssoId);
+         }
+
+         ssoValve.deregister(ssoId);
+      }
+      finally
+      {
+         beingRemotelyRemoved.set(null);
+      }
+   }
+   
+   /**
+    * Checks whether any peers remain for the given SSO; if not
+    * notifies the valve that the SSO is empty.
+    * 
+    * @param ssoId
+    */
+   private void handlePeerRemoval(String ssoId)
+   {
+      try
+      {
+         Set peers = getSSOPeers(ssoId);
+         if (peers.size() == 0)
+         {
+            ssoValve.notifySSOEmpty(ssoId);
+         }
+      }
+      catch (Exception e)
+      {
+         log.error("Caught exception checking if " +  ssoId + " is empty", e);
+      }
+   }
+
+   /**
+    * 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
+    */
+   @NodeModified
+   public void nodeModified(NodeModifiedEvent event)
+   {
+      if (event.isPre() || event.isOriginLocal())
+         return;
+      
+      Fqn fqn = event.getFqn();
+      String type = getTypeFromFqn(fqn);
+      if (CREDENTIALS.equals(type))
+      {
+         handleCredentialUpdate(getIdFromFqn(fqn), event.getData());
+      }
+      else if (SESSIONS.equals(type))
+      {
+         handleSessionSetChange(fqn);
+      }
+   }
+
+   /**  
+    * @param ssoId the id of the sso
+    * @param nodeData JBC data map assoicated with the update
+    */
+   private void handleCredentialUpdate(String ssoId, Map nodeData)
+   {
+      // Ignore invocations that come as a result of our additions
+      if (ssoId.equals(beingLocallyAdded.get()))
+      {
+         return;
+      }
+
+      if (log.isTraceEnabled())
+      {
+         log.trace("received a credentials modified message for SSO " + ssoId);
+      }
+
+      try
+      {
+         SSOCredentials data = (SSOCredentials) nodeData.get(KEY);
+         if (data != null)
+         {
+            ssoValve.remoteUpdate(ssoId, data);
+         }
+      }
+      catch (Exception e)
+      {
+         log.error("failed to update credentials for SSO " + ssoId, e);
+      }
+   }
+   
+   /**
+    *  
+    * @param fqn an Fqn that points to the SESSIONS node of an SSO or lower
+    */
+   private void handleSessionSetChange(Fqn fqn)
+   {
+      // Ignore anything not for a peer's session node
+      if (fqn.size() != 4)
+         return;
+
+      // Peers remove their entire node when it's empty, so any
+      // other modification means it's not empty      
+      ssoValve.notifySSONotEmpty(getIdFromFqn(fqn));
+   }
+
+   /**
+    * 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 Exception
+   {
+      // Validate and update our current component state
+      if (started)
+      {
+         throw new IllegalStateException("JBossCacheSSOClusterManager already Started");
+      }
+      
+      initThreadPool();
+
+      if (isCacheAvailable(true))
+      {
+         integrateWithCache();
+      }
+      
+      started = true;
+   }
+
+
+   /**
+    * Gracefully terminate the active use of the public methods of this
+    * component.  This method should be the last one called on a given
+    * instance of this component.  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 Exception
+   {
+      // Validate and update our current component state
+      if (!started)
+      {
+         throw new IllegalStateException("JBossCacheSSOClusterManager not Started");
+      }
+      
+      started = false;
+   }
+
+   
+   // -------------------------------------------------------  Public Methods
+
+   /**
+    * Gets the number of sessions associated with the given SSO. The same
+    * session active on more than one node will count twice.
+    */
+   public int getSessionCount(String ssoId) throws Exception
+   {
+      int count = 0;
+      Set peers = getSSOPeers(ssoId);
+      for (Iterator it = peers.iterator(); it.hasNext();)
+      {
+         Set ids = getSessionKeys(ssoId, (Serializable) it.next());
+         count += ids.size();
+      }
+      return count;
+   }
+   
+   // -------------------------------------------------------  Private Methods
+
+   private Object getFromTreeCache(Fqn fqn, Object key) throws Exception
+   {
+      return cache.get(fqn, key);
+   }
+   
+   private Set getSSOIds() throws Exception
+   {
+      Fqn ssoRootFqn = Fqn.fromElements(new Object[] {SSO});
+      Node ssoRoot = cache.getRoot().getChild(ssoRootFqn);
+      return ssoRoot == null ? new HashSet() : ssoRoot.getChildrenNames();
+   }
+   
+   private Set getSSOPeers(String ssoId) throws Exception
+   {
+      Fqn fqn = getSessionRootFqn(ssoId);
+      Node ssoRoot = cache.getRoot().getChild(fqn);
+      return ssoRoot == null ? new HashSet() : ssoRoot.getChildrenNames();
+   }
+
+   private Fqn getCredentialsFqn(String ssoid)
+   {
+      Object[] objs = new Object[]{SSO, ssoid, CREDENTIALS};
+      return Fqn.fromElements(objs);
+   }
+
+   private Fqn getSessionRootFqn(String ssoId)
+   {
+      Object[] objs = new Object[]{SSO, ssoId, SESSIONS };
+      return Fqn.fromElements(objs);      
+   }
+   
+   private Fqn getSessionsFqn(String ssoid)
+   {
+      return getSessionsFqn(ssoid, localAddress);
+   }
+   
+   private Fqn getSessionsFqn(String ssoid, Serializable address)
+   {
+      Object[] objs = new Object[]{SSO, ssoid, SESSIONS, address };
+      return Fqn.fromElements(objs);
+   }
+
+   private Fqn getSingleSignOnFqn(String ssoid)
+   {
+      Object[] objs = new Object[]{SSO, ssoid};
+      return Fqn.fromElements(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;
+   }
+
+   /**
+    * 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;
+   }
+   
+   private Set getSessionKeys(String ssoId)
+   {
+      return getSessionKeys(ssoId, localAddress);
+   }
+   
+   private Set getSessionKeys(String ssoId, Serializable peer)
+   {
+      Fqn fqn = getSessionsFqn(ssoId, peer);
+      Set keys = null;
+      Node sessions = cache.getRoot().getChild(fqn);
+      if (sessions != null)
+      {
+         keys = sessions.getKeys();
+      }
+      else
+      {
+          keys = new HashSet();
+      }
+      return keys;
+   }
+
+   /**
+    * 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 gets 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");
+      }
+      
+      // We no longer rule out buddy replication, as it can be valid if
+      // all activity for the SSO is meant to pinned to one server (i.e.
+      // only one session, or all sessions share the same session id cookie)
+      /*
+      if (cache.getConfiguration().getBuddyReplicationConfig() != null
+            && cache.getConfiguration().getBuddyReplicationConfig().isEnabled())
+      {
+         throw new IllegalStateException("Underlying cache is configured for " +
+                                         "buddy replication; use of buddy " +
+                                         "replication with ClusteredSingleSignOn " +
+                                         "is not supported");
+      }
+      */
+      
+      // 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 if (address != null)
+         localAddress = address.toString();
+      else if (CacheMode.LOCAL == cache.getConfiguration().getCacheMode())
+         localAddress = "LOCAL";
+      else 
+         throw new IllegalStateException("Cannot get local address from cache");
+      
+      
+      log.debug("Local address is " + localAddress);
+      
+      // Get the currentView
+      synchronized (currentView)
+      {
+         currentView.clear();
+         List members = cache.getMembers();
+         if (members != null)
+         {
+            currentView.addAll(members);
+            
+            log.debug("Current view is " + currentView);
+         }
+      }
+   }   
+
+   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("JBossCacheSSOClusterManager.endTransaction(): ", e);
+      }
+   }
+   
+   private MBeanServer getMBeanServer()
+   {
+      if (server == null && ssoValve != null)
+      {
+         server = ssoValve.getMBeanServer();
+      }
+      return server;
+   }
+
+   /**
+    * 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 isCacheAvailable(boolean forceCheck)
+   {
+      if (forceCheck || treeCacheAvailable == false)
+      {
+         boolean available = (cacheName != null);
+         if (available)
+         {       
+            try
+            {
+               CacheManager cm = CacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
+               available = cm.getConfigurationNames().contains(cacheName);
+            }
+            catch (IllegalStateException ise)
+            {
+               log.debug("No CacheManager available");
+               available = false;
+            }
+            
+            if (!available && getMBeanServer() != null)
+            {
+               // See if there is a legacy JMX binding
+               String onameStr = cacheName;
+               if (DEFAULT_CACHE_NAME.equals(cacheName))
+                  onameStr = LEGACY_CACHE_NAME;
+               try
+               {
+                  ObjectName oname = new ObjectName(onameStr);
+                  Set s = getMBeanServer().queryMBeans(oname, null);
+                  if (s.size() > 0)
+                  {
+                     available = true;
+                     // Save the object name to tell integrateWithCache to use JMX
+                     cacheObjectName = oname;
+                     cacheName = onameStr;
+                  }
+               }
+               catch (Exception e)
+               {
+                  // no jmx
+               }
+            }
+            
+            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 " +
+                            cacheName, e);
+                  available = false;
+               }
+            }
+         }
+         treeCacheAvailable = available;
+      }
+      return treeCacheAvailable;
+   }
+   
+   private boolean checkTreeCacheAvailable()
+   {
+      boolean avail = isCacheAvailable(false);
+      if (!avail)
+         logMissingCacheError();
+      return avail;
+   }
+
+   private void putInTreeCache(Fqn fqn, Object key, Object data) throws Exception
+   {
+      cache.put(fqn, key, data);
+   }
+
+   private void integrateWithCache() throws Exception
+   {
+      if (cache == null)
+      {
+         // We are likely going to cause creation and start of a cache here;
+         // we don't want to leak the TCCL to cache/jgroups threads, so
+         // we switch it to our classloader
+         ContextClassLoaderSwitcher switcher = (ContextClassLoaderSwitcher) AccessController.doPrivileged(ContextClassLoaderSwitcher.INSTANTIATOR);
+         ContextClassLoaderSwitcher.SwitchContext switchContext = switcher.getSwitchContext(getClass().getClassLoader());
+         try
+         {
+            // Determine if our cache is a PojoCache or a plain Cache
+            if (cacheObjectName == null)
+            {
+               CacheManager cm = CacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
+               cache = cm.getCache(cacheName, true);               
+            }
+            else if (server != null)
+            {            
+               // Look in JMX
+               MBeanInfo info = server.getMBeanInfo(cacheObjectName);
+               MBeanAttributeInfo[] attrs = info.getAttributes();
+               for (MBeanAttributeInfo attr : attrs)
+               {
+                  if ("PojoCache".equals(attr.getName()))
+                  {
+                     cache = ((PojoCache) getMBeanServer().getAttribute(cacheObjectName, "PojoCache")).getCache();
+                     break;
+                  }
+                  else if ("Cache".equals(attr.getName()))
+                  {
+                     cache = (Cache) getMBeanServer().getAttribute(cacheObjectName, "Cache");
+                     break;
+                  }
+               }
+            }
+            else
+            {
+               // Shouldn't be possible or isTreeCacheAvailable would return false
+               throw new IllegalStateException("No JBoss Cache available under name " + cacheName);
+            }
+            
+            if (cache.getCacheStatus() != CacheStatus.STARTED)
+               cache.start();
+         }
+         finally
+         {
+            // Restore the TCCL
+            switchContext.reset();
+         }
+         
+         // Ensure we have a transaction manager and a cluster-wide unique address
+         configureFromCache();
+         
+         // If the SSO region is inactive, activate it
+         activateCacheRegion();
+         
+         registerAsCacheListener();
+         
+         // Scan for any SSOs with no entries; mark them for expiration
+         launchSSOCleaner(true);
+         
+         log.debug("Successfully integrated with cache service " + cacheName);
+      }
+   }
+
+
+   /**
+    * 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, boolean localOnly) throws Exception
+   {
+      if (localOnly)
+      {
+         InvocationContext ctx = cache.getInvocationContext();
+         Option option = new Option();
+         option.setCacheModeLocal(true);
+         ctx.setOptionOverrides(option);
+      }
+      cache.removeNode(fqn);
+   }
+
+   private void removeFromTreeCache(Fqn fqn, Object key) throws Exception
+   {
+      cache.remove(fqn, key);
+   }
+
+   /**
+    * 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), KEY, data);
+      }
+      catch (Exception e)
+      {
+         log.error("Exception attempting to add TreeCache nodes for SSO " +
+            ssoId, e);
+      }
+      finally
+      {
+         beingLocallyAdded.set(null);
+      }
+   }
+   
+   private void initThreadPool()
+   {      
+      if (threadPoolName != null && getMBeanServer() != null)
+      {
+         try
+         {
+            ObjectName on = new ObjectName(threadPoolName);
+            threadPool = (ThreadPool) server.getAttribute(on, "Instance");
+            log.debug("Using ThreadPool at " + threadPoolName + " to clean dead members");
+         }
+         catch (Exception e)
+         {
+            log.info("Unable to access ThreadPool at " + threadPoolName + 
+                     " -- will use individual threads for cleanup work");
+            log.debug("Failure to access ThreadPool due to: " + e);
+         }
+      }
+      else
+      {
+         log.debug("No ThreadPool configured -- will use individual threads for cleanup work");         
+      }
+   }
+
+   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
+
+   /**
+    * Runnable that's run when the removal of a node from the cluster has been detected. 
+    * Removes any SessionAddress objects associated with dead members from the
+    * session set of each SSO.  Operates locally only so each node can independently clean
+    * its SSOs without concern about replication lock conflicts.
+    */
+   private class SSOCleanerTask implements Runnable
+   {    
+      boolean checkForEmpty = false;
+      
+      
+      boolean getCheckForEmpty()
+      {
+         return checkForEmpty;
+      }
+
+      void setCheckForEmpty(boolean checkForEmpty)
+      {
+         this.checkForEmpty = checkForEmpty;
+      }
+
+      public void run()
+      {
+         synchronized (cleanupMutex)
+         {
+            try
+            {      
+               // Ensure we have a TransactionManager
+               if (tm == null)
+                  configureFromCache();
+               
+               Set ids = getSSOIds();
+               for (Iterator iter = ids.iterator(); iter.hasNext();)
+               {
+                  cleanSSO((String) iter.next());                  
+               }
+            }
+            catch (Exception e)
+            {
+               log.error("Caught exception cleaning sessions from dead cluster members from SSOs ", e);
+            }
+         }
+      }
+      
+      private void cleanSSO(String ssoId)
+      {
+         boolean doTx = false;
+         try
+         {
+            // Don't start tx if there is already one associated with this thread.
+            if(tm.getTransaction() == null)
+               doTx = true;
+
+            if(doTx)
+               tm.begin();
+            
+            Set peers = getSSOPeers(ssoId);
+            if (peers != null && peers.size() > 0)
+            {
+               for (Iterator iter = peers.iterator(); iter.hasNext(); )
+               {
+                  Serializable peer = (Serializable) iter.next();
+                  boolean alive = true;
+                  synchronized (currentView)
+                  {
+                     alive = currentView.contains(peer);
+                  }
+                  if (!alive)
+                  {
+                     if (log.isTraceEnabled())
+                     {
+                        log.trace("Removing peer " + peer + " from SSO " + ssoId);
+                     }
+                     
+                     Fqn fqn = getSessionsFqn(ssoId, peer);
+                     // Remove the peer node, but local-only
+                     // Each cache is responsible for cleaning itself
+                     removeFromTreeCache(fqn, true);
+                  }
+               }
+            }
+            else if (checkForEmpty)
+            {
+               // SSO has no peers; notify our valve so we can expire it
+               ssoValve.notifySSOEmpty(ssoId);
+            }
+         }
+         catch (Exception e)
+         {
+            try
+            {
+               if(doTx)
+                  tm.setRollbackOnly();
+            }
+            catch (Exception ignored)
+            {
+            }
+            log.error("caught exception cleaning dead members from SSO " + ssoId, e);
+         }
+         finally
+         {
+            if (doTx)
+               endTransaction();
+         }         
+      }
+   }
+
+} // end JBossCacheSSOClusterManager
+




More information about the jboss-cvs-commits mailing list