[jboss-cvs] JBossAS SVN: r59745 - in branches/Branch_4_2/tomcat: src/main/org/jboss/net/ssl and 9 other directories.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Wed Jan 17 21:36:31 EST 2007
Author: remy.maucherat at jboss.com
Date: 2007-01-17 21:36:30 -0500 (Wed, 17 Jan 2007)
New Revision: 59745
Added:
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml
Removed:
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/tc5/
branches/Branch_4_2/tomcat/src/resources/tc5-cluster-service.xml
Modified:
branches/Branch_4_2/tomcat/build.xml
branches/Branch_4_2/tomcat/src/main/org/jboss/net/ssl/JBossSocketFactory.java
branches/Branch_4_2/tomcat/src/resources/META-INF/jboss-aop.xml
branches/Branch_4_2/tomcat/src/resources/jboss-service-all.xml
branches/Branch_4_2/tomcat/src/resources/jboss-service.xml
branches/Branch_4_2/tomcat/src/resources/server.xml
branches/Branch_4_2/tomcat/src/resources/standalone-server.xml
branches/Branch_4_2/tomcat/src/resources/web.xml
branches/Branch_4_2/tomcat/src/resources/webserver-xmbean.xml
branches/Branch_4_2/tomcat/src/webapps/ROOT.war/WEB-INF/web.xml
Log:
- Migrate from Tomcat to JBoss Web 2.
Modified: branches/Branch_4_2/tomcat/build.xml
===================================================================
--- branches/Branch_4_2/tomcat/build.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/build.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -20,7 +20,7 @@
<!-- $Id$ -->
-<project default="main" name="JBoss/Tomcat-5">
+<project default="main" name="JBoss/Web">
<!--
| Include the common Buildmagic elements.
@@ -46,8 +46,8 @@
<target name="configure" unless="configure.disable">
<!-- Module name(s) & version -->
- <property name="module.name" value="tomcat4"/>
- <property name="module.Name" value="JBoss Tomcat4 Plugin"/>
+ <property name="module.name" value="tomcat"/>
+ <property name="module.Name" value="JBoss Web Plugin"/>
<property file="module.version"/>
&libraries;
@@ -59,7 +59,6 @@
<path refid="dom4j.dom4j.classpath"/>
<path refid="oswego.concurrent.classpath"/>
<path refid="jgroups.jgroups.classpath"/>
- <path refid="apache.modeler.classpath"/>
</path>
<!-- ======= -->
@@ -114,21 +113,21 @@
<property name="local.classpath" value=""/>
<!-- The classpath required to build classes. -->
- <path id="javac.tomcat55.classpath">
+ <path id="javac.jboss.web.classpath">
<pathelement path="${classpath}"/>
<pathelement path="${local.classpath}"/>
<path refid="thirdparty.classpath"/>
- <path refid="apache.tomcat55.classpath"/>
+ <path refid="jboss.web.classpath"/>
</path>
<!-- The classpath required to build classes. -->
<path id="javac.classpath">
- <path refid="javac.tomcat55.classpath"/>
+ <path refid="javac.jboss.web.classpath"/>
</path>
<!-- The classpath required to build javadocs. -->
<path id="javadoc.classpath">
- <path refid="javac.tomcat55.classpath"/>
+ <path refid="javac.jboss.web.classpath"/>
</path>
<!-- Packages to include when generating api documentation -->
@@ -163,7 +162,7 @@
failonerror="${javac.fail.onerror}">
<src path="${source.java}"/>
<src path="${source.tests}"/>
- <classpath refid="javac.tomcat55.classpath"/>
+ <classpath refid="javac.jboss.web.classpath"/>
</javac>
<copy todir="${build.classes}">
<fileset dir="${source.java}">
@@ -205,34 +204,28 @@
<call target="compile"/>
<mkdir dir="${build.lib}"/>
- <mkdir dir="${build.deploy}/jbossweb-tomcat55.sar/META-INF"/>
+ <mkdir dir="${build.deploy}/jboss-web.sar/META-INF"/>
- <!-- Build the jbossweb-tomcat55.sar -->
+ <!-- Build the jboss-web.sar -->
<copy file="${build.resources}/jboss-service.xml"
- tofile="${build.deploy}/jbossweb-tomcat55.sar/META-INF/jboss-service.xml"/>
+ tofile="${build.deploy}/jboss-web.sar/META-INF/jboss-service.xml"/>
<copy file="${build.resources}/context.xml"
- todir="${build.deploy}/jbossweb-tomcat55.sar"/>
+ todir="${build.deploy}/jboss-web.sar"/>
<copy file="${build.resources}/web.xml"
- tofile="${build.deploy}/jbossweb-tomcat55.sar/conf/web.xml"/>
+ tofile="${build.deploy}/jboss-web.sar/conf/web.xml"/>
<copy file="${build.resources}/server.xml"
- tofile="${build.deploy}/jbossweb-tomcat55.sar/server.xml"/>
+ tofile="${build.deploy}/jboss-web.sar/server.xml"/>
<!-- TDI 20041108 breaks the build
<copy file="${build.resources}/sso-channel.xml"
- tofile="${build.deploy}/jbossweb-tomcat55.sar/sso-channel.xml" />
+ tofile="${build.deploy}/jboss-web.sar/sso-channel.xml" />
-->
- <copy todir="${build.deploy}/jbossweb-tomcat55.sar">
- <fileset dir="${apache.tomcat55.lib}">
+ <copy todir="${build.deploy}/jboss-web.sar">
+ <fileset dir="${jboss.web.lib}">
<include name="*.jar"/>
</fileset>
</copy>
- <copy todir="${build.deploy}/jbossweb-tomcat55.sar"
- file="${apache.modeler.lib}/commons-modeler.jar" />
-
- <copy todir="${build.deploy}/jbossweb-tomcat55.sar"
- file="${commons.el.lib}/commons-el.jar" />
-
- <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs">
+ <copy todir="${build.deploy}/jboss-web.sar/jsf-libs">
<fileset dir="${apache.myfaces.lib}">
<include name="jstl.jar"/>
<include name="myfaces-api.jar"/>
@@ -240,25 +233,16 @@
</fileset>
</copy>
- <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs"
- file="${apache.collections.lib}/commons-collections.jar" />
- <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs"
- file="${apache.beanutils.lib}/commons-beanutils.jar" />
- <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs"
- file="${apache.digester.lib}/commons-digester.jar" />
- <copy todir="${build.deploy}/jbossweb-tomcat55.sar/jsf-libs"
- file="${apache.lang.lib}/commons-lang.jar" />
-
- <jar jarfile="${build.deploy}/jbossweb-tomcat55.sar/tomcat55-service.jar"
+ <jar jarfile="${build.deploy}/jboss-web.sar/jbossweb-service.jar"
manifest="${build.etc}/default.mf">
<!-- The service classes -->
<fileset dir="${build.classes}">
<exclude name="org/jboss/**/*.properties"/>
- <exclude name="org/jboss/web/tomcat/tc5/session/AopMarker*"/>
+ <exclude name="org/jboss/web/tomcat/service/session/AopMarker*"/>
<include name="org/jboss/net/**"/>
<include name="org/jboss/web/tomcat/filters/**"/>
<include name="org/jboss/web/tomcat/servlets/**"/>
- <include name="org/jboss/web/tomcat/tc5/**"/>
+ <include name="org/jboss/web/tomcat/service/**"/>
<include name="org/jboss/web/tomcat/security/**"/>
<include name="org/jboss/web/tomcat/statistics/**"/>
</fileset>
@@ -267,17 +251,17 @@
<include name="org/jboss/**/*.properties"/>
</fileset>
</jar>
- <copy todir="${build.deploy}/jbossweb-tomcat55.sar">
+ <copy todir="${build.deploy}/jboss-web.sar">
<fileset dir="${source.webapps}">
</fileset>
</copy>
<!-- jar for fine grained aop -->
<!-- package in .aop. This is used for fine-grained replication -->
- <jar destfile="${build.lib}/tc5-cluster.aop">
+ <jar destfile="${build.lib}/jboss-web-cluster.aop">
<fileset dir="${build.classes}">
- <include name="org/jboss/web/tomcat/tc5/session/AopMarker.class"/>
- <include name="org/jboss/web/tomcat/tc5/session/InstanceOfAopMarker.class"/>
+ <include name="org/jboss/web/tomcat/service/session/AopMarker.class"/>
+ <include name="org/jboss/web/tomcat/service/session/InstanceOfAopMarker.class"/>
</fileset>
<fileset dir="${build.resources}">
<include name="META-INF/jboss-aop.xml"/>
@@ -285,11 +269,11 @@
</jar>
<!-- Cluster service sar and aop descriptor used for fine-grained replication -->
- <mkdir dir="${build.deploy}/tc5-cluster.sar/META-INF"/>
- <copy todir="${build.deploy}/tc5-cluster.sar"
- file="${build.lib}/tc5-cluster.aop"/>
- <copy tofile="${build.deploy}/tc5-cluster.sar/META-INF/jboss-service.xml"
- file="${build.resources}/tc5-cluster-service.xml"/>
+ <mkdir dir="${build.deploy}/jboss-web-cluster.sar/META-INF"/>
+ <copy todir="${build.deploy}/jboss-web-cluster.sar"
+ file="${build.lib}/jboss-web-cluster.aop"/>
+ <copy tofile="${build.deploy}/jboss-web-cluster.sar/META-INF/jboss-service.xml"
+ file="${build.resources}/jboss-web-cluster-service.xml"/>
<!-- Update the build marker to allow bypassing -->
<touch file="${build-bypass.marker}"/>
Modified: branches/Branch_4_2/tomcat/src/main/org/jboss/net/ssl/JBossSocketFactory.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/net/ssl/JBossSocketFactory.java 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/net/ssl/JBossSocketFactory.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -32,7 +32,7 @@
import javax.net.ssl.KeyManagerFactory;
import org.jboss.security.SecurityDomain;
-import org.apache.tomcat.util.net.jsse.JSSE14SocketFactory;
+import org.apache.tomcat.util.net.jsse.JSSESocketFactory;
/**
* Extends the tomcat JSSE14SocketFactory to obtain the server key and trust
@@ -41,7 +41,7 @@
*
*/
public class JBossSocketFactory
- extends JSSE14SocketFactory
+ extends JSSESocketFactory
{
private SecurityDomain securityDomain;
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,351 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import javax.management.ObjectName;
+
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+
+/**
+ * The tomcat war deployer configuration passed in from the web container.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public class DeployerConfig
+{
+ /**
+ * The tomcat sar class loader
+ */
+ private ClassLoader serviceClassLoader;
+ /**
+ * The domain used for the tomcat mbeans
+ */
+ private String catalinaDomain = "Catalina";
+
+ /**
+ * The fully qualified name of the class that will be used for session
+ * management if <tt>distributable</tt> is set to true.
+ */
+ private String managerClass = "org.jboss.web.tomcat.service.session.JBossManager";
+
+ /**
+ * JMX Object name of the TreeCache MBean. Used by JBossCacheManager
+ */
+ private String cacheName = "jboss.cache:service=TreeCache";
+
+ /**
+ * The web context class to create
+ */
+ private String contextClassName;
+ /**
+ * The parent class loader first model flag
+ */
+ private boolean java2ClassLoadingCompliance = false;
+ /**
+ * A flag indicating if war archives should be unpacked
+ */
+ private boolean unpackWars = true;
+ /**
+ * If true, ejb-links that don't resolve don't cause an error
+ * (fallback to jndi-name)
+ */
+ private boolean lenientEjbLink = false;
+ /**
+ * The tomcat service JMX object name
+ */
+ private ObjectName serviceName;
+ /**
+ * The catalina debug level
+ */
+ private int debugLevel;
+ /**
+ * A flag indicating if the JBoss UCL should be used
+ */
+ private boolean useJBossWebLoader = true;
+ /**
+ * A flag indicating if the working dir for a war deployment should be
+ * delete when the war is undeployed.
+ */
+ private boolean deleteWorkDirs = true;
+ /**
+ * Which snapshot mode should be used in clustered environment?
+ * Default: instant
+ */
+ private String snapshotMode = "instant"; // instant or interval
+ /**
+ * With IntervalSnapshotManager use this interval (in ms) for snapshotting
+ */
+ private int snapshotInterval = 1000;
+
+ /**
+ * Should the clustering code use caching or not?
+ */
+ private boolean useLocalCache;
+
+ /**
+ * Whether to use MOD_JK(2) for sticky session combined with JvmRoute. If set to true,
+ * it will insert a JvmRouteFilter to intercept every request and replace the JvmRoute
+ * if it detects a failover.
+ */
+ private boolean useJK = false;
+
+ /**
+ * Get the request attribute name under which the JAAS Subject is store
+ */
+ private String subjectAttributeName = null;
+ /**
+ * The default security-domain name to use
+ */
+ private String defaultSecurityDomain;
+ /** Package names that should be ignored for class loading */
+ private String[] filteredPackages;
+
+ /**
+ * Flag indicating whether web-app specific context xmls may set the privileged flag.
+ */
+ private boolean allowSelfPrivilegedWebApps = false;
+ /** The service used to flush authentication cache on session invalidation. */
+ private JaasSecurityManagerServiceMBean secMgrService;
+
+ public ClassLoader getServiceClassLoader()
+ {
+ return serviceClassLoader;
+ }
+
+ public void setServiceClassLoader(ClassLoader serviceClassLoader)
+ {
+ this.serviceClassLoader = serviceClassLoader;
+ }
+
+ public String getManagerClass()
+ {
+ return managerClass;
+ }
+
+ public void setManagerClass(String managerClass)
+ {
+ this.managerClass = managerClass;
+ }
+
+ public String getCacheName()
+ {
+ return cacheName;
+ }
+
+ public void setCacheName(String cacheName)
+ {
+ this.cacheName = cacheName;
+ }
+
+ public String getCatalinaDomain()
+ {
+ return catalinaDomain;
+ }
+
+ public void setCatalinaDomain(String catalinaDomain)
+ {
+ this.catalinaDomain = catalinaDomain;
+ }
+
+ public String getContextClassName()
+ {
+ return contextClassName;
+ }
+
+ public void setContextClassName(String contextClassName)
+ {
+ this.contextClassName = contextClassName;
+ }
+
+ public boolean isJava2ClassLoadingCompliance()
+ {
+ return java2ClassLoadingCompliance;
+ }
+
+ public void setJava2ClassLoadingCompliance(boolean java2ClassLoadingCompliance)
+ {
+ this.java2ClassLoadingCompliance = java2ClassLoadingCompliance;
+ }
+
+ public boolean isUnpackWars()
+ {
+ return unpackWars;
+ }
+
+ public void setUnpackWars(boolean unpackWars)
+ {
+ this.unpackWars = unpackWars;
+ }
+
+ public boolean isLenientEjbLink()
+ {
+ return lenientEjbLink;
+ }
+
+ public void setLenientEjbLink(boolean lenientEjbLink)
+ {
+ this.lenientEjbLink = lenientEjbLink;
+ }
+
+ public ObjectName getServiceName()
+ {
+ return serviceName;
+ }
+
+ public void setServiceName(ObjectName serviceName)
+ {
+ this.serviceName = serviceName;
+ }
+
+ public int getDebugLevel()
+ {
+ return debugLevel;
+ }
+
+ public void setDebugLevel(int debugLevel)
+ {
+ this.debugLevel = debugLevel;
+ }
+
+ public boolean isUseJBossWebLoader()
+ {
+ return useJBossWebLoader;
+ }
+
+ public void setUseJBossWebLoader(boolean useJBossWebLoader)
+ {
+ this.useJBossWebLoader = useJBossWebLoader;
+ }
+
+ public boolean isDeleteWorkDirs()
+ {
+ return deleteWorkDirs;
+ }
+
+ public void setDeleteWorkDirs(boolean deleteWorkDirs)
+ {
+ this.deleteWorkDirs = deleteWorkDirs;
+ }
+
+ public String getSnapshotMode()
+ {
+ return snapshotMode;
+ }
+
+ public void setSnapshotMode(String snapshotMode)
+ {
+ this.snapshotMode = snapshotMode;
+ }
+
+ public int getSnapshotInterval()
+ {
+ return snapshotInterval;
+ }
+
+ public void setSnapshotInterval(int snapshotInterval)
+ {
+ this.snapshotInterval = snapshotInterval;
+ }
+
+ public boolean isUseLocalCache()
+ {
+ return useLocalCache;
+ }
+
+ public void setUseLocalCache(boolean useLocalCache)
+ {
+ this.useLocalCache = useLocalCache;
+ }
+
+ public boolean isUseJK()
+ {
+ return useJK;
+ }
+
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK = useJK;
+ }
+
+ public String getSubjectAttributeName()
+ {
+ return subjectAttributeName;
+ }
+
+ public void setSubjectAttributeName(String subjectAttributeName)
+ {
+ this.subjectAttributeName = subjectAttributeName;
+ }
+
+ /**
+ * Get the default security domain implementation to use if a war
+ * does not declare a security-domain.
+ *
+ * @return jndi name of the security domain binding to use.
+ * @jmx:managed-attribute
+ */
+ public String getDefaultSecurityDomain()
+ {
+ return defaultSecurityDomain;
+ }
+
+ /**
+ * Set the default security domain implementation to use if a war
+ * does not declare a security-domain.
+ *
+ * @param defaultSecurityDomain - jndi name of the security domain binding
+ * to use.
+ * @jmx:managed-attribute
+ */
+ public void setDefaultSecurityDomain(String defaultSecurityDomain)
+ {
+ this.defaultSecurityDomain = defaultSecurityDomain;
+ }
+
+ public boolean isAllowSelfPrivilegedWebApps()
+ {
+ return allowSelfPrivilegedWebApps;
+ }
+
+ public void setAllowSelfPrivilegedWebApps(boolean allowSelfPrivilegedWebApps)
+ {
+ this.allowSelfPrivilegedWebApps = allowSelfPrivilegedWebApps;
+ }
+ public JaasSecurityManagerServiceMBean getSecurityManagerService()
+ {
+ return secMgrService;
+ }
+ public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr)
+ {
+ this.secMgrService = mgr;
+ }
+
+ public String[] getFilteredPackages()
+ {
+ return filteredPackages;
+ }
+ public void setFilteredPackages(String[] filteredPackages)
+ {
+ this.filteredPackages = filteredPackages;
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,729 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.security.jacc.PolicyContext;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Connector;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployment.DeploymentInfo;
+import org.jboss.deployment.SubDeployerExt;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+import org.jboss.system.ServiceControllerMBean;
+import org.jboss.system.server.Server;
+import org.jboss.system.server.ServerImplMBean;
+import org.jboss.web.AbstractWebContainer;
+import org.jboss.web.AbstractWebDeployer;
+import org.jboss.web.tomcat.security.HttpServletRequestPolicyContextHandler;
+import org.jboss.web.tomcat.service.session.SessionIDGenerator;
+
+
+/**
+ * An implementation of the AbstractWebContainer for the Jakarta Tomcat5
+ * servlet container. It has no code dependency on tomcat - only the new JMX
+ * model is used.
+ * <p/>
+ * Tomcat5 is organized as a set of mbeans - just like jboss.
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @author Wonne.Keysers at realsoftware.be
+ * @author Dimitris.Andreadis at jboss.org
+ * @author Anil.Saldhana at jboss.org
+ * @version $Revision: 57206 $
+ * @see org.jboss.web.AbstractWebContainer
+ */
+public class JBossWeb extends AbstractWebContainer
+ implements JBossWebMBean, NotificationListener
+{
+ // Constants -----------------------------------------------------
+ public static final String NAME = "JBossWeb";
+
+ /**
+ * Default value for property <code>cacheName</code>. This name will be used by JBossCache exclusively
+ * for Tomcat clustering, e.g., session and sso replication.
+ */
+ public static final String DEFAULT_CACHE_NAME =
+ "jboss.cache:service=TomcatClusteringCache";
+
+ // XXX We could make this configurable - so it can support other containers
+ // that provide JMX-based deployment.
+ private String contextClassName =
+ "org.apache.catalina.core.StandardContext";
+
+ /**
+ * Configurable map of tomcat authenticators
+ * Keyed in by the http auth method that gets
+ * plugged into the Context Config and then into the StandardContext
+ */
+ private Properties authenticators = null;
+
+ /**
+ * Domain for tomcat5 mbeans
+ */
+ private String catalinaDomain = "Catalina";
+
+ /**
+ * ObjectName of a shared TreeCache used for clustered session replication
+ * and clustered single-sign-on
+ */
+ private String cacheName = DEFAULT_CACHE_NAME;
+
+ /**
+ * The fully qualified name of the class that will be used for session
+ * management if <tt>distributable</tt> is set to true.
+ */
+ protected String managerClass = "org.jboss.web.tomcat.service.session.JBossCacheManager";
+
+ /**
+ * With IntervalSnapshotManager use this interval (in ms)
+ * for snapshotting
+ */
+ private int snapshotInterval = 1000;
+
+ /**
+ * Which snapshot mode should be used in clustered environment?
+ * Default: instant
+ */
+ private String snapshotMode = "instant"; // instant or interval
+
+ /**
+ * Should the clustering code use a local cache for the sessions?
+ */
+ private boolean useLocalCache = true;
+
+ /**
+ * Whether we are using Apache MOD_JK(2) module or not
+ */
+ private boolean useJK = false;
+
+ /**
+ * A flag indicating if the JBoss Loader should be used
+ */
+ private boolean useJBossWebLoader = true;
+
+ /**
+ * JBAS-3358: Work directory shouldn't be deleted on Context Destroy
+ */
+ private boolean deleteWorkDirOnContextDestroy = false;
+
+ /**
+ * JBAS-2283: Provide custom header based auth support
+ */
+ private String httpHeaderForSSOAuth = null;
+ private String sessionCookieForSSOAuth = null;
+
+ /**
+ * The server xml configuration file name
+ */
+ private String serverConfigFile = "server.xml";
+
+ /**
+ * Get the request attribute name under which the JAAS Subject is store
+ */
+ private String subjectAttributeName = null;
+
+ /**
+ * Flag indicating whether web-app specific context xmls may set the privileged flag.
+ */
+ private boolean allowSelfPrivilegedWebApps = false;
+
+ /** The service used to flush authentication cache on session invalidation. */
+ private JaasSecurityManagerServiceMBean secMgrService;
+
+ /** */
+ private String[] filteredPackages;
+
+ /** Hold a proxy reference to myself, used when registering to MainDeployer */
+ private SubDeployerExt thisProxy;
+
+ public String getName()
+ {
+ return NAME;
+ }
+
+
+ public String getManagerClass()
+ {
+ return managerClass;
+ }
+
+ public void setManagerClass(String managerClass)
+ {
+ this.managerClass = managerClass;
+ }
+
+
+ public String getDomain()
+ {
+ return this.catalinaDomain;
+ }
+ public Properties getAuthenticators()
+ {
+ return this.authenticators;
+ }
+
+ public void setAuthenticators(Properties prop)
+ {
+ this.authenticators = prop;
+ log.debug("Passed set of authenticators=" + prop);
+ }
+
+ /**
+ * The most important atteribute - defines the managed domain.
+ * A catalina instance (engine) corresponds to a JMX domain, that's
+ * how we know where to deploy webapps.
+ *
+ * @param catalinaDomain the domain portion of the JMX ObjectNames
+ */
+ public void setDomain(String catalinaDomain)
+ {
+ this.catalinaDomain = catalinaDomain;
+ }
+
+ public void setContextMBeanCode(String className)
+ {
+ this.contextClassName = className;
+ }
+
+ public String getContextMBeanCode()
+ {
+ return contextClassName;
+ }
+
+ /**
+ * Set the snapshot interval in milliseconds for snapshot mode = interval
+ */
+ public void setSnapshotInterval(int interval)
+ {
+ this.snapshotInterval = interval;
+ }
+
+ /**
+ * Get the snapshot interval
+ */
+ public int getSnapshotInterval()
+ {
+ return this.snapshotInterval;
+ }
+
+ /**
+ * Set the snapshot mode. Currently supported: instant or interval
+ */
+ public void setSnapshotMode(String mode)
+ {
+ this.snapshotMode = mode;
+ }
+
+ /**
+ * Get the snapshot mode
+ */
+ public String getSnapshotMode()
+ {
+ return this.snapshotMode;
+ }
+
+ /**
+ * Gets the JMX object name of a shared TreeCache to be used for clustered
+ * single-sign-on.
+ *
+ * @see #DEFAULT_CACHE_NAME
+ * @see org.jboss.web.tomcat.service.sso.TreeCacheSSOClusterManager
+ */
+ public String getCacheName()
+ {
+ return cacheName;
+ }
+
+ /**
+ * Gets the JMX object name of a shared TreeCache to be used for clustered
+ * single-sign-on.
+ * <p/>
+ * <b>NOTE:</b> TreeCache must be deployed before this service.
+ *
+ * @see #DEFAULT_CACHE_NAME
+ * @see org.jboss.web.tomcat.service.sso.TreeCacheSSOClusterManager
+ */
+ public void setCacheName(String cacheName)
+ {
+ this.cacheName = cacheName;
+ }
+
+ public boolean isUseLocalCache()
+ {
+ return useLocalCache;
+ }
+
+ public void setUseLocalCache(boolean useLocalCache)
+ {
+ this.useLocalCache = useLocalCache;
+ }
+
+ public boolean isUseJK()
+ {
+ return useJK;
+ }
+
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK = useJK;
+ }
+
+ public boolean getDeleteWorkDirOnContextDestroy()
+ {
+ return deleteWorkDirOnContextDestroy;
+ }
+
+ public void setDeleteWorkDirOnContextDestroy(boolean deleteFlag)
+ {
+ this.deleteWorkDirOnContextDestroy = deleteFlag;
+ }
+
+ public String getHttpHeaderForSSOAuth()
+ {
+ return httpHeaderForSSOAuth;
+ }
+
+ public void setHttpHeaderForSSOAuth(String httpHeader)
+ {
+ this.httpHeaderForSSOAuth = httpHeader;
+ }
+
+ public String getSessionCookieForSSOAuth()
+ {
+ return sessionCookieForSSOAuth;
+ }
+
+ public void setSessionCookieForSSOAuth(String sessionC)
+ {
+ this.sessionCookieForSSOAuth = sessionC;
+ }
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public void setSessionIdAlphabet(String sessionIdAlphabet)
+ {
+ SessionIDGenerator.getInstance().setSessionIdAlphabet(sessionIdAlphabet);
+ }
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public String getSessionIdAlphabet()
+ {
+ return SessionIDGenerator.getInstance().getSessionIdAlphabet();
+ }
+
+ public boolean getUseJBossWebLoader()
+ {
+ return useJBossWebLoader;
+ }
+
+ public void setUseJBossWebLoader(boolean flag)
+ {
+ this.useJBossWebLoader = flag;
+ }
+
+ public String getConfigFile()
+ {
+ return serverConfigFile;
+ }
+
+ public void setConfigFile(String configFile)
+ {
+ this.serverConfigFile = configFile;
+ }
+
+ public String getSubjectAttributeName()
+ {
+ return this.subjectAttributeName;
+ }
+
+ public void setSubjectAttributeName(String name)
+ {
+ this.subjectAttributeName = name;
+ }
+
+ public boolean isAllowSelfPrivilegedWebApps()
+ {
+ return allowSelfPrivilegedWebApps;
+ }
+
+ public void setAllowSelfPrivilegedWebApps(boolean allowSelfPrivilegedWebApps)
+ {
+ this.allowSelfPrivilegedWebApps = allowSelfPrivilegedWebApps;
+ }
+
+ public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr)
+ {
+ this.secMgrService = mgr;
+ }
+
+ public String[] getFilteredPackages()
+ {
+ return filteredPackages;
+ }
+ public void setFilteredPackages(String[] pkgs)
+ {
+ this.filteredPackages = pkgs;
+ }
+
+ public void startService()
+ throws Exception
+ {
+
+ System.setProperty("catalina.ext.dirs",
+ (System.getProperty("jboss.server.home.dir")
+ + File.separator + "lib"));
+
+ String objectNameS = catalinaDomain + ":type=server";
+ ObjectName objectName = new ObjectName(objectNameS);
+
+ // Set the modeler Registry MBeanServer to the that of the tomcat service
+ Registry.getRegistry().setMBeanServer(server);
+
+ Registry.getRegistry().registerComponent(Class.forName("org.apache.catalina.startup.Catalina").newInstance(),
+ objectName, "org.apache.catalina.startup.Catalina");
+
+ server.setAttribute(objectName, new Attribute
+ ("catalinaHome",
+ System.getProperty("jboss.server.home.dir")));
+ server.setAttribute(objectName, new Attribute
+ ("configFile", serverConfigFile));
+ server.setAttribute(objectName, new Attribute
+ ("useNaming", new Boolean(false)));
+ server.setAttribute(objectName, new Attribute
+ ("useShutdownHook", new Boolean(false)));
+ server.setAttribute(objectName, new Attribute
+ ("await", new Boolean(false)));
+ server.setAttribute(objectName, new Attribute
+ ("redirectStreams", new Boolean(false)));
+
+ server.invoke(objectName, "create", new Object[]{},
+ new String[]{});
+
+ server.invoke(objectName, "start", new Object[]{},
+ new String[]{});
+
+ // Configure any SingleSignOn valves
+
+ ObjectName ssoQuery = new ObjectName("*:type=Valve,*");
+ Iterator iterator = server.queryMBeans(ssoQuery, null).iterator();
+ while (iterator.hasNext())
+ {
+ ObjectName ssoObjectName =
+ ((ObjectInstance) iterator.next()).getObjectName();
+ String name = ssoObjectName.getKeyProperty("name");
+
+ /* Ensure that the SingleSignOn valve requires that each
+ request be reauthenticated to the security mgr. Should not
+ be neccessary now that we cache the principal in the session.
+ if ((name != null) && (name.indexOf("SingleSignOn") >= 0))
+ {
+ log.info("Turning on reauthentication of each request on " +
+ ssoObjectName);
+ server.setAttribute(ssoObjectName, new Attribute
+ ("requireReauthentication", Boolean.TRUE));
+ }
+ */
+
+ // If the valve is a ClusteredSingleSignOn and we have a shared
+ // TreeCache configured, configure the valve to use the shared one
+ if (cacheName != null && "ClusteredSingleSignOn".equals(name))
+ {
+ String tcName = (String) server.getAttribute(ssoObjectName,
+ "treeCacheName");
+ tcName = (tcName != null ? tcName : DEFAULT_CACHE_NAME);
+ ObjectName ssoCacheName = new ObjectName(tcName);
+ // Only override if the valve's cacheName property was not
+ // explicitly set in server.xml to a non-default value
+ if (ssoCacheName.equals(new ObjectName(DEFAULT_CACHE_NAME)))
+ {
+ log.info("Setting the cache name to " + cacheName +
+ " on " + ssoObjectName);
+ server.setAttribute(ssoObjectName,
+ new Attribute("treeCacheName", cacheName));
+ }
+ }
+ }
+
+ // Register the web container JACC PolicyContextHandlers
+ HttpServletRequestPolicyContextHandler handler = new HttpServletRequestPolicyContextHandler();
+ PolicyContext.registerHandler(HttpServletRequestPolicyContextHandler.WEB_REQUEST_KEY,
+ handler, true);
+
+ // The ServiceController used to control web app startup dependencies
+ serviceController = (ServiceControllerMBean)
+ MBeanProxyExt.create(ServiceControllerMBean.class, ServiceControllerMBean.OBJECT_NAME, server);
+
+ // make a proxy to myself, so that calls from the MainDeployer
+ // can go through the MBeanServer, so interceptors can be added
+ thisProxy = (SubDeployerExt)
+ MBeanProxyExt.create(SubDeployerExt.class, super.getServiceName(), super.getServer());
+
+ // Register with the main deployer
+ mainDeployer.addDeployer(thisProxy);
+
+ // If we are hot-deployed *after* the overall server is started
+ // we'll never receive Server.START_NOTIFICATION_TYPE, so check
+ // with the Server and start the connectors immediately, if this is the case.
+ // Otherwise register to receive the server start-up notification.
+ Boolean started = (Boolean)server.getAttribute(ServerImplMBean.OBJECT_NAME, "Started");
+ if (started.booleanValue() == true)
+ {
+ log.debug("Server '" + ServerImplMBean.OBJECT_NAME +
+ "' already started, starting connectors now");
+
+ startConnectors();
+ }
+ else
+ {
+ // Register for notification of the overall server startup
+ log.debug("Server '" + ServerImplMBean.OBJECT_NAME +
+ "' not started, registering for start-up notification");
+
+ server.addNotificationListener(ServerImplMBean.OBJECT_NAME, this, null, null);
+ }
+ }
+
+
+ public void stopService()
+ throws Exception
+ {
+
+ String objectNameS = catalinaDomain + ":type=server";
+ ObjectName objectName = new ObjectName(objectNameS);
+
+ server.invoke(objectName, "stop", new Object[]{},
+ new String[]{});
+
+ server.invoke(objectName, "destroy", new Object[]{},
+ new String[]{});
+
+ server.unregisterMBean(objectName);
+
+ MBeanServer server2 = server;
+
+ // deregister with MainDeployer
+ mainDeployer.removeDeployer(thisProxy);
+
+ // Unregister any remaining jboss.web or Catalina MBeans
+ ObjectName queryObjectName = new ObjectName
+ (catalinaDomain + ":*");
+ Iterator iterator =
+ server2.queryMBeans(queryObjectName, null).iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ ObjectName toRemove = oi.getObjectName();
+ // Exception: Don't unregister the service right now
+ if (!"WebServer".equals(toRemove.getKeyProperty("service")))
+ {
+ if (server2.isRegistered(toRemove))
+ {
+ server2.unregisterMBean(toRemove);
+ }
+ }
+ }
+ queryObjectName = new ObjectName("Catalina:*");
+ iterator = server2.queryMBeans(queryObjectName, null).iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ ObjectName name = oi.getObjectName();
+ server2.unregisterMBean(name);
+ }
+
+ }
+
+ public void startConnectors() throws Exception
+ {
+ ObjectName service = new ObjectName(catalinaDomain + ":type=Service,serviceName=jboss.web");
+ Object[] args = {};
+ String[] sig = {};
+ Connector[] connectors = (Connector[]) server.invoke(service,
+ "findConnectors", args, sig);
+ for (int n = 0; n < connectors.length; n++)
+ {
+ Lifecycle lc = (Lifecycle) connectors[n];
+ lc.start();
+ }
+
+ //There may be a need to start the connectors that are defined in
+ //the multiple services in tomcat server.xml
+ startAllConnectors();
+
+ // Notify listeners that connectors have started processing requests
+ sendNotification(new Notification(TOMCAT_CONNECTORS_STARTED,
+ this, getNextNotificationSequenceNumber()));
+ }
+
+ public void stopConnectors() throws Exception
+ {
+ ObjectName service = new ObjectName(catalinaDomain + ":type=Service,serviceName=jboss.web");
+ Object[] args = {};
+ String[] sig = {};
+ Connector[] connectors = (Connector[]) server.invoke(service,
+ "findConnectors", args, sig);
+ for (int n = 0; n < connectors.length; n++)
+ {
+ Lifecycle lc = (Lifecycle) connectors[n];
+ lc.stop();
+ }
+ //There may be a need to stop the connectors that are defined in
+ //the multiple services in tomcat server.xml
+ stopAllConnectors();
+ }
+
+ public void handleNotification(Notification msg, Object handback)
+ {
+ String type = msg.getType();
+ if (type.equals(Server.START_NOTIFICATION_TYPE))
+ {
+ log.debug("Saw " + type + " notification, starting connectors");
+ try
+ {
+ startConnectors();
+ }
+ catch (Exception e)
+ {
+ log.warn("Failed to startConnectors", e);
+ }
+ }
+ }
+
+ public AbstractWebDeployer getDeployer(DeploymentInfo di) throws Exception
+ {
+ ClassLoader loader = di.ucl;
+ Class deployerClass = loader.loadClass("org.jboss.web.tomcat.service.TomcatDeployer");
+ AbstractWebDeployer deployer = (AbstractWebDeployer) deployerClass.newInstance();
+ DeployerConfig config = new DeployerConfig();
+ config.setDefaultSecurityDomain(this.defaultSecurityDomain);
+ config.setSubjectAttributeName(this.subjectAttributeName);
+ config.setServiceClassLoader(getClass().getClassLoader());
+ config.setManagerClass(this.managerClass);
+ config.setJava2ClassLoadingCompliance(this.java2ClassLoadingCompliance);
+ config.setUnpackWars(this.unpackWars);
+ config.setLenientEjbLink(this.lenientEjbLink);
+ config.setCatalinaDomain(catalinaDomain);
+ config.setContextClassName(contextClassName);
+ config.setServiceName(serviceName);
+ config.setSnapshotInterval(this.snapshotInterval);
+ config.setSnapshotMode(this.snapshotMode);
+ config.setUseLocalCache(this.useLocalCache);
+ config.setUseJK(this.useJK);
+ config.setSubjectAttributeName(this.subjectAttributeName);
+ config.setUseJBossWebLoader(this.useJBossWebLoader);
+ config.setAllowSelfPrivilegedWebApps(this.allowSelfPrivilegedWebApps);
+ config.setSecurityManagerService(this.secMgrService);
+ config.setFilteredPackages(filteredPackages);
+ deployer.setServer(server);
+ deployer.init(config);
+ return deployer;
+ }
+
+ /**
+ * Start all the connectors
+ *
+ * @throws JMException
+ * @throws LifecycleException
+ */
+ private void startAllConnectors() throws JMException, LifecycleException
+ {
+ /**
+ * Not able to query the Catalina server for the services that it has
+ * registered. A usuable solution is to query the MBean server for the
+ * tomcat services.
+ * http://www.jboss.com/index.html?module=bb&op=viewtopic&t=75353
+ */
+ ObjectName oname = new ObjectName("*:type=Service,*");
+ Set services = server.queryMBeans(oname,null);
+ Iterator iter = services.iterator();
+ while(iter.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance)iter.next();
+ ObjectName on = oi.getObjectName();
+ //Ignore jboss.web:*
+ if(this.catalinaDomain.equals(on.getDomain()) )
+ continue;
+ String key = on.getKeyProperty("serviceName");
+ if(key != null)
+ {
+ Connector[] connectors = (Connector[]) server.invoke(on,
+ "findConnectors", new Object[0], new String[0]);
+ for (int n = 0; n < connectors.length; n++)
+ {
+ Lifecycle lc = (Lifecycle) connectors[n];
+ lc.start();
+ }
+ }
+ }
+ }
+
+ /**
+ * Stop all the connectors
+ *
+ * @throws JMException
+ * @throws LifecycleException
+ */
+ private void stopAllConnectors() throws JMException, LifecycleException
+ {
+ ObjectName oname = new ObjectName("*:type=Service,*");
+ Set services = server.queryMBeans(oname,null);
+ Iterator iter = services.iterator();
+ while(iter.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance)iter.next();
+ ObjectName on = oi.getObjectName();
+ //Ignore jboss.web:*
+ if(this.catalinaDomain.equals(on.getDomain()) )
+ continue;
+ String key = on.getKeyProperty("serviceName");
+ if(key != null)
+ {
+ Connector[] connectors = (Connector[]) server.invoke(on,
+ "findConnectors", new Object[0], new String[0]);
+ for (int n = 0; n < connectors.length; n++)
+ {
+ Lifecycle lc = (Lifecycle) connectors[n];
+ lc.stop();
+ }
+ }
+ }
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWeb.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,216 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import org.jboss.web.AbstractWebContainerMBean;
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+
+/**
+ * An implementation of the AbstractWebContainer for the Jakarta Tomcat5
+ * servlet container. It has no code dependency on tomcat - only the new JMX
+ * model is used.
+ * <p/>
+ * Tomcat5 is organized as a set of mbeans - just like jboss.
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @version $Revision: 57206 $
+ * @see AbstractWebContainerMBean
+ */
+public interface JBossWebMBean extends AbstractWebContainerMBean
+{
+ /** JMX notification type to signal after-start connector event */
+ public final String TOMCAT_CONNECTORS_STARTED = "jboss.tomcat.connectors.started";
+
+ /**
+ * @return the jmx domain for the tomcat management mbeans
+ */
+ public String getDomain();
+
+ /**
+ * The most important attribute - defines the managed domain.
+ * A catalina instance (engine) corresponds to a JMX domain, that's
+ * how we know where to deploy webapps.
+ *
+ * @param domainName the jmx domain under which tc registers
+ */
+ public void setDomain(String domainName);
+
+ /**
+ * Set the snapshot mode in a clustered environment
+ */
+ public void setSnapshotMode(String mode);
+
+ /**
+ * Get the snapshot mode in a clustered environment
+ */
+ public String getSnapshotMode();
+
+ /**
+ * Set the snapshot interval in ms for the interval snapshot mode
+ */
+ public void setSnapshotInterval(int interval);
+
+ /**
+ * Get the snapshot interval
+ */
+ public int getSnapshotInterval();
+
+ /**
+ * Get the clustering code cache behaviour
+ */
+ public boolean isUseLocalCache();
+
+ /**
+ * Set the clustering code cache behaviour
+ */
+ public void setUseLocalCache(boolean useLocalCache);
+
+ /**
+ * Get the clustering code failover behaviour whether MOD_JK(2) is used or not.
+ */
+ public boolean isUseJK();
+
+ /**
+ * Set the clustering code failover behaviour whether MOD_JK(2) is used or not.
+ */
+ public void setUseJK(boolean useJK);
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public void setSessionIdAlphabet(String sessionIdAlphabet);
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public String getSessionIdAlphabet();
+
+ /**
+ * Gets the JMX object name of a shared TreeCache to be used for clustered
+ * single-sign-on.
+ *
+ * @see org.jboss.web.tomcat.tc5.sso.TreeCacheSSOClusterManager
+ */
+ public String getCacheName();
+
+ /**
+ * Gets the JMX object name of a shared TreeCache to be used for clustered
+ * single-sign-on.
+ * <p/>
+ * <b>NOTE:</b> TreeCache must be deployed before this service.
+ *
+ * @see org.jboss.web.tomcat.tc5.sso.TreeCacheSSOClusterManager
+ */
+ public void setCacheName(String cacheName);
+
+ /**
+ * Get the JBoss UCL use flag
+ */
+ public boolean getUseJBossWebLoader();
+
+ /**
+ * Set the JBoss UCL use flag
+ */
+ public void setUseJBossWebLoader(boolean flag);
+
+ public String getManagerClass();
+
+ public void setManagerClass(String managerClass);
+
+ /** */
+ public String getContextMBeanCode();
+
+ /** */
+ public void setContextMBeanCode(String className);
+
+ /**
+ * Get the name of the external tomcat server configuration file.
+ *
+ * @return the config file name, server.xml for example
+ */
+ public String getConfigFile();
+
+ /**
+ * Set the name of the external tomcat server configuration file.
+ *
+ * @param configFile - the config file name, server.xml for example
+ */
+ public void setConfigFile(String configFile);
+
+ /**
+ * Get the request attribute name under which the JAAS Subject is store
+ */
+ public String getSubjectAttributeName();
+
+ /**
+ * Set the request attribute name under which the JAAS Subject will be
+ * stored when running with a security mgr that supports JAAS. If this is
+ * empty then the Subject will not be store in the request.
+ *
+ * @param name the HttpServletRequest attribute name to store the Subject
+ */
+ public void setSubjectAttributeName(String name);
+
+ /**
+ * Start all connectors of the Domain + ":type=Service,serviceName=jboss.web"
+ * service.
+ *
+ * @throws Exception
+ */
+ public void startConnectors() throws Exception;
+
+ /**
+ * Stop all connectors of the Domain + ":type=Service,serviceName=jboss.web"
+ * service.
+ *
+ * @throws Exception
+ */
+ public void stopConnectors() throws Exception;
+
+ /**
+ * Get whether web-apps are able to control the privileged flag
+ */
+ public boolean isAllowSelfPrivilegedWebApps();
+
+ /**
+ * Set whether web-apps are able to control the privileged flag
+ */
+ public void setAllowSelfPrivilegedWebApps(boolean flag);
+
+ /** Set the SecurityManagerService binding. This is used to flush any
+ * associated authentication cache on session invalidation.
+ * @param mgr the JaasSecurityManagerServiceMBean
+ */
+ public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr);
+
+ /**
+ *
+ * @return
+ */
+ public String[] getFilteredPackages();
+ /**
+ *
+ * @param pkgs
+ */
+ public void setFilteredPackages(String[] pkgs);
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JBossWebMBean.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,204 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Session;
+import org.jboss.logging.Logger;
+import org.jboss.util.NestedRuntimeException;
+import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+
+/**
+ * Web request filter to specifically handle Tomcat jvmRoute using mod_jk(2)
+ * module. We assume that the session is set by cookie only for now, i.e., no
+ * support of that from URL. Furthermore, the session id has a format of
+ * id.jvmRoute where jvmRoute is used by JK module to determine sticky session
+ * during load balancing.
+ *
+ * @author Ben Wang
+ * @author Marco Antonioni
+ * @version $Revision: 57206 $
+ * @deprecated 4.0.3
+ */
+public class JvmRouteFilter
+ implements Filter
+{
+ protected AbstractJBossManager manager_;
+ protected static Logger log_ = Logger.getLogger(JvmRouteFilter.class);
+
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ if (log_.isDebugEnabled())
+ {
+ ServletContext sc = filterConfig.getServletContext();
+ Enumeration names = sc.getAttributeNames();
+ while (names.hasMoreElements())
+ {
+ String name = (String) names.nextElement();
+ Object value = sc.getAttribute(name);
+ log_.debug("name=" + name + ", value.className: [" + value.getClass().getName() + "] value.toString: [" + value.toString() + "]");
+ }
+ }
+ manager_ = (AbstractJBossManager) filterConfig.getServletContext().getAttribute("AbstractJBossManager");
+ if (manager_ == null)
+ {
+ throw new RuntimeException("JvmRouteFilter.init(): No AbstractJBossManager found for clustering support.");
+ }
+
+ if (log_.isDebugEnabled())
+ log_.debug("JvmRouteFilter.init(): initializing JvmRouteFilter");
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ // Check if request and response is valid
+ if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse))
+ {
+ // Don't know how to handle. There is a mistake.
+ throw new RuntimeException("JvmRouteFilter.doFilter(): Not a http request and response type.");
+ }
+
+ // get session id
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse res = (HttpServletResponse) response;
+ HttpSession session = req.getSession(false);
+ if (session != null)
+ {
+ String sessionId = session.getId();
+
+ // Obtain JvmRoute
+ String jvmRoute = manager_.getJvmRoute();
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("doFilter(): check if need to re-route based on JvmRoute. Session id: " +
+ sessionId + " jvmRoute: " + jvmRoute);
+ }
+
+ if (jvmRoute == null)
+ {
+ throw new RuntimeException("JvmRouteFilter.doFilter(): Tomcat JvmRoute is null. " +
+ "Need to assign a value in Tomcat server.xml for load balancing.");
+ }
+
+ // Check if incoming session id has JvmRoute appended. If not, append it.
+ // TODO. We handle only get session id by cookie only
+ if (req.isRequestedSessionIdFromURL())
+ {
+ // Warning but do nothing
+ log_.error("JvmRouteFilter.doFilter(): Can't handle clustering where session id is from URL. Will skip.");
+ }
+ else
+ {
+ handleJvmRoute(sessionId, jvmRoute, res);
+ }
+ }
+ chain.doFilter(request, response);
+ }
+
+ protected void handleJvmRoute(String sessionId, String jvmRoute, HttpServletResponse response)
+ {
+ // get requested jvmRoute.
+ // TODO. The current format is assumed to be id.jvmRoute. Can be generalized later.
+ String requestedJvmRoute = null;
+ int ind = sessionId.indexOf(".");
+ if (ind > 0)
+ {
+ requestedJvmRoute = sessionId.substring(sessionId.indexOf(".") + 1, sessionId.length());
+ }
+
+ String sid = sessionId;
+ if (requestedJvmRoute == null)
+ {
+ // If this filter is turned on, we assume we have an appendix of jvmRoute. So this request is new.
+ sid = sessionId + "." + jvmRoute;
+ manager_.setNewSessionCookie(sid, response);
+ }
+ else if (requestedJvmRoute.equals(jvmRoute))
+ {
+ return; // Nothing more needs to be done.
+ }
+ else
+ {
+ // We just have a failover since jvmRoute does not match. We will replace the old one with the new one.
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): We have detected a failover with differen jvmRoute." +
+ " old one: " + requestedJvmRoute + " new one: " + jvmRoute + ". Will reset the session id.");
+ }
+ int index = sessionId.indexOf(".");
+ if (index > 0)
+ {
+ String base = sessionId.substring(0, sessionId.indexOf("."));
+ sid = base + "." + jvmRoute;
+ }
+ else
+ {
+ throw new RuntimeException("JvmRouteFilter.handleJvmRoute(): session id doesn't JvmRoute.");
+ }
+
+ manager_.setNewSessionCookie(sid, response);
+ // Change the sessionId with the new one using local jvmRoute
+ Session catalinaSession = null;
+ try
+ {
+ catalinaSession = manager_.findSession(sessionId);
+ // change session id with the new one using local jvmRoute and update cluster if needed.
+ if( catalinaSession != null )
+ {
+ catalinaSession.setId(sid);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): changed catalina session to= [" + sid + "] old one= [" + sessionId + "]");
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): manager_.findSession() unable to find session= [" + sessionId + "]", e);
+ }
+ throw new NestedRuntimeException("JvmRouteFilter.handleJvmRoute(): cannot find session [" + sessionId + "]", e);
+ }
+ }
+ }
+
+
+ public void destroy()
+ {
+ manager_ = null;
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,749 @@
+/*
+ * Copyright 2002-2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JBoss, the OpenSource WebOS
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.Constants;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.logging.Logger;
+
+
+/**
+ * Standard implementation of the <code>Service</code> interface. The
+ * associated Container is generally an instance of Engine, but this is
+ * not required.
+ * <p/>
+ * This version does not start the connectors in the start method so that
+ * all web apps may be completely deployed before enabling the transport
+ * layer.
+ *
+ * @author Craig R. McClanahan
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 30190 $
+ */
+public class StandardService
+ implements Lifecycle, Service, MBeanRegistration
+{
+ private static Logger log = Logger.getLogger(StandardService.class);
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The set of Connectors associated with this Service.
+ */
+ private Connector connectors[] = new Connector[0];
+
+
+ /**
+ * The Container associated with this Service.
+ */
+ private Container container = null;
+
+
+ /**
+ * The debugging detail level for this component.
+ */
+ private int debug = 0;
+
+
+ /**
+ * Descriptive information about this component implementation.
+ */
+ private static final String info =
+ "org.jboss.web.tomcat.service.StandardService/1.0";
+
+
+ /**
+ * Has this component been initialized?
+ */
+ private boolean initialized = false;
+
+
+ /**
+ * The name of this service.
+ */
+ private String name = null;
+
+
+ /**
+ * The lifecycle event support for this component.
+ */
+ private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+ /**
+ * The string manager for this package.
+ */
+ private static final StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+ /**
+ * The <code>Server</code> that owns this Service, if any.
+ */
+ private Server server = null;
+
+ /**
+ * Has this component been started?
+ */
+ private boolean started = false;
+
+
+ /**
+ * The property change support for this component.
+ */
+ protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * Return the <code>Container</code> that handles requests for all
+ * <code>Connectors</code> associated with this Service.
+ */
+ public Container getContainer()
+ {
+
+ return (this.container);
+
+ }
+
+
+ /**
+ * Set the <code>Container</code> that handles requests for all
+ * <code>Connectors</code> associated with this Service.
+ *
+ * @param container The new Container
+ */
+ public void setContainer(Container container)
+ {
+
+ Container oldContainer = this.container;
+ if ((oldContainer != null) && (oldContainer instanceof Engine))
+ ((Engine) oldContainer).setService(null);
+ this.container = container;
+ if ((this.container != null) && (this.container instanceof Engine))
+ ((Engine) this.container).setService(this);
+ if (started && (this.container != null) &&
+ (this.container instanceof Lifecycle))
+ {
+ try
+ {
+ ((Lifecycle) this.container).start();
+ }
+ catch (LifecycleException e)
+ {
+ ;
+ }
+ }
+ synchronized (connectors)
+ {
+ for (int i = 0; i < connectors.length; i++)
+ connectors[i].setContainer(this.container);
+ }
+ if (started && (oldContainer != null) &&
+ (oldContainer instanceof Lifecycle))
+ {
+ try
+ {
+ ((Lifecycle) oldContainer).stop();
+ }
+ catch (LifecycleException e)
+ {
+ ;
+ }
+ }
+
+ // Report this property change to interested listeners
+ support.firePropertyChange("container", oldContainer, this.container);
+
+ }
+
+ public ObjectName getContainerName()
+ {
+ if (container instanceof ContainerBase)
+ {
+ return ((ContainerBase) container).getJmxName();
+ }
+ return null;
+ }
+
+
+ /**
+ * Return the debugging detail level of this component.
+ */
+ public int getDebug()
+ {
+
+ return (this.debug);
+
+ }
+
+
+ /**
+ * Set the debugging detail level of this component.
+ *
+ * @param debug The new debugging detail level
+ */
+ public void setDebug(int debug)
+ {
+
+ this.debug = debug;
+
+ }
+
+
+ /**
+ * Return descriptive information about this Service implementation and
+ * the corresponding version number, in the format
+ * <code><description>/<version></code>.
+ */
+ public String getInfo()
+ {
+
+ return (info);
+
+ }
+
+
+ /**
+ * Return the name of this Service.
+ */
+ public String getName()
+ {
+
+ return (this.name);
+
+ }
+
+
+ /**
+ * Set the name of this Service.
+ *
+ * @param name The new service name
+ */
+ public void setName(String name)
+ {
+
+ this.name = name;
+
+ }
+
+
+ /**
+ * Return the <code>Server</code> with which we are associated (if any).
+ */
+ public Server getServer()
+ {
+
+ return (this.server);
+
+ }
+
+
+ /**
+ * Set the <code>Server</code> with which we are associated (if any).
+ *
+ * @param server The server that owns this Service
+ */
+ public void setServer(Server server)
+ {
+
+ this.server = server;
+
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Add a new Connector to the set of defined Connectors, and associate it
+ * with this Service's Container.
+ *
+ * @param connector The Connector to be added
+ */
+ public void addConnector(Connector connector)
+ {
+
+ synchronized (connectors)
+ {
+ connector.setContainer(this.container);
+ connector.setService(this);
+ Connector results[] = new Connector[connectors.length + 1];
+ System.arraycopy(connectors, 0, results, 0, connectors.length);
+ results[connectors.length] = connector;
+ connectors = results;
+
+ if (initialized)
+ {
+ try
+ {
+ connector.initialize();
+ }
+ catch (LifecycleException e)
+ {
+ e.printStackTrace(System.err);
+ }
+ }
+
+ if (started && (connector instanceof Lifecycle))
+ {
+ try
+ {
+ ((Lifecycle) connector).start();
+ }
+ catch (LifecycleException e)
+ {
+ ;
+ }
+ }
+
+ // Report this property change to interested listeners
+ support.firePropertyChange("connector", null, connector);
+ }
+
+ }
+
+ public ObjectName[] getConnectorNames()
+ {
+ ObjectName results[] = new ObjectName[connectors.length];
+ for (int i = 0; i < results.length; i++)
+ {
+ // if it's a coyote connector
+ //if( connectors[i] instanceof CoyoteConnector ) {
+ // results[i]=((CoyoteConnector)connectors[i]).getJmxName();
+ //}
+ }
+ return results;
+ }
+
+
+ /**
+ * Add a property change listener to this component.
+ *
+ * @param listener The listener to add
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+
+ support.addPropertyChangeListener(listener);
+
+ }
+
+
+ /**
+ * Find and return the set of Connectors associated with this Service.
+ */
+ public Connector[] findConnectors()
+ {
+
+ return (connectors);
+
+ }
+
+
+ /**
+ * Remove the specified Connector from the set associated from this
+ * Service. The removed Connector will also be disassociated from our
+ * Container.
+ *
+ * @param connector The Connector to be removed
+ */
+ public void removeConnector(Connector connector)
+ {
+
+ synchronized (connectors)
+ {
+ int j = -1;
+ for (int i = 0; i < connectors.length; i++)
+ {
+ if (connector == connectors[i])
+ {
+ j = i;
+ break;
+ }
+ }
+ if (j < 0)
+ return;
+ if (started && (connectors[j] instanceof Lifecycle))
+ {
+ try
+ {
+ ((Lifecycle) connectors[j]).stop();
+ }
+ catch (LifecycleException e)
+ {
+ ;
+ }
+ }
+ connectors[j].setContainer(null);
+ connector.setService(null);
+ int k = 0;
+ Connector results[] = new Connector[connectors.length - 1];
+ for (int i = 0; i < connectors.length; i++)
+ {
+ if (i != j)
+ results[k++] = connectors[i];
+ }
+ connectors = results;
+
+ // Report this property change to interested listeners
+ support.firePropertyChange("connector", connector, null);
+ }
+
+ }
+
+
+ /**
+ * Remove a property change listener from this component.
+ *
+ * @param listener The listener to remove
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+
+ support.removePropertyChangeListener(listener);
+
+ }
+
+
+ /**
+ * Return a String representation of this component.
+ */
+ public String toString()
+ {
+
+ StringBuffer sb = new StringBuffer("StandardService[");
+ sb.append(getName());
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+
+ // ------------------------------------------------------ Lifecycle Methods
+
+
+ /**
+ * Add a LifecycleEvent listener to this component.
+ *
+ * @param listener The listener to add
+ */
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+
+ lifecycle.addLifecycleListener(listener);
+
+ }
+
+
+ /**
+ * Get the lifecycle listeners associated with this lifecycle. If this
+ * Lifecycle has no listeners registered, a zero-length array is returned.
+ */
+ public LifecycleListener[] findLifecycleListeners()
+ {
+
+ return lifecycle.findLifecycleListeners();
+
+ }
+
+
+ /**
+ * Remove a LifecycleEvent listener from this component.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+
+ lifecycle.removeLifecycleListener(listener);
+
+ }
+
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called before any of the public
+ * methods of this component are utilized. It should also send a
+ * LifecycleEvent of type START_EVENT to any registered listeners.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ public void start() throws LifecycleException
+ {
+
+ // Validate and update our current component state
+ if (started)
+ {
+ log.info(sm.getString("standardService.start.started"));
+ }
+
+ if (!initialized)
+ init();
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
+
+ log.info(sm.getString("standardService.start.name", this.name));
+ lifecycle.fireLifecycleEvent(START_EVENT, null);
+ started = true;
+
+ // Start our defined Container first
+ if (container != null)
+ {
+ synchronized (container)
+ {
+ if (container instanceof Lifecycle)
+ {
+ ((Lifecycle) container).start();
+ }
+ }
+ }
+
+ /* Start our defined Connectors second
+ synchronized (connectors) {
+ for (int i = 0; i < connectors.length; i++) {
+ if (connectors[i] instanceof Lifecycle)
+ ((Lifecycle) connectors[i]).start();
+ }
+ }
+*/
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
+
+ }
+
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component. It should also send a LifecycleEvent
+ * of type STOP_EVENT to any registered listeners.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that needs to be reported
+ */
+ public void stop() throws LifecycleException
+ {
+
+ // Validate and update our current component state
+ if (!started)
+ {
+ return;
+ }
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
+
+ // Stop our defined Connectors first
+ synchronized (connectors)
+ {
+ for (int i = 0; i < connectors.length; i++)
+ {
+ connectors[i].pause();
+ }
+ }
+
+ // Heuristic: Sleep for a while to ensure pause of the connector
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e)
+ {
+ // Ignore
+ }
+
+ lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+
+ log.info
+ (sm.getString("standardService.stop.name", this.name));
+ started = false;
+
+ // Stop our defined Container second
+ if (container != null)
+ {
+ synchronized (container)
+ {
+ if (container instanceof Lifecycle)
+ {
+ ((Lifecycle) container).stop();
+ }
+ }
+ }
+
+ // Stop our defined Connectors first
+ synchronized (connectors)
+ {
+ for (int i = 0; i < connectors.length; i++)
+ {
+ if (connectors[i] instanceof Lifecycle)
+ ((Lifecycle) connectors[i]).stop();
+ }
+ }
+
+ if (oname == controller)
+ {
+ // we registered ourself on init().
+ // That should be the typical case - this object is just for
+ // backward compat, nobody should bother to load it explicitely
+ Registry.getRegistry(null, null).unregisterComponent(oname);
+ }
+
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
+
+ }
+
+
+ /**
+ * Invoke a pre-startup initialization. This is used to allow connectors
+ * to bind to restricted ports under Unix operating environments.
+ */
+ public void initialize()
+ throws LifecycleException
+ {
+ // Service shouldn't be used with embeded, so it doesn't matter
+ if (initialized)
+ {
+ log.info(sm.getString("standardService.initialize.initialized"));
+ return;
+ }
+ initialized = true;
+
+ if (oname == null)
+ {
+ try
+ {
+ // Hack - Server should be deprecated...
+ Container engine = this.getContainer();
+ domain = engine.getName();
+ oname = new ObjectName(domain + ":type=Service,serviceName=" + name);
+ this.controller = oname;
+ Registry.getRegistry(null, null)
+ .registerComponent(this, oname, null);
+ }
+ catch (Exception e)
+ {
+ log.error("Error registering ", e);
+ }
+
+
+ }
+ if (server == null)
+ {
+ // Register with the server
+ // HACK: ServerFactory should be removed...
+
+ ServerFactory.getServer().addService(this);
+ }
+
+
+ // Initialize our defined Connectors
+ synchronized (connectors)
+ {
+ for (int i = 0; i < connectors.length; i++)
+ {
+ connectors[i].initialize();
+ }
+ }
+ }
+
+ public void destroy() throws LifecycleException
+ {
+ if (started) stop();
+ // unregister should be here probably
+ }
+
+ public void init()
+ {
+ try
+ {
+ initialize();
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ }
+
+ protected String type;
+ protected String domain;
+ protected String suffix;
+ protected ObjectName oname;
+ protected ObjectName controller;
+ protected MBeanServer mserver;
+
+ public ObjectName getObjectName()
+ {
+ return oname;
+ }
+
+ public String getDomain()
+ {
+ return domain;
+ }
+
+ public ObjectName preRegister(MBeanServer server,
+ ObjectName name) throws Exception
+ {
+ oname = name;
+ mserver = server;
+ domain = name.getDomain();
+ return name;
+ }
+
+ public void postRegister(Boolean registrationDone)
+ {
+ }
+
+ public void preDeregister() throws Exception
+ {
+ }
+
+ public void postDeregister()
+ {
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,388 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tomcat.util.modeler.Registry;
+
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.manager.Constants;
+import org.apache.catalina.manager.StatusTransformer;
+
+/**
+ * This servlet will display a complete status of the HTTP/1.1 connector.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 41072 $ $Date: 2006-02-09 08:47:17 +0100 (jeu., 09 févr. 2006) $
+ */
+
+public class StatusServlet
+ extends HttpServlet implements NotificationListener
+{
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The debugging detail level for this servlet.
+ */
+ private int debug = 0;
+
+
+ /**
+ * MBean server.
+ */
+ protected MBeanServer mBeanServer = null;
+
+
+ /**
+ * Vector of protocol handlers object names.
+ */
+ protected Vector protocolHandlers = new Vector();
+
+
+ /**
+ * Vector of thread pools object names.
+ */
+ protected Vector threadPools = new Vector();
+
+
+ /**
+ * Vector of request processors object names.
+ */
+ protected Vector requestProcessors = new Vector();
+
+
+ /**
+ * Vector of global request processors object names.
+ */
+ protected Vector globalRequestProcessors = new Vector();
+
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Initialize this servlet.
+ */
+ public void init() throws ServletException
+ {
+
+ // Retrieve the MBean server
+ mBeanServer = Registry.getServer();
+
+ // Set our properties from the initialization parameters
+ String value = null;
+ try
+ {
+ value = getServletConfig().getInitParameter("debug");
+ debug = Integer.parseInt(value);
+ }
+ catch (Throwable t)
+ {
+ ;
+ }
+
+ try
+ {
+
+ // Query protocol handlers
+ String onStr = "*:type=ProtocolHandler,*";
+ ObjectName objectName = new ObjectName(onStr);
+ Set set = mBeanServer.queryMBeans(objectName, null);
+ Iterator iterator = set.iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ protocolHandlers.addElement(oi.getObjectName());
+ }
+
+ // Query Thread Pools
+ onStr = "*:type=ThreadPool,*";
+ objectName = new ObjectName(onStr);
+ set = mBeanServer.queryMBeans(objectName, null);
+ iterator = set.iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ threadPools.addElement(oi.getObjectName());
+ }
+
+ // Query Global Request Processors
+ onStr = "*:type=GlobalRequestProcessor,*";
+ objectName = new ObjectName(onStr);
+ set = mBeanServer.queryMBeans(objectName, null);
+ iterator = set.iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ globalRequestProcessors.addElement(oi.getObjectName());
+ }
+
+ // Query Request Processors
+ onStr = "*:type=RequestProcessor,*";
+ objectName = new ObjectName(onStr);
+ set = mBeanServer.queryMBeans(objectName, null);
+ iterator = set.iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ requestProcessors.addElement(oi.getObjectName());
+ }
+
+ // Register with MBean server
+ onStr = "JMImplementation:type=MBeanServerDelegate";
+ objectName = new ObjectName(onStr);
+ mBeanServer.addNotificationListener(objectName, this, null, null);
+
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ }
+
+
+ /**
+ * Finalize this servlet.
+ */
+ public void destroy()
+ {
+
+ ; // No actions necessary
+
+ }
+
+
+ /**
+ * Process a GET request for the specified resource.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ * @throws IOException if an input/output error occurs
+ * @throws ServletException if a servlet-specified error occurs
+ */
+ public void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws IOException, ServletException
+ {
+
+ // mode is flag for HTML or XML output
+ int mode = 0;
+ // if ?XML=true, set the mode to XML
+ if ("true".equals(request.getParameter("XML")))
+ {
+ mode = 1;
+ }
+ StatusTransformer.setContentType(response, mode);
+
+ PrintWriter writer = response.getWriter();
+
+ boolean completeStatus = false;
+ if ("true".equals(request.getParameter("full")))
+ {
+ completeStatus = true;
+ }
+
+ // use StatusTransformer to output status
+ if (mode == 0)
+ {
+ // HTML Header Section
+ writer.print(HTML_HEADER);
+ }
+ else if (mode == 1)
+ {
+ writer.write(Constants.XML_DECLARATION);
+ writer.write
+ (Constants.XML_STYLE);
+ writer.write("<status>");
+ }
+
+ try
+ {
+
+ // Display virtual machine statistics
+ StatusTransformer.writeVMState(writer, mode);
+
+ Enumeration i = threadPools.elements();
+ while (i.hasMoreElements())
+ {
+ ObjectName objectName = (ObjectName) i.nextElement();
+ String name = objectName.getKeyProperty("name");
+ // use StatusTransformer to output status
+ StatusTransformer.writeConnectorState
+ (writer, objectName,
+ name, mBeanServer, globalRequestProcessors,
+ requestProcessors, mode);
+ }
+
+ if (completeStatus)
+ {
+ // Note: Retrieving the full status is much slower
+ // use StatusTransformer to output status
+ StatusTransformer.writeDetailedState
+ (writer, mBeanServer, mode);
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new ServletException(e);
+ }
+
+ if (mode == 0)
+ {
+ writer.print(HTML_FOOTER);
+ }
+ else if (mode == 1)
+ {
+ writer.write("</status>");
+ }
+
+ }
+
+ // ------------------------------------------- NotificationListener Methods
+
+
+ public void handleNotification(Notification notification,
+ java.lang.Object handback)
+ {
+
+ if (notification instanceof MBeanServerNotification)
+ {
+ ObjectName objectName =
+ ((MBeanServerNotification) notification).getMBeanName();
+ if (notification.getType().equals
+ (MBeanServerNotification.REGISTRATION_NOTIFICATION))
+ {
+ String type = objectName.getKeyProperty("type");
+ if (type != null)
+ {
+ if (type.equals("ProtocolHandler"))
+ {
+ protocolHandlers.addElement(objectName);
+ }
+ else if (type.equals("ThreadPool"))
+ {
+ threadPools.addElement(objectName);
+ }
+ else if (type.equals("GlobalRequestProcessor"))
+ {
+ globalRequestProcessors.addElement(objectName);
+ }
+ else if (type.equals("RequestProcessor"))
+ {
+ requestProcessors.addElement(objectName);
+ }
+ }
+ }
+ else if (notification.getType().equals
+ (MBeanServerNotification.UNREGISTRATION_NOTIFICATION))
+ {
+ String type = objectName.getKeyProperty("type");
+ if (type != null)
+ {
+ if (type.equals("ProtocolHandler"))
+ {
+ protocolHandlers.removeElement(objectName);
+ }
+ else if (type.equals("ThreadPool"))
+ {
+ threadPools.removeElement(objectName);
+ }
+ else if (type.equals("GlobalRequestProcessor"))
+ {
+ globalRequestProcessors.removeElement(objectName);
+ }
+ else if (type.equals("RequestProcessor"))
+ {
+ requestProcessors.removeElement(objectName);
+ }
+ }
+ String j2eeType = objectName.getKeyProperty("j2eeType");
+ if (j2eeType != null)
+ {
+
+ }
+ }
+ }
+
+ }
+
+
+ // ------------------------------------------------------- Private Constats
+
+
+ private static final String HTML_HEADER =
+ "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
+ + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+ + "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ + "<head>"
+ + "<title>Tomcat Status</title>"
+ + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />"
+ + "<link rel=\"StyleSheet\" href=\"jboss.css\" type=\"text/css\"/>"
+ + "</head>"
+ + "<body>"
+ + "<!-- header begin -->"
+ + "<a href=\"http://www.jboss.org\">"
+ + "<img src=\"logo.gif\" alt=\"JBoss\" id=\"logo\" width=\"226\" height=\"105\" />"
+ + "</a>"
+ + "<div id=\"header\"> </div>"
+ + "<div id=\"navigation_bar\">"
+ + "</div>"
+ + "<!-- header end -->";
+
+ private static final String HTML_FOOTER =
+ "<!-- footer begin -->"
+ + "<div id=\"footer\">"
+ + "<div id=\"credits\">JBoss™ Application Server</div>"
+ + "<div id=\"footer_bar\"> </div>"
+ + "</div>"
+ + "<!-- footer end -->"
+ + "</body>"
+ + "</html>";
+
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,553 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.cert.Certificate;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.management.Attribute;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Loader;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployment.DeploymentException;
+import org.jboss.deployment.DeploymentInfo;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.web.AbstractWebContainer;
+import org.jboss.web.AbstractWebDeployer;
+import org.jboss.web.WebApplication;
+import org.jboss.web.tomcat.security.CustomPrincipalValve;
+import org.jboss.web.tomcat.security.JaccContextValve;
+import org.jboss.web.tomcat.security.RunAsListener;
+import org.jboss.web.tomcat.security.SecurityAssociationValve;
+import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+import org.jboss.web.tomcat.service.session.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.JBossCacheManager;
+
+/**
+ * The tomcat web application deployer
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @version $Revision: 57206 $
+ */
+public class TomcatDeployer extends AbstractWebDeployer
+{
+ /**
+ * The name of the war level context configuration descriptor
+ */
+ private static final String CONTEXT_CONFIG_FILE = "WEB-INF/context.xml";
+
+ private DeployerConfig config;
+ private String[] javaVMs =
+ {" jboss.management.local:J2EEServer=Local,j2eeType=JVM,name=localhost"};
+ private String serverName = "jboss";
+ private HashMap vhostToHostNames = new HashMap();
+
+ public void init(Object containerConfig) throws Exception
+ {
+ this.config = (DeployerConfig) containerConfig;
+ super.setJava2ClassLoadingCompliance(config.isJava2ClassLoadingCompliance());
+ super.setUnpackWars(config.isUnpackWars());
+ super.setLenientEjbLink(config.isLenientEjbLink());
+ super.setDefaultSecurityDomain(config.getDefaultSecurityDomain());
+ }
+
+ /**
+ * Perform the tomcat specific deployment steps.
+ */
+ protected void performDeploy(WebApplication appInfo, String warUrl,
+ AbstractWebContainer.WebDescriptorParser webAppParser)
+ throws Exception
+ {
+ WebMetaData metaData = appInfo.getMetaData();
+ String hostName = null;
+ // Get any jboss-web/virtual-hosts
+ Iterator vhostNames = metaData.getVirtualHosts();
+ // Map the virtual hosts onto the configured hosts
+ Iterator hostNames = mapVirtualHosts(vhostNames);
+ if (hostNames.hasNext())
+ {
+ hostName = hostNames.next().toString();
+ }
+ performDeployInternal(hostName, appInfo, warUrl, webAppParser);
+ while (hostNames.hasNext())
+ {
+ String additionalHostName = hostNames.next().toString();
+ performDeployInternal(additionalHostName, appInfo, warUrl, webAppParser);
+ }
+ }
+
+ protected void performDeployInternal(String hostName,
+ WebApplication appInfo, String warUrl,
+ AbstractWebContainer.WebDescriptorParser webAppParser)
+ throws Exception
+ {
+
+ WebMetaData metaData = appInfo.getMetaData();
+ String ctxPath = metaData.getContextRoot();
+ if (ctxPath.equals("/") || ctxPath.equals("/ROOT") || ctxPath.equals(""))
+ {
+ log.debug("deploy root context=" + ctxPath);
+ ctxPath = "/";
+ metaData.setContextRoot(ctxPath);
+ }
+
+ log.info("deploy, ctxPath=" + ctxPath + ", warUrl=" + shortWarUrlFromServerHome(warUrl));
+
+ URL url = new URL(warUrl);
+
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ /* If we are using the jboss class loader we need to augment its path
+ to include the WEB-INF/{lib,classes} dirs or else scoped class loading
+ does not see the war level overrides. The call to setWarURL adds these
+ paths to the deployment UCL.
+ */
+ Loader webLoader = null;
+ if (config.isUseJBossWebLoader())
+ {
+ WebCtxLoader jbossLoader = new WebCtxLoader(loader);
+ jbossLoader.setWarURL(url);
+ webLoader = jbossLoader;
+ }
+ else
+ {
+ String[] pkgs = config.getFilteredPackages();
+ WebAppLoader jbossLoader = new WebAppLoader(loader, pkgs);
+ jbossLoader.setDelegate(getJava2ClassLoadingCompliance());
+ webLoader = jbossLoader;
+ }
+
+ // We need to establish the JNDI ENC prior to the start
+ // of the web container so that init on startup servlets are able
+ // to interact with their ENC. We hook into the context lifecycle
+ // events to be notified of the start of the
+ // context as this occurs before the servlets are started.
+ if (appInfo.getAppData() == null)
+ webAppParser.parseWebAppDescriptors(loader, appInfo.getMetaData());
+
+ appInfo.setName(url.getPath());
+ appInfo.setClassLoader(loader);
+ appInfo.setURL(url);
+
+ String objectNameS = config.getCatalinaDomain()
+ + ":j2eeType=WebModule,name=//" +
+ ((hostName == null) ? "localhost" : hostName)
+ + ctxPath + ",J2EEApplication=none,J2EEServer=none";
+
+ ObjectName objectName = new ObjectName(objectNameS);
+
+ if (server.isRegistered(objectName))
+ {
+ log.debug("Already exists, destroying " + objectName);
+ server.invoke(objectName, "destroy", new Object[]{},
+ new String[]{});
+ }
+
+ Registry.getRegistry().registerComponent(Class.forName(config.getContextClassName()).newInstance(),
+ objectName, "org.apache.catalina.core.StandardContext");
+
+ // Find and set config file on the context
+ // If WAR is packed, expand config file to temp folder
+ String ctxConfig = null;
+ try
+ {
+ ctxConfig = findConfig(url);
+ }
+ catch (IOException e)
+ {
+ log.debug("No " + CONTEXT_CONFIG_FILE + " in " + url, e);
+ }
+
+ server.setAttribute(objectName, new Attribute("docBase", url.getFile()));
+
+ server.setAttribute(objectName, new Attribute("configFile", ctxConfig));
+
+ server.setAttribute(objectName, new Attribute
+ ("defaultContextXml", "context.xml"));
+ server.setAttribute(objectName, new Attribute
+ ("defaultWebXml", "conf/web.xml"));
+
+ server.setAttribute(objectName, new Attribute("javaVMs", javaVMs));
+
+ server.setAttribute(objectName, new Attribute("server", serverName));
+
+ server.setAttribute(objectName, new Attribute
+ ("saveConfig", Boolean.FALSE));
+
+ if (webLoader != null)
+ {
+ server.setAttribute(objectName, new Attribute
+ ("loader", webLoader));
+ }
+ else
+ {
+ server.setAttribute(objectName, new Attribute
+ ("parentClassLoader", loader));
+ }
+
+ server.setAttribute(objectName, new Attribute
+ ("delegate", new Boolean(getJava2ClassLoadingCompliance())));
+
+ String[] jspCP = getCompileClasspath(loader);
+ StringBuffer classpath = new StringBuffer();
+ for (int u = 0; u < jspCP.length; u++)
+ {
+ String repository = jspCP[u];
+ if (repository == null)
+ continue;
+ if (repository.startsWith("file://"))
+ repository = repository.substring(7);
+ else if (repository.startsWith("file:"))
+ repository = repository.substring(5);
+ else
+ continue;
+ if (repository == null)
+ continue;
+ // ok it is a file. Make sure that is is a directory or jar file
+ File fp = new File(repository);
+ if (!fp.isDirectory())
+ {
+ // if it is not a directory, try to open it as a zipfile.
+ try
+ {
+ // avoid opening .xml files
+ if (fp.getName().toLowerCase().endsWith(".xml"))
+ continue;
+
+ ZipFile zip = new ZipFile(fp);
+ zip.close();
+ }
+ catch (IOException e)
+ {
+ continue;
+ }
+
+ }
+ if (u > 0)
+ classpath.append(File.pathSeparator);
+ classpath.append(repository);
+ }
+
+ server.setAttribute(objectName, new Attribute
+ ("compilerClasspath", classpath.toString()));
+
+ // Set the session cookies flag according to metadata
+ switch (metaData.getSessionCookies())
+ {
+ case WebMetaData.SESSION_COOKIES_ENABLED:
+ server.setAttribute(objectName, new Attribute
+ ("cookies", new Boolean(true)));
+ log.debug("Enabling session cookies");
+ break;
+ case WebMetaData.SESSION_COOKIES_DISABLED:
+ server.setAttribute(objectName, new Attribute
+ ("cookies", new Boolean(false)));
+ log.debug("Disabling session cookies");
+ break;
+ default:
+ log.debug("Using session cookies default setting");
+ }
+
+ // Add a valve to estalish the JACC context before authorization valves
+ Certificate[] certs = null;
+ CodeSource cs = new CodeSource(url, certs);
+ JaccContextValve jaccValve = new JaccContextValve(metaData.getJaccContextID(), cs);
+ server.invoke(objectName, "addValve",
+ new Object[]{jaccValve},
+ new String[]{"org.apache.catalina.Valve"});
+
+ // Pass the metadata to the RunAsListener via a thread local
+ RunAsListener.metaDataLocal.set(metaData);
+
+ try
+ {
+ // Init the container; this will also start it
+ server.invoke(objectName, "init", new Object[]{}, new String[]{});
+ }
+ finally
+ {
+ RunAsListener.metaDataLocal.set(null);
+ }
+
+ // make the context class loader known to the WebMetaData, ws4ee needs it
+ // to instanciate service endpoint pojos that live in this webapp
+ Loader ctxLoader = (Loader)server.getAttribute(objectName, "loader");
+ metaData.setContextLoader(ctxLoader.getClassLoader());
+
+ // Clustering
+ if (metaData.getDistributable())
+ {
+ // Try to initate clustering, fallback to standard if no clustering is available
+ try
+ {
+ AbstractJBossManager manager = null;
+ String managerClassName = config.getManagerClass();
+ Class managerClass = Thread.currentThread().getContextClassLoader().loadClass(managerClassName);
+ manager = (AbstractJBossManager) managerClass.newInstance();
+
+ if (manager instanceof JBossCacheManager)
+ {
+ // TODO either deprecate snapshot mode or move its config
+ // into jboss-web.xml.
+ String snapshotMode = config.getSnapshotMode();
+ int snapshotInterval = config.getSnapshotInterval();
+ JBossCacheManager jbcm = (JBossCacheManager) manager;
+ jbcm.setSnapshotMode(snapshotMode);
+ jbcm.setSnapshotInterval(snapshotInterval);
+ }
+
+ String name = "//" + ((hostName == null) ? "localhost" : hostName) + ctxPath;
+ manager.init(name, metaData, config.isUseJK(), config.isUseLocalCache());
+
+ // Don't assign the manager to the context until all config
+ // is done, or else the manager will be started without the config
+ server.setAttribute(objectName, new Attribute("manager", manager));
+
+ log.debug("Enabled clustering support for ctxPath=" + ctxPath);
+ }
+ catch (ClusteringNotSupportedException e)
+ {
+ // JBAS-3513 Just log a WARN, not an ERROR
+ log.warn("Failed to setup clustering, clustering disabled. ClusteringNotSupportedException: " +
+ e.getMessage());
+ }
+ catch (NoClassDefFoundError ncdf)
+ {
+ // JBAS-3513 Just log a WARN, not an ERROR
+ log.debug("Classes needed for clustered webapp unavailable", ncdf);
+ log.warn("Failed to setup clustering, clustering disabled. NoClassDefFoundError: " +
+ ncdf.getMessage());
+ }
+ catch(Throwable t)
+ {
+ // TODO consider letting this through and fail the deployment
+ log.error("Failed to setup clustering, clustering disabled. Exception: ", t);
+ }
+ }
+
+ /* Add security association valve after the authorization
+ valves so that the authenticated user may be associated with the
+ request thread/session.
+ */
+ SecurityAssociationValve valve = new SecurityAssociationValve(metaData,
+ config.getSecurityManagerService());
+ valve.setSubjectAttributeName(config.getSubjectAttributeName());
+ server.invoke(objectName, "addValve",
+ new Object[]{valve},
+ new String[]{"org.apache.catalina.Valve"});
+
+ // Retrieve the state, and throw an exception in case of a failure
+ Integer state = (Integer) server.getAttribute(objectName, "state");
+ if (state.intValue() != 1)
+ {
+ throw new DeploymentException("URL " + warUrl + " deployment failed");
+ }
+
+ appInfo.setAppData(objectName);
+
+ // Create mbeans for the servlets
+ DeploymentInfo di = webAppParser.getDeploymentInfo();
+ di.deployedObject = objectName;
+ ObjectName servletQuery = new ObjectName
+ (config.getCatalinaDomain() + ":j2eeType=Servlet,WebModule="
+ + objectName.getKeyProperty("name") + ",*");
+ Iterator iterator = server.queryMBeans(servletQuery, null).iterator();
+ while (iterator.hasNext())
+ {
+ di.mbeans.add(((ObjectInstance) iterator.next()).getObjectName());
+ }
+
+ log.debug("Initialized: " + appInfo + " " + objectName);
+
+ }
+
+
+ /**
+ * Called as part of the undeploy() method template to ask the
+ * subclass for perform the web container specific undeployment steps.
+ */
+ protected void performUndeploy(String warUrl, WebApplication appInfo)
+ throws Exception
+ {
+ if (appInfo == null)
+ {
+ log.debug("performUndeploy, no WebApplication found for URL "
+ + warUrl);
+ return;
+ }
+
+ log.info("undeploy, ctxPath=" + appInfo.getMetaData().getContextRoot()
+ + ", warUrl=" + shortWarUrlFromServerHome(warUrl));
+
+ WebMetaData metaData = appInfo.getMetaData();
+ String hostName = null;
+ Iterator hostNames = metaData.getVirtualHosts();
+ if (hostNames.hasNext())
+ {
+ hostName = hostNames.next().toString();
+ }
+ performUndeployInternal(hostName, warUrl, appInfo);
+ while (hostNames.hasNext())
+ {
+ String additionalHostName = hostNames.next().toString();
+ performUndeployInternal(additionalHostName, warUrl, appInfo);
+ }
+
+ }
+
+ protected void performUndeployInternal(String hostName, String warUrl,
+ WebApplication appInfo)
+ throws Exception
+ {
+
+ WebMetaData metaData = appInfo.getMetaData();
+ String ctxPath = metaData.getContextRoot();
+
+ // If the server is gone, all apps were stopped already
+ if (server == null)
+ return;
+
+ ObjectName objectName = new ObjectName(config.getCatalinaDomain()
+ + ":j2eeType=WebModule,name=//" +
+ ((hostName == null) ? "localhost" : hostName)
+ + ctxPath + ",J2EEApplication=none,J2EEServer=none");
+
+ if (server.isRegistered(objectName))
+ {
+ // Contexts should be stopped by the host already
+ server.invoke(objectName, "destroy", new Object[]{},
+ new String[]{});
+ }
+
+ }
+
+ /**
+ * Resolve the input virtual host names to the names of the configured Hosts
+ * @param vhostNames Iterator<String> for the jboss-web/virtual-host elements
+ * @return Iterator<String> of the unique Host names
+ * @throws Exception
+ */
+ protected synchronized Iterator mapVirtualHosts(Iterator vhostNames)
+ throws Exception
+ {
+ if( vhostToHostNames.size() == 0 )
+ {
+ // Query the configured Host mbeans
+ String hostQuery = config.getCatalinaDomain() + ":type=Host,*";
+ ObjectName query = new ObjectName(hostQuery);
+ Set hosts = server.queryNames(query, null);
+ Iterator iter = hosts.iterator();
+ while( iter.hasNext() )
+ {
+ ObjectName host = (ObjectName) iter.next();
+ String name = host.getKeyProperty("host");
+ if( name != null )
+ {
+ vhostToHostNames.put(name, name);
+ String[] aliases = (String[])
+ server.invoke(host, "findAliases", null, null);
+ int count = aliases != null ? aliases.length : 0;
+ for(int n = 0;n < count; n ++)
+ {
+ vhostToHostNames.put(aliases[n], name);
+ }
+ }
+ }
+ }
+
+ // Map the virtual host names to the hosts
+ HashSet hosts = new HashSet();
+ while( vhostNames.hasNext() )
+ {
+ String vhost = (String) vhostNames.next();
+ String host = (String) vhostToHostNames.get(vhost);
+ if( host == null )
+ {
+ log.warn("Failed to map vhost: "+vhost);
+ // This will cause a new host to be created
+ host = vhost;
+ }
+ hosts.add(host);
+ }
+ return hosts.iterator();
+ }
+
+ private String findConfig(URL warURL) throws IOException
+ {
+ String result = null;
+ // See if the warUrl is a dir or a file
+ File warFile = new File(warURL.getFile());
+ if (warURL.getProtocol().equals("file") && warFile.isDirectory() == true)
+ {
+ File webDD = new File(warFile, CONTEXT_CONFIG_FILE);
+ if (webDD.exists() == true) result = webDD.getAbsolutePath();
+ }
+ else
+ {
+ ZipFile zipFile = new ZipFile(warFile);
+ ZipEntry entry = zipFile.getEntry(CONTEXT_CONFIG_FILE);
+ if (entry != null)
+ {
+ InputStream zipIS = zipFile.getInputStream(entry);
+ byte[] buffer = new byte[512];
+ int bytes;
+ result = warFile.getAbsolutePath() + "-context.xml";
+ FileOutputStream fos = new FileOutputStream(result);
+ while ((bytes = zipIS.read(buffer)) > 0)
+ {
+ fos.write(buffer, 0, bytes);
+ }
+ zipIS.close();
+ fos.close();
+ }
+ zipFile.close();
+ }
+ return result;
+ }
+
+ private String shortWarUrlFromServerHome(String warUrl)
+ {
+ String serverHomeUrl = System.getProperty(org.jboss.system.server.ServerConfig.SERVER_HOME_URL);
+
+ if (warUrl == null || serverHomeUrl == null)
+ return warUrl;
+
+ if (warUrl.startsWith(serverHomeUrl))
+ return ".../" + warUrl.substring(serverHomeUrl.length());
+ else
+ return warUrl;
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/TomcatDeployer.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import org.apache.catalina.loader.WebappClassLoader;
+import org.jboss.logging.Logger;
+
+/**
+ * Subclass the tomcat web app class loader to override the filter method
+ * to exclude classes which cannot be override by the web app due to their
+ * use in the tomcat web container/integration.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public class WebAppClassLoader extends WebappClassLoader
+{
+ static Logger log = Logger.getLogger(WebAppClassLoader.class);
+ private String[] filteredPackages = {
+ "org.apache.commons.logging"
+ };
+
+ public WebAppClassLoader()
+ {
+ }
+
+ public WebAppClassLoader(ClassLoader parent)
+ {
+ super(parent);
+ }
+
+ public String[] getFilteredPackages()
+ {
+ return filteredPackages;
+ }
+ public void setFilteredPackages(String[] pkgs)
+ {
+ this.filteredPackages = pkgs;
+ }
+
+ /**
+ * Overriden to filter out classes in the packages listed in the
+ * filteredPackages settings.
+ *
+ * @param name
+ * @return true if the class should be loaded from the parent class loader,
+ * false if it can be loaded from this class loader.
+ */
+ protected boolean filter(String name)
+ {
+ boolean excludeClass = super.filter(name);
+ if( excludeClass == false )
+ {
+ // Check class against our filtered packages
+ int length = filteredPackages != null ? filteredPackages.length : 0;
+ for(int n = 0; n < length; n ++)
+ {
+ String pkg = filteredPackages[n];
+ if( name.startsWith(pkg) )
+ {
+ excludeClass = true;
+ break;
+ }
+ }
+ }
+ log.trace("filter name="+name+", exclude="+excludeClass);
+ return excludeClass;
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,68 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.LifecycleException;
+
+/**
+ * Override the tomcat WebappLoader to set the default class loader to the
+ * WebAppClassLoader and pass the filtered packages to the WebAppClassLoader.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public class WebAppLoader extends WebappLoader
+{
+ private String[] filteredPackages = {
+ "org.apache.commons.logging"
+ };
+
+ public WebAppLoader()
+ {
+ super();
+ setLoaderClass(WebAppClassLoader.class.getName());
+ }
+
+ public WebAppLoader(ClassLoader parent, String[] filteredPackages)
+ {
+ super(parent);
+ setLoaderClass(WebAppClassLoader.class.getName());
+ this.filteredPackages = filteredPackages;
+ }
+
+ /**
+ * Override to apply the filteredPackages to the jboss WebAppClassLoader
+ *
+ * @throws LifecycleException
+ */
+ public void start() throws LifecycleException
+ {
+ super.start();
+ ClassLoader loader = getClassLoader();
+ if( loader instanceof WebAppClassLoader )
+ {
+ WebAppClassLoader webLoader = (WebAppClassLoader) loader;
+ webLoader.setFilteredPackages(filteredPackages);
+ }
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,320 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+
+import javax.servlet.ServletContext;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.jboss.mx.loading.RepositoryClassLoader;
+
+/**
+ * Initial version of a JBoss implementation of the Tomcat Loader.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public class WebCtxLoader
+ implements Lifecycle, Loader
+{
+ /**
+ * The ClassLoader used to scope the ENC
+ */
+ protected ClassLoader encLoader;
+ /**
+ * The ClassLoader returned from getClassLoader
+ */
+ protected ENCLoader ctxLoader;
+ /**
+ * The war UCL used to load the war classes
+ */
+ protected RepositoryClassLoader delegate;
+ protected Container webContainer;
+ protected URL warURL;
+
+ /**
+ * The set of repositories associated with this class loader.
+ */
+ private ArrayList repositories = new ArrayList();
+
+ /**
+ * Create a WebCtxLoader given the ENC scoping class loader.
+ *
+ * @param encLoader
+ */
+ WebCtxLoader(ClassLoader encLoader)
+ {
+ this.encLoader = encLoader;
+ this.ctxLoader = new ENCLoader(encLoader);
+ ClassLoader parent = encLoader;
+ while ((parent instanceof RepositoryClassLoader) == false && parent != null)
+ parent = parent.getParent();
+ this.delegate = (RepositoryClassLoader) parent;
+ }
+
+ public void setWarURL(URL warURL) throws MalformedURLException
+ {
+ this.warURL = warURL;
+ String path = warURL.getFile();
+ File classesDir = new File(path, "WEB-INF/classes");
+ if (classesDir.exists())
+ {
+ delegate.addURL(classesDir.toURL());
+ ctxLoader.addURLInternal(classesDir.toURL());
+ }
+ File libDir = new File(path, "WEB-INF/lib");
+ if (libDir.exists())
+ {
+ File[] jars = libDir.listFiles();
+ int length = jars != null ? jars.length : 0;
+ for (int j = 0; j < length; j++)
+ {
+ File jar = jars[j];
+ if(jar.getAbsolutePath().endsWith(".jar"))
+ {
+ delegate.addURL(jar.toURL());
+ ctxLoader.addURLInternal(jar.toURL());
+ }
+ }
+ }
+ }
+
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return new LifecycleListener[0];
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ }
+
+ public void start() throws LifecycleException
+ {
+ // ctxLoader is set upon construction and nullified during stop
+ if (this.ctxLoader == null)
+ throw new LifecycleException("WebCtxLoader cannot be restarted");
+
+ setClassPath();
+ ServletContext servletContext = ((Context) webContainer).getServletContext();
+ if (servletContext == null)
+ return;
+ }
+
+ public void stop() throws LifecycleException
+ {
+ // Remove the ctxLoader mapping kept by the DirContextURLStreamHandler
+ DirContextURLStreamHandler.unbind(ctxLoader);
+ org.apache.commons.logging.LogFactory.release(ctxLoader);
+ org.apache.commons.logging.LogFactory.release(encLoader);
+ this.encLoader = null;
+ this.ctxLoader = null;
+ this.delegate = null;
+ this.repositories.clear();
+ this.warURL = null;
+ this.webContainer = null;
+ }
+
+ public void backgroundProcess()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ /**
+ * We must pass the wrapped encLoader as tomcat needs to see a unique
+ * class loader that is distinct from the thread context class loader seen
+ * to be in effect when the web app is started. This is due to how it
+ * binds contexts using the DirContextURLStreamHandler class.
+ *
+ * @return The ENC scoping class loader
+ * @see org.apache.naming.resources.DirContextURLStreamHandler
+ */
+ public ClassLoader getClassLoader()
+ {
+ return ctxLoader;
+ }
+
+ public Container getContainer()
+ {
+ return webContainer;
+ }
+
+ public void setContainer(Container container)
+ {
+ webContainer = container;
+
+ }
+
+ public boolean getDelegate()
+ {
+ return false;
+ }
+
+ public void setDelegate(boolean delegate)
+ {
+ }
+
+ public String getInfo()
+ {
+ return null;
+ }
+
+ public boolean getReloadable()
+ {
+ return false;
+ }
+
+ public void setReloadable(boolean reloadable)
+ {
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ }
+
+ public void addRepository(String repository)
+ {
+ if (repositories.contains(repository) == true)
+ return;
+ repositories.add(repository);
+ setClassPath();
+ }
+
+ public String[] findRepositories()
+ {
+ String[] tmp = new String[repositories.size()];
+ repositories.toArray(tmp);
+ return tmp;
+ }
+
+ public boolean modified()
+ {
+ return false;
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ }
+
+ /**
+ * Set the appropriate context attribute for our class path. This
+ * is required only because Jasper depends on it.
+ */
+ private void setClassPath()
+ {
+ // Validate our current state information
+ if (!(webContainer instanceof Context))
+ return;
+ ServletContext servletContext = ((Context) webContainer).getServletContext();
+ if (servletContext == null)
+ return;
+
+ try
+ {
+ Method method =
+ webContainer.getClass().getMethod("getCompilerClasspath", null);
+ Object baseClasspath = method.invoke(webContainer, null);
+ if (baseClasspath != null)
+ {
+ servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+ baseClasspath.toString());
+ return;
+ }
+ }
+ catch (Exception e)
+ {
+ // Ignore
+ e.printStackTrace();
+ }
+
+ StringBuffer classpath = new StringBuffer();
+
+ // Assemble the class path information from our repositories
+ for (int i = 0; i < repositories.size(); i++)
+ {
+ String repository = repositories.get(i).toString();
+ if (repository.startsWith("file://"))
+ repository = repository.substring(7);
+ else if (repository.startsWith("file:"))
+ repository = repository.substring(5);
+ else if (repository.startsWith("jndi:"))
+ repository = servletContext.getRealPath(repository.substring(5));
+ else
+ continue;
+ if (repository == null)
+ continue;
+ if (i > 0)
+ classpath.append(File.pathSeparator);
+ classpath.append(repository);
+ }
+
+ // Store the assembled class path as a servlet context attribute
+ servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+ classpath.toString());
+
+ }
+
+ /**
+ * A trival extension of URLClassLoader that uses an empty URL[] as its
+ * classpath so that all work is delegated to its parent.
+ */
+ static class ENCLoader extends URLClassLoader
+ {
+ private URL[] urllist = new URL[0];
+
+ ENCLoader(ClassLoader parent)
+ {
+ super(new URL[0], parent);
+ }
+
+ void addURLInternal(URL url)
+ {
+ URL[] result = new URL[urllist.length + 1];
+ for (int i = 0; i < urllist.length; i++)
+ result[i] = urllist[i];
+ result[urllist.length] = url;
+ urllist = result;
+ }
+
+ public URL[] getURLs()
+ {
+ return urllist;
+ }
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,807 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.jasper;
+
+import java.util.Properties;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.Map;
+import java.io.File;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.apache.jasper.Options;
+import org.apache.jasper.Constants;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.apache.jasper.compiler.TldLocationsCache;
+import org.apache.jasper.compiler.JspConfig;
+import org.apache.jasper.compiler.TagPluginManager;
+import org.apache.jasper.compiler.Localizer;
+import org.jboss.logging.Logger;
+
+/**
+ * Override the default JspServletOptions to externalize the jsp layer
+ * configuration. This overrides the default TagLibCache to the jboss version.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 41071 $
+ */
+public class JspServletOptions
+ implements Options
+{
+ static Logger log = Logger.getLogger(JspServletOptions.class);
+
+ private Properties settings = new Properties();
+
+ /**
+ * Is Jasper being used in development mode?
+ */
+ private boolean development = true;
+
+ /**
+ * Should Ant fork its java compiles of JSP pages.
+ */
+ public boolean fork = true;
+
+ /**
+ * Do you want to keep the generated Java files around?
+ */
+ private boolean keepGenerated = true;
+
+ /**
+ * Should white spaces between directives or actions be trimmed?
+ */
+ private boolean trimSpaces = false;
+
+ /**
+ * Determines whether tag handler pooling is enabled.
+ */
+ private boolean isPoolingEnabled = true;
+
+ /**
+ * Do you want support for "mapped" files? This will generate servlet that
+ * has a print statement per line of the JSP file. This seems like a really
+ * nice feature to have for debugging.
+ */
+ private boolean mappedFile = true;
+
+ /**
+ * Do you want stack traces and such displayed in the client's browser? If
+ * this is false, such messages go to the standard error or a log file if the
+ * standard error is redirected.
+ */
+ private boolean sendErrorToClient = false;
+
+ /**
+ * Do we want to include debugging information in the class file?
+ */
+ private boolean classDebugInfo = true;
+
+ /**
+ * Background compile thread check interval in seconds.
+ */
+ private int checkInterval = 0;
+
+ /**
+ * Is the generation of SMAP info for JSR45 debuggin suppressed?
+ */
+ private boolean isSmapSuppressed = false;
+
+ /**
+ * Should SMAP info for JSR45 debugging be dumped to a file?
+ */
+ private boolean isSmapDumped = false;
+
+ /**
+ * Are Text strings to be generated as char arrays?
+ */
+ private boolean genStringAsCharArray = false;
+
+ private boolean errorOnUseBeanInvalidClassAttribute = true;
+
+ /**
+ * I want to see my generated servlets. Which directory are they in?
+ */
+ private File scratchDir;
+
+ /**
+ * Need to have this as is for versions 4 and 5 of IE. Can be set from the
+ * initParams so if it changes in the future all that is needed is to have a
+ * jsp initParam of type ieClassId="<value>"
+ */
+ private String ieClassId = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93";
+
+ /**
+ * What classpath should I use while compiling generated servlets?
+ */
+ private String classpath = null;
+
+ /**
+ * Compiler to use.
+ */
+ private String compiler = null;
+
+ /**
+ * The compiler class name.
+ */
+ private String compilerClassName = null;
+
+ /**
+ * Compiler target VM.
+ */
+ private String compilerTargetVM = "1.4";
+
+ /**
+ * The compiler source VM.
+ */
+ private String compilerSourceVM = "1.4";
+
+ /**
+ * Should we include a source fragment in exception messages, which could be displayed
+ * to the developer ?
+ */
+ private boolean displaySourceFragment = true;
+
+ /**
+ * Cache for the TLD locations
+ */
+ private TldLocationsCache tldLocationsCache = null;
+
+ /**
+ * Jsp config information
+ */
+ private JspConfig jspConfig = null;
+
+ /**
+ * TagPluginManager
+ */
+ private TagPluginManager tagPluginManager = null;
+
+ /**
+ * Java platform encoding to generate the JSP page servlet.
+ */
+ private String javaEncoding = "UTF8";
+
+ /**
+ * Modification test interval.
+ */
+ private int modificationTestInterval = 4;
+
+ /**
+ * Is generation of X-Powered-By response header enabled/disabled?
+ */
+ private boolean xpoweredBy;
+
+ public String getProperty(String name)
+ {
+ return settings.getProperty(name);
+ }
+
+ public void setProperty(String name, String value)
+ {
+ if (name != null && value != null)
+ {
+ settings.setProperty(name, value);
+ }
+ }
+
+ /**
+ * Are we keeping generated code around?
+ */
+ public boolean getKeepGenerated()
+ {
+ return keepGenerated;
+ }
+
+ /**
+ * Should white spaces between directives or actions be trimmed?
+ */
+ public boolean getTrimSpaces()
+ {
+ return trimSpaces;
+ }
+
+ public boolean isPoolingEnabled()
+ {
+ return isPoolingEnabled;
+ }
+
+ /**
+ * Are we supporting HTML mapped servlets?
+ */
+ public boolean getMappedFile()
+ {
+ return mappedFile;
+ }
+
+ /**
+ * Should errors be sent to client or thrown into stderr?
+ */
+ public boolean getSendErrorToClient()
+ {
+ return sendErrorToClient;
+ }
+
+ /**
+ * Should class files be compiled with debug information?
+ */
+ public boolean getClassDebugInfo()
+ {
+ return classDebugInfo;
+ }
+
+ /**
+ * Background JSP compile thread check intervall
+ */
+ public int getCheckInterval()
+ {
+ return checkInterval;
+ }
+
+ /**
+ * Should we include a source fragment in exception messages, which could be displayed
+ * to the developer ?
+ */
+ public boolean getDisplaySourceFragment() {
+ return displaySourceFragment;
+ }
+
+ /**
+ * Modification test interval.
+ */
+ public int getModificationTestInterval()
+ {
+ return modificationTestInterval;
+ }
+
+ /**
+ * Is Jasper being used in development mode?
+ */
+ public boolean getDevelopment()
+ {
+ return development;
+ }
+
+ /**
+ * Is the generation of SMAP info for JSR45 debuggin suppressed?
+ */
+ public boolean isSmapSuppressed()
+ {
+ return isSmapSuppressed;
+ }
+
+ /**
+ * Should SMAP info for JSR45 debugging be dumped to a file?
+ */
+ public boolean isSmapDumped()
+ {
+ return isSmapDumped;
+ }
+
+ /**
+ * Are Text strings to be generated as char arrays?
+ */
+ public boolean genStringAsCharArray()
+ {
+ return this.genStringAsCharArray;
+ }
+
+ /**
+ * Class ID for use in the plugin tag when the browser is IE.
+ */
+ public String getIeClassId()
+ {
+ return ieClassId;
+ }
+
+ /**
+ * What is my scratch dir?
+ */
+ public File getScratchDir()
+ {
+ return scratchDir;
+ }
+
+ /**
+ * What classpath should I use while compiling the servlets generated from
+ * JSP files?
+ */
+ public String getClassPath()
+ {
+ return classpath;
+ }
+
+ /**
+ * Is generation of X-Powered-By response header enabled/disabled?
+ */
+ public boolean isXpoweredBy()
+ {
+ return xpoweredBy;
+ }
+
+ /**
+ * Compiler to use.
+ */
+ public String getCompiler()
+ {
+ return compiler;
+ }
+
+ /**
+ * Java compiler class to use.
+ */
+ public String getCompilerClassName() {
+ return compilerClassName;
+ }
+
+ /**
+ * @see Options#getCompilerTargetVM
+ */
+ public String getCompilerTargetVM()
+ {
+ return compilerTargetVM;
+ }
+
+ /**
+ * @see Options#getCompilerSourceVM
+ */
+ public String getCompilerSourceVM()
+ {
+ return compilerSourceVM;
+ }
+
+ public boolean getErrorOnUseBeanInvalidClassAttribute()
+ {
+ return errorOnUseBeanInvalidClassAttribute;
+ }
+
+ public void setErrorOnUseBeanInvalidClassAttribute(boolean b)
+ {
+ errorOnUseBeanInvalidClassAttribute = b;
+ }
+
+ public TldLocationsCache getTldLocationsCache()
+ {
+ return tldLocationsCache;
+ }
+
+ public void setTldLocationsCache(TldLocationsCache tldC)
+ {
+ tldLocationsCache = tldC;
+ }
+
+ public String getJavaEncoding()
+ {
+ return javaEncoding;
+ }
+
+ public boolean getFork()
+ {
+ return fork;
+ }
+
+ public JspConfig getJspConfig()
+ {
+ return jspConfig;
+ }
+
+ public TagPluginManager getTagPluginManager()
+ {
+ return tagPluginManager;
+ }
+
+ /**
+ * Is caching enabled (used for precompilation).
+ */
+ public boolean isCaching()
+ {
+ return false;
+ }
+
+ /**
+ * The web-application wide cache for the returned TreeNode
+ * by parseXMLDocument in TagLibraryInfoImpl.parseTLD,
+ * if isCaching returns true.
+ *
+ * @return the Map(String uri, TreeNode tld) instance.
+ */
+ public Map getCache()
+ {
+ return null;
+ }
+
+ /**
+ * Create an EmbeddedServletOptions object using data available from
+ * ServletConfig and ServletContext.
+ */
+ public JspServletOptions(ServletConfig config, ServletContext context)
+ {
+
+ Enumeration enumeration = config.getInitParameterNames();
+ while (enumeration.hasMoreElements())
+ {
+ String k = (String) enumeration.nextElement();
+ String v = config.getInitParameter(k);
+ setProperty(k, v);
+ }
+
+ // quick hack
+ String validating = config.getInitParameter("validating");
+ if ("false".equals(validating)) ParserUtils.validating = false;
+
+ String keepgen = config.getInitParameter("keepgenerated");
+ if (keepgen != null)
+ {
+ if (keepgen.equalsIgnoreCase("true"))
+ {
+ this.keepGenerated = true;
+ }
+ else if (keepgen.equalsIgnoreCase("false"))
+ {
+ this.keepGenerated = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.keepgen"));
+ }
+ }
+
+
+ String trimsp = config.getInitParameter("trimSpaces");
+ if (trimsp != null)
+ {
+ if (trimsp.equalsIgnoreCase("true"))
+ {
+ trimSpaces = true;
+ }
+ else if (trimsp.equalsIgnoreCase("false"))
+ {
+ trimSpaces = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.trimspaces"));
+ }
+ }
+
+ this.isPoolingEnabled = true;
+ String poolingEnabledParam
+ = config.getInitParameter("enablePooling");
+ if (poolingEnabledParam != null
+ && !poolingEnabledParam.equalsIgnoreCase("true"))
+ {
+ if (poolingEnabledParam.equalsIgnoreCase("false"))
+ {
+ this.isPoolingEnabled = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.enablePooling"));
+ }
+ }
+
+ String mapFile = config.getInitParameter("mappedfile");
+ if (mapFile != null)
+ {
+ if (mapFile.equalsIgnoreCase("true"))
+ {
+ this.mappedFile = true;
+ }
+ else if (mapFile.equalsIgnoreCase("false"))
+ {
+ this.mappedFile = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.mappedFile"));
+ }
+ }
+
+ String senderr = config.getInitParameter("sendErrToClient");
+ if (senderr != null)
+ {
+ if (senderr.equalsIgnoreCase("true"))
+ {
+ this.sendErrorToClient = true;
+ }
+ else if (senderr.equalsIgnoreCase("false"))
+ {
+ this.sendErrorToClient = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.sendErrToClient"));
+ }
+ }
+
+ String debugInfo = config.getInitParameter("classdebuginfo");
+ if (debugInfo != null)
+ {
+ if (debugInfo.equalsIgnoreCase("true"))
+ {
+ this.classDebugInfo = true;
+ }
+ else if (debugInfo.equalsIgnoreCase("false"))
+ {
+ this.classDebugInfo = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.classDebugInfo"));
+ }
+ }
+
+ String checkInterval = config.getInitParameter("checkInterval");
+ if (checkInterval != null)
+ {
+ try
+ {
+ this.checkInterval = Integer.parseInt(checkInterval);
+ if (this.checkInterval == 0)
+ {
+ this.checkInterval = 300;
+ log.warn(Localizer.getMessage("jsp.warning.checkInterval"));
+ }
+ }
+ catch (NumberFormatException ex)
+ {
+ log.warn(Localizer.getMessage("jsp.warning.checkInterval"));
+ }
+ }
+
+ String modificationTestInterval = config.getInitParameter("modificationTestInterval");
+ if (modificationTestInterval != null)
+ {
+ try
+ {
+ this.modificationTestInterval = Integer.parseInt(modificationTestInterval);
+ }
+ catch (NumberFormatException ex)
+ {
+ log.warn(Localizer.getMessage("jsp.warning.modificationTestInterval"));
+ }
+ }
+
+ String development = config.getInitParameter("development");
+ if (development != null)
+ {
+ if (development.equalsIgnoreCase("true"))
+ {
+ this.development = true;
+ }
+ else if (development.equalsIgnoreCase("false"))
+ {
+ this.development = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.development"));
+ }
+ }
+
+ String suppressSmap = config.getInitParameter("suppressSmap");
+ if (suppressSmap != null)
+ {
+ if (suppressSmap.equalsIgnoreCase("true"))
+ {
+ isSmapSuppressed = true;
+ }
+ else if (suppressSmap.equalsIgnoreCase("false"))
+ {
+ isSmapSuppressed = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.suppressSmap"));
+ }
+ }
+
+ String dumpSmap = config.getInitParameter("dumpSmap");
+ if (dumpSmap != null)
+ {
+ if (dumpSmap.equalsIgnoreCase("true"))
+ {
+ isSmapDumped = true;
+ }
+ else if (dumpSmap.equalsIgnoreCase("false"))
+ {
+ isSmapDumped = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.dumpSmap"));
+ }
+ }
+
+ String genCharArray = config.getInitParameter("genStrAsCharArray");
+ if (genCharArray != null)
+ {
+ if (genCharArray.equalsIgnoreCase("true"))
+ {
+ genStringAsCharArray = true;
+ }
+ else if (genCharArray.equalsIgnoreCase("false"))
+ {
+ genStringAsCharArray = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.genchararray"));
+ }
+ }
+
+ String errBeanClass =
+ config.getInitParameter("errorOnUseBeanInvalidClassAttribute");
+ if (errBeanClass != null)
+ {
+ if (errBeanClass.equalsIgnoreCase("true"))
+ {
+ errorOnUseBeanInvalidClassAttribute = true;
+ }
+ else if (errBeanClass.equalsIgnoreCase("false"))
+ {
+ errorOnUseBeanInvalidClassAttribute = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.errBean"));
+ }
+ }
+
+ String ieClassId = config.getInitParameter("ieClassId");
+ if (ieClassId != null)
+ this.ieClassId = ieClassId;
+
+ String classpath = config.getInitParameter("classpath");
+ if (classpath != null)
+ this.classpath = classpath;
+
+ /*
+ * scratchdir
+ */
+ String dir = config.getInitParameter("scratchdir");
+ if (dir != null)
+ {
+ scratchDir = new File(dir);
+ }
+ else
+ {
+ // First try the Servlet 2.2 javax.servlet.context.tempdir property
+ scratchDir = (File) context.getAttribute(Constants.TMP_DIR);
+ if (scratchDir == null)
+ {
+ // Not running in a Servlet 2.2 container.
+ // Try to get the JDK 1.2 java.io.tmpdir property
+ dir = System.getProperty("java.io.tmpdir");
+ if (dir != null)
+ scratchDir = new File(dir);
+ }
+ }
+ if (this.scratchDir == null)
+ {
+ log.fatal(Localizer.getMessage("jsp.error.no.scratch.dir"));
+ return;
+ }
+
+ if (!(scratchDir.exists() && scratchDir.canRead() &&
+ scratchDir.canWrite() && scratchDir.isDirectory()))
+ log.fatal(Localizer.getMessage("jsp.error.bad.scratch.dir",
+ scratchDir.getAbsolutePath()));
+
+ this.compiler = config.getInitParameter("compiler");
+
+ String compilerClassName = config.getInitParameter("compilerClassName");
+ if (compilerClassName != null)
+ {
+ this.compilerClassName = compilerClassName;
+ }
+
+ String compilerTargetVM = config.getInitParameter("compilerTargetVM");
+ if (compilerTargetVM != null)
+ {
+ this.compilerTargetVM = compilerTargetVM;
+ }
+
+ String compilerSourceVM = config.getInitParameter("compilerSourceVM");
+ if (compilerSourceVM != null)
+ {
+ this.compilerSourceVM = compilerSourceVM;
+ }
+
+ String javaEncoding = config.getInitParameter("javaEncoding");
+ if (javaEncoding != null)
+ {
+ this.javaEncoding = javaEncoding;
+ }
+
+ String displaySourceFragment = config.getInitParameter("displaySourceFragment");
+ if (displaySourceFragment != null)
+ {
+ if (displaySourceFragment.equalsIgnoreCase("true"))
+ {
+ this.displaySourceFragment = true;
+ }
+ else if (displaySourceFragment.equalsIgnoreCase("false"))
+ {
+ this.displaySourceFragment = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.displaySourceFragment"));
+ }
+ }
+
+ String fork = config.getInitParameter("fork");
+ if (fork != null)
+ {
+ if (fork.equalsIgnoreCase("true"))
+ {
+ this.fork = true;
+ }
+ else if (fork.equalsIgnoreCase("false"))
+ {
+ this.fork = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.fork"));
+ }
+ }
+
+ String xpoweredBy = config.getInitParameter("xpoweredBy");
+ if (xpoweredBy != null)
+ {
+ if (xpoweredBy.equalsIgnoreCase("true"))
+ {
+ this.xpoweredBy = true;
+ }
+ else if (xpoweredBy.equalsIgnoreCase("false"))
+ {
+ this.xpoweredBy = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.xpoweredBy"));
+ }
+ }
+
+ /* Setup the global Tag Libraries location cache for this web app. The
+ tagLibJarN entries define the jars the cache should scan.
+ */
+ String base = "tagLibJar";
+ ArrayList tldJars = new ArrayList();
+ int count = 0;
+ String jarPath = null;
+ do
+ {
+ String name = base + count;
+ jarPath = config.getInitParameter(name);
+ if( jarPath != null )
+ tldJars.add(jarPath);
+ count ++;
+ } while( jarPath != null );
+
+ tldLocationsCache = new TagLibCache(context, tldJars);
+
+ // Setup the jsp config info for this web app.
+ jspConfig = new JspConfig(context);
+
+ // Create a Tag plugin instance
+ tagPluginManager = new TagPluginManager(context);
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,446 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.jasper;
+
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.MalformedURLException;
+import javax.servlet.ServletContext;
+import java.util.Map; // SYNC
+
+import org.apache.jasper.compiler.TldLocationsCache;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.Constants;
+import org.apache.jasper.xmlparser.TreeNode;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.jboss.logging.Logger;
+
+/**
+ * A prototype TagLibCache that allows one to obtain shared tlds from the
+ * jbossweb sar conf/tlds directory.
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 41791 $
+ */
+public class TagLibCache extends TldLocationsCache
+{
+ private static final String WEB_XML = "/WEB-INF/web.xml";
+ private static final String JAR_FILE_SUFFIX = ".jar";
+
+ private static Logger log = Logger.getLogger(TagLibCache.class);
+
+ private ServletContext ctx;
+ private HashMap mappings;
+ private ArrayList tagLibJars;
+
+ public TagLibCache(ServletContext ctx, ArrayList tagLibJars)
+ {
+ super(ctx, true);
+ this.ctx = ctx;
+ this.tagLibJars = tagLibJars;
+ }
+
+ /**
+ * Gets the 'location' of the TLD associated with the given taglib 'uri'.
+ *
+ * Returns null if the uri is not associated with any tag library 'exposed'
+ * in the web application. A tag library is 'exposed' either explicitly in
+ * web.xml or implicitly via the uri tag in the TLD of a taglib deployed in a
+ * jar file (WEB-INF/lib).
+ * @param uri The taglib uri
+ * @return An array of two Strings: The first element denotes the real path
+ * to the TLD. If the path to the TLD points to a jar file, then the
+ * second element denotes the name of the TLD entry in the jar file.
+ * Returns null if the uri is not associated with any tag library
+ * 'exposed' in the web application.
+ */
+ public String[] getLocation(String uri) throws JasperException
+ {
+ if (mappings == null)
+ init();
+ String[] locations = (String[]) mappings.get(uri);
+ return locations;
+ }
+
+ private synchronized void init() throws JasperException
+ {
+ if (mappings != null)
+ {
+ return;
+ }
+
+ HashMap tmpMappings = null;
+ try
+ {
+ tmpMappings = new HashMap();
+ processWebDotXml(tmpMappings);
+ loadStandardTlds(tmpMappings);
+ processTldsInFileSystem("/WEB-INF/", tmpMappings);
+ }
+ catch (Exception ex)
+ {
+ String msg = Localizer.getMessage("jsp.error.internal.tldinit", ex.getMessage());
+ throw new JasperException(msg, ex);
+ }
+ finally
+ {
+ mappings = tmpMappings;
+ }
+ }
+
+ /*
+ * Populates taglib map described in web.xml.
+ */
+ protected void processWebDotXml(Map tmpMappings) throws Exception
+ {
+
+ InputStream is = null;
+
+ try
+ {
+ // Acquire input stream to web application deployment descriptor
+ String altDDName = (String) ctx.getAttribute(Constants.ALT_DD_ATTR);
+ if (altDDName != null)
+ {
+ try
+ {
+ is = new FileInputStream(altDDName);
+ }
+ catch (FileNotFoundException e)
+ {
+ log.warn(Localizer.getMessage("jsp.error.internal.filenotfound",
+ altDDName));
+ }
+ }
+ else
+ {
+ is = ctx.getResourceAsStream(WEB_XML);
+ if (is == null)
+ {
+ log.warn(Localizer.getMessage("jsp.error.internal.filenotfound",
+ WEB_XML));
+ }
+ }
+
+ if (is == null)
+ {
+ return;
+ }
+
+ // Parse the web application deployment descriptor
+ TreeNode webtld = null;
+ // altDDName is the absolute path of the DD
+ if (altDDName != null)
+ {
+ webtld = new ParserUtils().parseXMLDocument(altDDName, is);
+ }
+ else
+ {
+ webtld = new ParserUtils().parseXMLDocument(WEB_XML, is);
+ }
+
+ // Allow taglib to be an element of the root or jsp-config (JSP2.0)
+ TreeNode jspConfig = webtld.findChild("jsp-config");
+ if (jspConfig != null)
+ {
+ webtld = jspConfig;
+ }
+ Iterator taglibs = webtld.findChildren("taglib");
+ while (taglibs.hasNext())
+ {
+
+ // Parse the next <taglib> element
+ TreeNode taglib = (TreeNode) taglibs.next();
+ String tagUri = null;
+ String tagLoc = null;
+ TreeNode child = taglib.findChild("taglib-uri");
+ if (child != null)
+ tagUri = child.getBody();
+ child = taglib.findChild("taglib-location");
+ if (child != null)
+ tagLoc = child.getBody();
+
+ // Save this location if appropriate
+ if (tagLoc == null)
+ continue;
+ if (uriType(tagLoc) == NOROOT_REL_URI)
+ tagLoc = "/WEB-INF/" + tagLoc;
+ String tagLoc2 = null;
+ if (tagLoc.endsWith(JAR_FILE_SUFFIX))
+ {
+ tagLoc = ctx.getResource(tagLoc).toString();
+ tagLoc2 = "META-INF/taglib.tld";
+ }
+ tmpMappings.put(tagUri, new String[]{tagLoc, tagLoc2}); // SYNC
+ }
+ }
+ finally
+ {
+ if (is != null)
+ {
+ try
+ {
+ is.close();
+ }
+ catch (Throwable t)
+ {
+ }
+ }
+ }
+ }
+
+ protected void loadStandardTlds(Map tmpMappings) throws MalformedURLException // SYNC
+ {
+ if( tagLibJars.size() == 0 )
+ return;
+
+ // Locate the conf/web.xml
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ URL web = loader.getResource("conf/web.xml");
+ URL sarURL = new URL(web, "..");
+ for(int n = 0; n < tagLibJars.size(); n ++)
+ {
+ String jarPath = (String) tagLibJars.get(n);
+ try
+ {
+ URL url = new URL(sarURL, jarPath);
+ String resourcePath = url.toString();
+ log.debug("Scanning for tlds in: "+resourcePath);
+ URLConnection conn = url.openConnection();
+ conn.setUseCaches(false);
+ scanJar(conn, resourcePath, true, tmpMappings); // SYNC
+ }
+ catch (Exception e)
+ {
+ log.debug("Failed to scan: "+jarPath, e);
+ }
+ }
+ }
+
+ /*
+ * Searches the filesystem under /WEB-INF for any TLD files, and adds
+ * an implicit map entry to the taglib map for any TLD that has a <uri>
+ * element.
+ */
+ protected void processTldsInFileSystem(String startPath, Map tmpMappings) // SYNC
+ throws Exception
+ {
+
+ Set dirList = ctx.getResourcePaths(startPath);
+ if (dirList != null)
+ {
+ Iterator it = dirList.iterator();
+ while (it.hasNext())
+ {
+ String path = (String) it.next();
+ if (path.endsWith("/"))
+ {
+ processTldsInFileSystem(path, tmpMappings); // SYNC
+ }
+ if( path.endsWith(".jar") )
+ {
+ URL resURL = ctx.getResource(path);
+ URLConnection conn = resURL.openConnection();
+ conn.setUseCaches(false);
+ this.scanJar(conn, resURL.toString(), false, tmpMappings); // SYNC
+ }
+ else if ( path.endsWith(".tld") == true )
+ {
+ InputStream stream = ctx.getResourceAsStream(path);
+ String uri = null;
+ try
+ {
+ uri = getUriFromTld(path, stream);
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (Throwable t)
+ {
+ // do nothing
+ }
+ }
+ }
+ // Add implicit map entry only if its uri is not already
+ // present in the map
+ if (uri != null && tmpMappings.get(uri) == null) // SYNC
+ {
+ tmpMappings.put(uri, new String[]{path, null}); // SYNC
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Scans the given JarInputStream for TLD files located in META-INF (or a
+ * subdirectory of it), adding an implicit map entry to the taglib map for
+ * any TLD that has a <uri> element.
+ * @param conn - the
+ * @param ignore true if any exceptions raised when processing the given JAR
+ * should be ignored, false otherwise
+ */
+ private void scanJar(URLConnection conn, String resourcePath, boolean ignore, Map tmpMappings) // SYNC
+ throws JasperException, IOException
+ {
+ InputStream connIS = conn.getInputStream();
+ JarInputStream jis = new JarInputStream(connIS);
+ try
+ {
+ JarEntry entry = jis.getNextJarEntry();
+ while( entry != null )
+ {
+ String name = entry.getName();
+ if( name.endsWith(".tld") == false )
+ {
+ entry = jis.getNextJarEntry();
+ continue;
+ }
+
+ EntryInputStream eis = new EntryInputStream(jis);
+ String uri = getUriFromTld(resourcePath, eis);
+ // Add implicit map entry only if its uri is not already
+ // present in the map
+ if (uri != null && tmpMappings.get(uri) == null) // SYNC
+ {
+ tmpMappings.put(uri, new String[]{resourcePath, name}); // SYNC
+ }
+ entry = jis.getNextJarEntry();
+ }
+ }
+ catch (Exception ex)
+ {
+ if (!ignore)
+ {
+ throw new JasperException(ex);
+ }
+ }
+ finally
+ {
+ if( jis != null )
+ {
+ try
+ {
+ jis.close();
+ }
+ catch (Throwable t)
+ {
+ // ignore
+ }
+ }
+
+ if (connIS != null)
+ {
+ try
+ {
+ connIS.close();
+ }
+ catch (Throwable t)
+ {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /*
+ * Returns the value of the uri element of the given TLD, or null if the
+ * given TLD does not contain any such element.
+ */
+ private String getUriFromTld(String resourcePath, InputStream in)
+ throws JasperException
+ {
+ // Parse the tag library descriptor at the specified resource path
+ TreeNode tld = new ParserUtils().parseXMLDocument(resourcePath, in);
+ TreeNode uri = tld.findChild("uri");
+ if (uri != null)
+ {
+ String body = uri.getBody();
+ if (body != null)
+ return body;
+ }
+
+ return null;
+ }
+
+ /**
+ * Used to ignore the close on the jar entry input stream since this
+ * closes the jar stream, not just the entry.
+ */
+ static class EntryInputStream extends InputStream
+ {
+ private JarInputStream jis;
+ EntryInputStream(JarInputStream jis)
+ {
+ this.jis = jis;
+ }
+
+ public int read() throws IOException
+ {
+ return jis.read();
+ }
+
+ public int available() throws IOException
+ {
+ return jis.available();
+ }
+
+ public void close() throws IOException
+ {
+
+ }
+
+ public void reset() throws IOException
+ {
+ jis.reset();
+ }
+
+ public boolean markSupported()
+ {
+ return jis.markSupported();
+ }
+
+ public synchronized void mark(int readlimit)
+ {
+ jis.mark(readlimit);
+ }
+
+ public long skip(long n) throws IOException
+ {
+ return jis.skip(n);
+ }
+
+ public int read(byte b[], int off, int len) throws IOException
+ {
+ return jis.read(b, off, len);
+ }
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,264 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.jca;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.resource.ResourceException;
+import javax.servlet.ServletException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.resource.connectionmanager.CachedConnectionManager;
+
+/**
+ * This valve checks for unclosed connections on a servlet request
+ *
+ * @author <a href="mailto:adrian at jboss.com">Adrian Brock</a>
+ * @version $Revision: 57206 $
+ */
+public class CachedConnectionValve extends ValveBase implements Lifecycle
+{
+ /**
+ * The log
+ */
+ private static final Logger log = Logger.getLogger(CachedConnectionValve.class);
+
+ /**
+ * The info string for this Valve
+ */
+ private static final String info = "CachedConnectionValve/1.0";
+
+ /**
+ * Valve-lifecycle helper object
+ */
+ protected LifecycleSupport support = new LifecycleSupport(this);
+
+ /**
+ * The object name of the cached connection manager
+ */
+ protected String ccmName;
+
+ /**
+ * The cached connection manager
+ */
+ protected CachedConnectionManager ccm;
+
+ /**
+ * The object name of the transaction manager service
+ */
+ protected String tmName;
+
+ /**
+ * The transaction manager
+ */
+ protected TransactionManager tm;
+
+ /**
+ * The unshareable resources
+ */
+ protected Set unsharableResources = new HashSet();
+
+ /**
+ * Create a new valve
+ *
+ * @param ccm the cached connection manager for the valve
+ */
+ public CachedConnectionValve()
+ {
+ super();
+ }
+
+ /**
+ * Get information about this Valve.
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ /**
+ * Get the cached connection manager object name
+ */
+ public String getCachedConnectionManagerObjectName()
+ {
+ return ccmName;
+ }
+
+ /**
+ * Set the cached connection manager object name
+ */
+ public void setCachedConnectionManagerObjectName(String ccmName)
+ {
+ this.ccmName = ccmName;
+ }
+
+ /**
+ * Get the transaction manager object name
+ */
+ public String getTransactionManagerObjectName()
+ {
+ return tmName;
+ }
+
+ /**
+ * Set the transaction manager object name
+ */
+ public void setTransactionManagerObjectName(String tmName)
+ {
+ this.tmName = tmName;
+ }
+
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+ if(ccm == null)
+ throw new IllegalStateException("Uncomment the dependency on CachedConnectionManager"
+ + " in META-INF/jboss-service.xml of jbossweb-tomcatxxx.sar");
+ try
+ {
+ ccm.pushMetaAwareObject(this, unsharableResources);
+ try
+ {
+ getNext().invoke(request, response);
+ }
+ finally
+ {
+ try
+ {
+ ccm.popMetaAwareObject(unsharableResources);
+ }
+ finally
+ {
+ checkTransactionComplete(request);
+ }
+ }
+ }
+ catch (ResourceException e)
+ {
+ throw new ServletException("Error invoking cached connection manager", e);
+ }
+ }
+
+ // Lifecycle-interface
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ support.addLifecycleListener(listener);
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ support.removeLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return support.findLifecycleListeners();
+ }
+
+ public void start() throws LifecycleException
+ {
+ try
+ {
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ ccm = (CachedConnectionManager) server.getAttribute(new ObjectName(ccmName), "Instance");
+ tm = (TransactionManager) server.getAttribute(new ObjectName(tmName), "TransactionManager");
+ }
+ catch (Exception e)
+ {
+ throw new LifecycleException(e);
+ }
+
+ // TODO unshareable resources
+ support.fireLifecycleEvent(START_EVENT, this);
+ }
+
+ public void stop() throws LifecycleException
+ {
+ support.fireLifecycleEvent(STOP_EVENT, this);
+ unsharableResources.clear();
+ }
+
+ protected void checkTransactionComplete(Request request)
+ {
+ int status = Status.STATUS_NO_TRANSACTION;
+
+ try
+ {
+ status = tm.getStatus();
+ }
+ catch (SystemException ex)
+ {
+ log.error("Failed to get status", ex);
+ }
+
+ switch (status)
+ {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_COMMITTING:
+ case Status.STATUS_MARKED_ROLLBACK:
+ case Status.STATUS_PREPARING:
+ case Status.STATUS_ROLLING_BACK:
+ try
+ {
+ tm.rollback();
+ }
+ catch (Exception ex)
+ {
+ log.error("Failed to rollback", ex);
+ }
+ // fall through...
+ case Status.STATUS_PREPARED:
+ String servletName = "<Unknown>";
+ try
+ {
+ Wrapper servlet = request.getWrapper();
+ if (servlet != null)
+ {
+ servletName = servlet.getName();
+ if (servlet.getJspFile() != null)
+ servletName = servlet.getJspFile();
+ }
+ }
+ catch (Throwable ignored)
+ {
+ }
+
+ String msg = "Application error: " + servletName + " did not complete its transaction";
+ log.error(msg);
+ }
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+ <mbean name="CachedConnectionValve"
+ description="Valve checks for unclosed connections on a servlet request"
+ domain="Catalina"
+ group="Valve"
+ type="org.jboss.web.tomcat.service.jca.CachedConnectionValve">
+
+ <attribute name="className"
+ description="Fully qualified class name of the managed object"
+ type="java.lang.String"
+ writeable="false"/>
+
+ <attribute name="debug"
+ description="The debugging detail level for this component"
+ type="int"/>
+
+ <attribute name="containerName"
+ description="Object name of the container"
+ type="javax.management.ObjectName"/>
+
+ <attribute name="cachedConnectionManagerObjectName"
+ description="The cached connection manager"
+ type="java.lang.String"/>
+
+ </mbean>
+
+</mbeans-descriptors>
\ No newline at end of file
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,85 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.Manager;
+import org.jboss.metadata.WebMetaData;
+
+/** Common interface for the http session replication managers.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 57206 $
+ */
+public interface AbstractJBossManager extends Manager
+{
+ /** Initialize the manager with the web metadata and
+ * @param name
+ * @param webMetaData
+ * @param useJK
+ * @param useLocalCache
+ * @throws ClusteringNotSupportedException
+ */
+ public void init(String name, WebMetaData webMetaData, boolean useJK,
+ boolean useLocalCache)
+ throws ClusteringNotSupportedException;
+
+ /** The session invalidation policy. One of:
+ SESSION_INVALIDATE_ACCESS =0;
+ SESSION_INVALIDATE_SET_AND_GET =1;
+ SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET =2;
+ SESSION_INVALIDATE_SET =3;
+ * @return the invalidation policy constant
+ */
+ public int getInvalidateSessionPolicy();
+
+ /**
+ * Retrieve the JvmRoute for the enclosing Engine.
+ *
+ * @return the JvmRoute or null.
+ */
+ public String getJvmRoute();
+
+ /**
+ * Sets a new cookie for the given session id and response
+ *
+ * @param sessionId The session id
+ */
+ public void setNewSessionCookie(String sessionId, HttpServletResponse response);
+
+ /**
+ * Remove the active session locally from the manager without replicating to the cluster. This can be
+ * useful when the session is exipred, for example, where there is not need to propagate the expiration.
+ *
+ * @param session
+ */
+ public void removeLocal(Session session);
+
+ /**
+ * Store the modified session.
+ *
+ * @param session
+ */
+ public boolean storeSession(Session session);
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,31 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+/**
+ * Marker interface for use in aop annotation declaration. For example, can use this in pojo
+ * annotation instead of declaring jboss-aop.xml. For a pojo annotation declaration, just declare
+ * this in the pojo class level, and then there is a corresponding jboss-aop.xml that is needed.
+ * @author Ben Wang
+ */
+public interface AopMarker {
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,278 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+/**
+ * Implementation of a clustered session for the JBossCacheManager. The replication granularity
+ * level is attribute based; that is, we replicate only the dirty attributes.
+ * We use JBossCache for our internal, deplicated data store.
+ * The internal structure is like in JBossCache:
+ * <pre>
+ * /JSESSION
+ * /hostname
+ * /web_app_path (path + session id is unique)
+ * /id Map(id, session)
+ * (VERSION_KEY, version) // Used for version tracking. version is an Integer.
+ * /ATTRIBUTE Map(attr_key, value)
+ * </pre>
+ * <p/>
+ * Note that the isolation level of the cache dictates the
+ * concurrency behavior. Also note that session and its associated attribtues are stored in different nodes.
+ * This will be ok since cache will take care of concurrency. When replicating, we will need to replicate both
+ * session and its attributes.</p>
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 57206 $
+ */
+class AttributeBasedClusteredSession
+ extends JBossCacheClusteredSession
+{
+ static final long serialVersionUID = -5625209785550936713L;
+ /**
+ * Descriptive information describing this Session implementation.
+ */
+ protected static final String info = "AttributeBasedClusteredSession/1.0";
+
+ // Transient map to store attr changes for replication.
+ private transient Map attrModifiedMap_ = new HashMap();
+ // Note that the removed attr is intentionally stored in a map
+ // instead of a Set so it is faster to lookup and remove.
+ private transient Map attrRemovedMap_ = new HashMap();
+ private static final int REMOVE = 0; // Used to track attribute changes
+ private static final int MODIFY = 1;
+ // TODO why isn't the superclass field sufficient?
+ private transient Map attributes_ = Collections.synchronizedMap(new HashMap());
+
+ public AttributeBasedClusteredSession(JBossCacheManager manager)
+ {
+ super(manager);
+ }
+
+ // ----------------------------------------------- Overridden Public Methods
+
+
+ /**
+ * Override the superclass to additionally reset this class' fields.
+ * <p>
+ * <strong>NOTE:</strong> It is not anticipated that this method will be
+ * called on a ClusteredSession, but we are overriding the method to be
+ * thorough.
+ * </p>
+ */
+ public void recycle()
+ {
+ super.recycle();
+
+ attributes_.clear();
+ clearAttrChangedMaps();
+ }
+
+ /**
+ * Return a string representation of this object.
+ */
+ public String toString()
+ {
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("AttributeBasedClusteredSession[");
+ sb.append(super.toString());
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+ /**
+ * Overrides the superclass version to read in the attributes.
+ */
+ public synchronized void processSessionRepl()
+ {
+ // Replicate the metadata first. Note this will be lightweight since many
+ // of the fields are transient and the attribute map isn't included.
+ if (log.isTraceEnabled())
+ {
+ log.trace("processSessionRepl(): session is dirty. Will increment " +
+ "version from: " + getVersion() + " and replicate.");
+ }
+ this.incrementVersion();
+ proxy_.putSession(realId, this);
+
+ // Go thru the attribute change list
+
+ if (getSessionAttributesDirty())
+ {
+ // Go thru the modified attr list first
+ int modCount = attrModifiedMap_.size();
+ if (modCount == 1)
+ {
+ for (Iterator it = attrModifiedMap_.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Entry) it.next();
+ proxy_.putAttribute(realId, (String) entry.getKey(), entry.getValue());
+ }
+ }
+ else if (modCount > 0)
+ {
+ // It's more efficient to write a map than 2 method calls,
+ // plus it reduces the number of CacheListener notifications
+ proxy_.putAttribute(realId, attrModifiedMap_);
+ }
+
+ // Go thru the remove attr list
+ if (attrRemovedMap_.size() > 0)
+ {
+ for (Iterator it = attrRemovedMap_.keySet().iterator(); it.hasNext(); )
+ {
+ proxy_.removeAttribute(realId, (String) it.next());
+ }
+ }
+
+ clearAttrChangedMaps();
+ }
+
+ sessionAttributesDirty = false;
+ sessionMetadataDirty = false;
+
+ updateLastReplicated();
+ }
+
+ public void removeMyself()
+ {
+ // This is a shortcut to remove session and it's child attributes.
+ proxy_.removeSession(realId);
+ }
+
+ public void removeMyselfLocal()
+ {
+ // Need to evict attribute first before session to clean up everything.
+ // BRIAN -- the attributes *are* already evicted, but we leave the
+ // removeAttributesLocal call here in order to evict the ATTRIBUTE node.
+ // Otherwise empty nodes for the session root and child ATTRIBUTE will
+ // remain in the tree and screw up our list of session names.
+ proxy_.removeAttributesLocal(realId);
+ proxy_.removeSessionLocal(realId);
+ }
+
+ // ------------------------------------------------ JBoss internal abstract method
+
+ /**
+ * Populate the attributes stored in the distributed store to local transient ones.
+ */
+ protected void populateAttributes()
+ {
+ Map map = proxy_.getAttributes(realId);
+
+ // Preserve any local attributes that were excluded from replication
+ Map excluded = removeExcludedAttributes(attributes_);
+ if (excluded != null)
+ map.putAll(excluded);
+
+ attributes_ = Collections.synchronizedMap(map);
+ attrModifiedMap_.clear();
+ attrRemovedMap_.clear();
+ }
+
+ protected Object getJBossInternalAttribute(String name)
+ {
+ Object result = attributes_.get(name);
+
+ // Do dirty check even if result is null, as w/ SET_AND_GET null
+ // still makes us dirty (ensures timely replication w/o using ACCESS)
+ if (isGetDirty(result) && !replicationExcludes.contains(name))
+ {
+ attributeChanged(name, result, MODIFY);
+ }
+
+ return result;
+ }
+
+ protected Object removeJBossInternalAttribute(String name,
+ boolean localCall,
+ boolean localOnly)
+ {
+ Object result = attributes_.remove(name);
+ if (localCall && !replicationExcludes.contains(name))
+ attributeChanged(name, result, REMOVE);
+ return result;
+ }
+
+ protected Map getJBossInternalAttributes()
+ {
+ return attributes_;
+ }
+
+ protected Set getJBossInternalKeys()
+ {
+ return attributes_.keySet();
+ }
+
+ /**
+ * Method inherited from Tomcat. Return zero-length based string if not found.
+ */
+ protected String[] keys()
+ {
+ return ((String[]) getJBossInternalKeys().toArray(EMPTY_ARRAY));
+ }
+
+ protected Object setJBossInternalAttribute(String key, Object value)
+ {
+ Object old = attributes_.put(key, value);
+ if (!replicationExcludes.contains(key))
+ attributeChanged(key, value, MODIFY);
+ return old;
+ }
+
+ protected synchronized void attributeChanged(Object key, Object value, int op)
+ {
+ if (op == MODIFY)
+ {
+ if (attrRemovedMap_.containsKey(key))
+ {
+ attrRemovedMap_.remove(key);
+ }
+ attrModifiedMap_.put(key, value);
+ }
+ else if (op == REMOVE)
+ {
+ if (attrModifiedMap_.containsKey(key))
+ {
+ attrModifiedMap_.remove(key);
+ }
+ attrRemovedMap_.put(key, value);
+ }
+ sessionAttributesDirty();
+ }
+
+ protected synchronized void clearAttrChangedMaps()
+ {
+ attrRemovedMap_.clear();
+ attrModifiedMap_.clear();
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,149 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+import org.jboss.logging.Logger;
+
+/**
+ * This Valve handles batch replication mode. It uses the cache tm to performa batch replication.
+ *
+ * @author Ben Wang
+ * @version $Revision: 57206 $
+ */
+public class BatchReplicationClusteredSessionValve extends ValveBase implements Lifecycle
+{
+ private static Logger log_ = Logger.getLogger(BatchReplicationClusteredSessionValve.class);
+
+ // The info string for this Valve
+ private static final String info = "BatchReplicationClusteredSessionValve/1.0";
+
+ // Valve-lifecycle_ helper object
+ protected LifecycleSupport support = new LifecycleSupport(this);
+
+ protected JBossCacheManager manager_;
+
+ /**
+ * Create a new Valve.
+ *
+ */
+ public BatchReplicationClusteredSessionValve(AbstractJBossManager manager)
+ {
+ super();
+ manager_ = (JBossCacheManager)manager;
+ }
+
+ /**
+ * Get information about this Valve.
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ /**
+ * Valve-chain handler method.
+ * This method gets called when the request goes through the Valve-chain. Our session replication mechanism replicates the
+ * session after request got through the servlet code.
+ *
+ * @param request The request object associated with this request.
+ * @param response The response object associated with this request.
+ */
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+ // Note: we use specfically the tm in cache.
+ TransactionManager tm = manager_.getCacheService().getTransactionManager();
+ if(tm == null)
+ {
+ throw new RuntimeException("BatchReplicationClusteredSessionValve.invoke(): Obtain null tm");
+ }
+
+ // Before we start a tx, get the session. If this is a failover
+ // situation, this will cause data gravitation, which will occur
+ // thus outside of the scope of the tx we are about to start.
+ // JBossCacheManager will ensure the gravitation is in its own tx
+ request.getSession(false);
+
+ // Start a new transaction, we need transaction so all the replication are sent in batch.
+ try
+ {
+ tm.begin();
+
+ // let the servlet invocation go through
+ getNext().invoke(request, response);
+
+ log_.trace("Ready to commit batch replication for field level granularity");
+
+ tm.commit();
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ tm.rollback();
+ }
+ catch (Exception exn)
+ {
+ exn.printStackTrace();
+ }
+ // We will need to alert Tomcat of this exception.
+ throw new RuntimeException("JBossCacheManager.processSessionRepl(): failed to replicate session.", e);
+ }
+ }
+
+ // Lifecylce-interface
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ support.addLifecycleListener(listener);
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ support.removeLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return support.findLifecycleListeners();
+ }
+
+ public void start() throws LifecycleException
+ {
+ support.fireLifecycleEvent(START_EVENT, this);
+ }
+
+ public void stop() throws LifecycleException
+ {
+ support.fireLifecycleEvent(STOP_EVENT, this);
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,273 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.cache.AbstractTreeCacheListener;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+
+
+public class CacheListener extends AbstractTreeCacheListener
+{
+ // Element within an FQN that is JSESSION
+ private static final int JSESSION_FQN_INDEX = 0;
+ // Element within an FQN that is the hostname
+ private static final int HOSTNAME_FQN_INDEX = 1;
+ // ELEMENT within an FQN this is the webapp name
+ private static final int WEBAPP_FQN_INDEX = 2;
+ // Element within an FQN that is the session id
+ private static final int SESSION_ID_FQN_INDEX = 3;
+ // Size of an Fqn that points to the root of a session
+ private static final int SESSION_FQN_SIZE = SESSION_ID_FQN_INDEX + 1;
+ // Element within an FQN that is the root of a Pojo attribute map
+ private static final int POJO_ATTRIBUTE_FQN_INDEX = SESSION_ID_FQN_INDEX + 1;
+ // Element within an FQN that is the root of an individual Pojo attribute
+ private static final int POJO_KEY_FQN_INDEX = POJO_ATTRIBUTE_FQN_INDEX + 1;
+ // Size of an Fqn that points to the root of a session
+ private static final int POJO_KEY_FQN_SIZE = POJO_KEY_FQN_INDEX + 1;
+ // The index of the root of a buddy backup subtree
+ private static final int BUDDY_BACKUP_ROOT_OWNER_INDEX = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN.size();
+ // The size of the root of a buddy backup subtree (including owner)
+ private static final int BUDDY_BACKUP_ROOT_OWNER_SIZE = BUDDY_BACKUP_ROOT_OWNER_INDEX + 1;
+
+// private static final String TREE_CACHE_CLASS = "org.jboss.cache.TreeCache";
+// private static final String DATA_GRAVITATION_CLEANUP = "_dataGravitationCleanup";
+
+ private static Logger log_ = Logger.getLogger(CacheListener.class);
+ private JBossCacheWrapper cacheWrapper_;
+ private JBossCacheManager manager_;
+ private String webapp_;
+ private String hostname_;
+ private boolean fieldBased_;
+ // When trying to ignore unwanted notifications, do we check for local activity first?
+ private boolean disdainLocalActivity_;
+
+ CacheListener(JBossCacheWrapper wrapper, JBossCacheManager manager, String hostname, String webapp)
+ {
+ cacheWrapper_ = wrapper;
+ manager_ = manager;
+ hostname_ = hostname;
+ webapp_ = webapp;
+ int granularity = manager_.getReplicationGranularity();
+ fieldBased_ = (granularity == WebMetaData.REPLICATION_GRANULARITY_FIELD);
+ // TODO decide if disdaining local activity is always good for REPL_ASYNC
+ disdainLocalActivity_ = (granularity == WebMetaData.REPLICATION_GRANULARITY_SESSION);; // for now
+ }
+
+ // --------------- TreeCacheListener methods ------------------------------------
+
+ public void nodeRemoved(Fqn fqn)
+ {
+ // Ignore our own activity
+ if (SessionReplicationContext.isLocallyActive())
+ return;
+
+ boolean isBuddy = isBuddyFqn(fqn);
+
+ // Potential removal of a Pojo where we need to unregister as an Observer.
+ if (fieldBased_
+ && isFqnPojoKeySized(fqn, isBuddy)
+ && isFqnForOurWebapp(fqn, isBuddy))
+ {
+ String sessId = getIdFromFqn(fqn, isBuddy);
+ String attrKey = getPojoKeyFromFqn(fqn, isBuddy);
+ manager_.processRemoteAttributeRemoval(sessId, attrKey);
+ }
+ else if(isFqnSessionRootSized(fqn, isBuddy)
+ && isFqnForOurWebapp(fqn, isBuddy)
+// && !isDataGravitationCleanup()
+ )
+ {
+ // A session has been invalidated from another node;
+ // need to inform manager
+ String sessId = getIdFromFqn(fqn, isBuddy);
+ manager_.processRemoteInvalidation(sessId);
+ }
+ }
+
+ public void nodeModified(Fqn fqn)
+ {
+ // If checking for local activity has a higher likelihood of
+ // catching unwanted notifications than checking fqn size,
+ // do it first
+ if (disdainLocalActivity_)
+ {
+ if (SessionReplicationContext.isLocallyActive())
+ return;
+ }
+
+ boolean isBuddy = isBuddyFqn(fqn);
+ // We only care if there is a chance this is for a session root
+ if (!isFqnSessionRootSized(fqn, isBuddy))
+ return;
+
+ if (!disdainLocalActivity_)
+ {
+ if (SessionReplicationContext.isLocallyActive())
+ return;
+ }
+
+ // We only care if this is for our webapp
+ if (!isFqnForOurWebapp(fqn, isBuddy))
+ return;
+
+ // Query if we have version value in the distributed cache.
+ // If we have a version value, compare the version and invalidate if necessary.
+ Integer version = (Integer)cacheWrapper_.get(fqn, JBossCacheService.VERSION_KEY);
+ if(version != null)
+ {
+ String realId = getIdFromFqn(fqn, isBuddy);
+
+ ClusteredSession session = manager_.findLocalSession(realId);
+ if (session == null)
+ {
+ String owner = isBuddy ? getBuddyOwner(fqn) : null;
+ // Notify the manager that an unloaded session has been updated
+ manager_.unloadedSessionChanged(realId, owner);
+ }
+ else if (session.isNewData(version.intValue()))
+ {
+ // Need to invalidate the loaded session
+ session.setOutdatedVersion(version.intValue());
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("nodeDirty(): session in-memory data is " +
+ "invalidated with id: " + realId + " and version: " +
+ version.intValue());
+ }
+ }
+ else if (isBuddy)
+ {
+ // We have a local session but got a modification for the buddy tree.
+ // This means another node is in the process of taking over the session;
+ // we don't worry about it
+ ;
+ }
+ else
+ {
+ // This could be an issue but can happen legitimately in unusual
+ // circumstances, so just log something at INFO, not WARN
+
+ // Unusual circumstance: create session; don't touch session again
+ // until timeout period expired; fail over to another node after
+ // timeout but before session expiration thread has run. Existing
+ // session will be expired locally on new node and a new session created.
+ // When that session replicates, the version id will match the still
+ // existing cached session on the first node. Unlikely, but due
+ // to design of a unit test, it happens every testsuite run :-)
+ log_.info("Possible concurrency problem: Replicated version id " +
+ version + " matches in-memory version for session " + realId);
+
+ // Mark the loaded session outdated anyway; in the above mentioned
+ // "unusual circumstance" that's the correct thing to do
+ session.setOutdatedVersion(version.intValue());
+ }
+ }
+ else
+ {
+ log_.warn("No VERSION_KEY attribute found in " + fqn);
+ }
+ }
+
+ private boolean isFqnForOurWebapp(Fqn fqn, boolean isBuddy)
+ {
+ try
+ {
+ if (webapp_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + WEBAPP_FQN_INDEX : WEBAPP_FQN_INDEX))
+ && hostname_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + HOSTNAME_FQN_INDEX : HOSTNAME_FQN_INDEX))
+ && JBossCacheService.SESSION.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + JSESSION_FQN_INDEX : JSESSION_FQN_INDEX)))
+ return true;
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ // can't be ours; too small; just fall through
+ }
+
+ return false;
+ }
+
+ private static boolean isFqnSessionRootSized(Fqn fqn, boolean isBuddy)
+ {
+ return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_FQN_SIZE : SESSION_FQN_SIZE);
+ }
+
+ private static boolean isFqnPojoKeySized(Fqn fqn, boolean isBuddy)
+ {
+ return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_SIZE : POJO_KEY_FQN_SIZE);
+ }
+
+ private static String getIdFromFqn(Fqn fqn, boolean isBuddy)
+ {
+ return (String)fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_ID_FQN_INDEX : SESSION_ID_FQN_INDEX);
+ }
+
+ private static String getPojoKeyFromFqn(Fqn fqn, boolean isBuddy)
+ {
+ return (String) fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_INDEX: POJO_KEY_FQN_INDEX);
+ }
+
+ private static boolean isBuddyFqn(Fqn fqn)
+ {
+ try
+ {
+ return BuddyManager.BUDDY_BACKUP_SUBTREE.equals(fqn.get(0));
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ // Can only happen if fqn is ROOT, and we shouldn't get
+ // notifications for ROOT.
+ // If it does, just means it's not a buddy
+ return false;
+ }
+ }
+
+ /**
+ * Extracts the owner portion of an buddy subtree Fqn.
+ *
+ * @param fqn An Fqn that is a child of the buddy backup root node.
+ */
+ private static String getBuddyOwner(Fqn fqn)
+ {
+ return (String) fqn.get(BUDDY_BACKUP_ROOT_OWNER_INDEX);
+ }
+
+// /**
+// * FIXME This is a hack that examines the stack trace looking
+// * for the TreeCache._dataGravitationCleanup method.
+// *
+// * @return
+// */
+// private static boolean isDataGravitationCleanup()
+// {
+// StackTraceElement[] trace = new Throwable().getStackTrace();
+// for (int i = 0; i < trace.length; i++)
+// {
+// if (TREE_CACHE_CLASS.equals(trace[i].getClassName())
+// && DATA_GRAVITATION_CLEANUP.equals(trace[i].getMethodName()))
+// return true;
+// }
+//
+// return false;
+// }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1455 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.beans.PropertyChangeSupport;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Session;
+import org.apache.catalina.session.StandardSession;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.StringManager;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+
+/**
+ * Abstract base class for session clustering based on StandardSession. Different session
+ * replication strategy can be implemented such as session- or attribute-based ones.
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 57206 $
+ */
+abstract class ClusteredSession
+ extends StandardSession
+ implements Externalizable
+{
+ private static final long serialVersionUID = -758573655613558722L;
+ protected static Logger log = Logger.getLogger(ClusteredSession.class);
+
+ // ----------------------------------------------------- Instance Variables
+ /**
+ * Descriptive information describing this Session implementation.
+ */
+ protected static final String info = "ClusteredSession/1.0";
+
+ /**
+ * Set of attribute names which are not allowed to be replicated/persisted.
+ */
+ protected static final String[] excludedAttributes = {
+ Globals.SUBJECT_ATTR
+ };
+
+ /**
+ * Set containing all members of {@link #excludedAttributes}.
+ */
+ protected static final Set replicationExcludes;
+ static
+ {
+ HashSet set = new HashSet();
+ for (int i = 0; i < excludedAttributes.length; i++)
+ {
+ set.add(excludedAttributes[i]);
+ }
+ replicationExcludes = Collections.unmodifiableSet(set);
+ }
+
+ protected int invalidationPolicy;
+
+ /**
+ * If true, means the local in-memory session data contains
+ * changes that have not been published to the distributed cache.
+ *
+ * @deprecated not used
+ */
+ protected transient boolean isSessionModifiedSinceLastSave;
+
+ /**
+ * If true, means the local in-memory session data contains metadata
+ * changes that have not been published to the distributed cache.
+ */
+ protected transient boolean sessionMetadataDirty;
+
+ /**
+ * If true, means the local in-memory session data contains attribute
+ * changes that have not been published to the distributed cache.
+ */
+ protected transient boolean sessionAttributesDirty;
+
+ /**
+ * The last version that was passed to {@link #setOutdatedVersion} or
+ * <code>0</code> if <code>setIsOutdated(false)</code> was subsequently called.
+ */
+ protected transient int outdatedVersion;
+
+ /**
+ * The last time {@link #setIsOutdated setIsOutdated(true)} was called or
+ * <code>0</code> if <code>setIsOutdated(false)</code> was subsequently called.
+ */
+ protected transient long outdatedTime;
+
+ /**
+ * Version number to track cache invalidation. If any new version number is
+ * greater than this one, it means the data it holds is newer than this one.
+ */
+ protected int version;
+
+ /**
+ * The session's id with any jvmRoute removed.
+ */
+ protected transient String realId;
+
+ /**
+ * Whether JK is being used, in which case our realId will
+ * not match our id
+ */
+ private transient boolean useJK;
+
+ /**
+ * Timestamp when we were last replicated.
+ */
+ protected transient long lastReplicated;
+
+ /**
+ * Maximum percentage of the inactive interval this session
+ * should be allowed to go unreplicated if access to the
+ * session doesn't mark it as dirty. Drives the calculation
+ * of maxUnreplicatedInterval.
+ */
+ protected transient int maxUnreplicatedFactor = 80;
+
+ /**
+ * Maximum number of milliseconds this session
+ * should be allowed to go unreplicated if access to the
+ * session doesn't mark it as dirty.
+ */
+ protected transient long maxUnreplicatedInterval;
+
+ /**
+ * Whether any of this session's attributes implement
+ * HttpSessionActivationListener.
+ */
+ protected transient Boolean hasActivationListener;
+
+ /**
+ * Has this session only been accessed once?
+ */
+ protected transient boolean firstAccess;
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm =
+ StringManager.getManager(ClusteredSession.class.getPackage().getName());
+
+ /**
+ * Create a new ClusteredSession.
+ *
+ * @param manager the session's manager
+ *
+ * @deprecated use {@link ClusteredSession(AbstractJBossManager, boolean)}
+ */
+ protected ClusteredSession(AbstractJBossManager manager)
+ {
+ this(manager, false);
+ }
+
+ protected ClusteredSession(AbstractJBossManager manager, boolean useJK)
+ {
+ super(manager);
+ invalidationPolicy = manager.getInvalidateSessionPolicy();
+ this.useJK = useJK;
+ this.firstAccess = true;
+ calcMaxUnreplicatedInterval();
+ }
+
+ /**
+ * Check to see if the session data is still valid. Outdated here means
+ * that the in-memory data is not in sync with one in the data store.
+ *
+ * @return
+ */
+ public boolean isOutdated()
+ {
+ return thisAccessedTime < outdatedTime;
+ }
+
+ /**
+ * Marks this session as outdated or up-to-date vis-a-vis the distributed
+ * cache.
+ *
+ * @param outdated
+ *
+ * @deprecated use {@link #setOutdatedVersion(int)} and {@link #clearOutdated()}
+ */
+ public void setIsOutdated(boolean outdated)
+ {
+ if (outdated)
+ outdatedTime = System.currentTimeMillis();
+ else
+ clearOutdated();
+ }
+
+ public void setOutdatedVersion(int version)
+ {
+ this.outdatedVersion = version;
+ outdatedTime = System.currentTimeMillis();
+ }
+
+ public void clearOutdated()
+ {
+ // Only overwrite the access time if access() hasn't been called
+ // since setOutdatedVersion() was called
+ if (outdatedTime > thisAccessedTime)
+ {
+ lastAccessedTime = thisAccessedTime;
+ thisAccessedTime = outdatedTime;
+ }
+ outdatedTime = 0;
+
+ // Only overwrite the version if the outdated version is greater
+ // Otherwise when we first unmarshal a session that has been
+ // replicated many times, we will reset the version to 0
+ if (outdatedVersion > version)
+ version = outdatedVersion;
+
+ outdatedVersion = 0;
+ }
+
+ public void updateAccessTimeFromOutdatedTime()
+ {
+ if (outdatedTime > thisAccessedTime)
+ {
+ lastAccessedTime = thisAccessedTime;
+ thisAccessedTime = outdatedTime;
+ }
+ outdatedTime = 0;
+ }
+
+ /**
+ * Gets the session id with any appended jvmRoute info removed.
+ *
+ * @see #getUseJK()
+ */
+ public String getRealId()
+ {
+ return realId;
+ }
+
+ private void parseRealId(String sessionId)
+ {
+ String newId = null;
+ if (useJK)
+ newId = Util.getRealId(sessionId);
+ else
+ newId = sessionId;
+
+ // realId is used in a lot of map lookups, so only replace it
+ // if the new id is actually different -- preserve object identity
+ if (!newId.equals(realId))
+ realId = newId;
+ }
+
+ /**
+ * This is called specifically for failover case using mod_jk where the new
+ * session has this node name in there. As a result, it is safe to just
+ * replace the id since the backend store is using the "real" id
+ * without the node name.
+ *
+ * @param id
+ */
+ public void resetIdWithRouteInfo(String id)
+ {
+ this.id = id;
+ parseRealId(id);
+ }
+
+ public boolean getUseJK()
+ {
+ return useJK;
+ }
+
+ /**
+ * Check to see if the input version number is greater than I am. If it is,
+ * it means we will need to invalidate the in-memory cache.
+ * @param version
+ * @return
+ */
+ public boolean isNewData(int version)
+ {
+ return (this.version < version);
+ }
+
+ public int getVersion()
+ {
+ return version;
+ }
+
+ public void setVersion(int version)
+ {
+ this.version = version;
+ }
+
+ /**
+ * There are couple ways to generate this version number.
+ * But we will stick with the simple one of incrementing for now.
+ *
+ * @return the new version
+ */
+ public int incrementVersion()
+ {
+ return version++;
+ }
+
+ /**
+ * Gets the maximum percentage of the <code>maxInactiveInterval</code>
+ * beyond which a session should be replicated upon access even if it
+ * isn't dirty. Used to ensure that even a read-only session gets
+ * replicated before it expires, so that it isn't removed from other
+ * nodes.
+ *
+ * @return an int between 1 and 100, or -1 if replicating on access is
+ * disabled
+ */
+ public int getMaxUnreplicatedFactor()
+ {
+ return maxUnreplicatedFactor;
+ }
+
+ /**
+ * Sets the maximum percentage of the <code>maxInactiveInterval</code>
+ * beyond which a session should be replicated upon access even if it
+ * isn't dirty. Used to ensure that even a read-only session gets
+ * replicated before it expires, so that it isn't removed from other
+ * nodes.
+ *
+ * @param maxUnreplicatedFactor an int between 1 and 100, or -1 to
+ * disable replicating on access
+ *
+ * @throws IllegalArgumentException if the factor isn't -1 or between
+ * 1 and 100
+ */
+ public void setMaxUnreplicatedFactor(int factor)
+ {
+ if ((factor != -1 && factor < 1) || factor > 100)
+ throw new IllegalArgumentException("Invalid factor " + factor +
+ " -- must be between 1 and 100 or -1");
+ this.maxUnreplicatedFactor = factor;
+ calcMaxUnreplicatedInterval();
+ }
+
+
+ /**
+ * Overrides the superclass to calculate
+ * {@link #getMaxUnreplicatedInterval() maxUnreplicatedInterval}.
+ */
+ public void setMaxInactiveInterval(int interval)
+ {
+ super.setMaxInactiveInterval(interval);
+ calcMaxUnreplicatedInterval();
+ sessionMetadataDirty();
+ }
+
+ /**
+ * Gets the time {@link #updateLastReplicated()} was last called, or
+ * <code>0</code> if it has never been called.
+ */
+ public long getLastReplicated()
+ {
+ return lastReplicated;
+ }
+
+ /**
+ * Sets the {@link #getLastReplicated() lastReplicated} field to
+ * the current time.
+ */
+ public void updateLastReplicated()
+ {
+ lastReplicated = System.currentTimeMillis();
+ }
+
+ public long getMaxUnreplicatedInterval()
+ {
+ return maxUnreplicatedInterval;
+ }
+
+ public boolean getExceedsMaxUnreplicatedInterval()
+ {
+ boolean result = false;
+
+ if (maxUnreplicatedInterval > 0) // -1 means ignore; 0 means expire now
+ {
+ result = ((System.currentTimeMillis() - lastReplicated) >= maxUnreplicatedInterval);
+ }
+
+ return result;
+ }
+
+ private void calcMaxUnreplicatedInterval()
+ {
+ if (maxInactiveInterval < 0 || maxUnreplicatedFactor < 0)
+ maxUnreplicatedInterval = -1;
+ else
+ maxUnreplicatedInterval = maxInactiveInterval * maxUnreplicatedFactor / 100;
+ }
+
+ /**
+ * This is called after loading a session to initialize the transient values.
+ *
+ * @param manager
+ */
+ public abstract void initAfterLoad(AbstractJBossManager manager);
+
+ /**
+ * Propogate session to the internal store.
+ */
+ public abstract void processSessionRepl();
+
+ /**
+ * Remove myself from the internal store.
+ */
+ public abstract void removeMyself();
+
+ /**
+ * Remove myself from the <t>local</t> internal store.
+ */
+ public abstract void removeMyselfLocal();
+
+
+ // ----------------------------------------------- Overridden Public Methods
+
+ public void access()
+ {
+ super.access();
+
+ // JBAS-3528. If it's not the first access, make sure
+ // the 'new' flag is correct
+ if (!firstAccess && isNew)
+ {
+ setNew(false);
+ }
+
+ if (invalidationPolicy == WebMetaData.SESSION_INVALIDATE_ACCESS)
+ {
+ this.sessionMetadataDirty();
+ }
+ }
+
+
+ public void endAccess()
+ {
+ super.endAccess();
+
+ if (firstAccess)
+ {
+ firstAccess = false;
+ // Tomcat marks the session as non new, but that's not really
+ // accurate per SRV.7.2, as the second request hasn't come in yet
+ // So, we fix that
+ isNew = true;
+ }
+ }
+
+ public Object getAttribute(String name)
+ {
+
+ if (!isValid())
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.getAttribute.ise"));
+
+ return getAttributeInternal(name);
+ }
+
+ public Enumeration getAttributeNames()
+ {
+ if (!isValid())
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.getAttributeNames.ise"));
+
+ return (new Enumerator(getAttributesInternal().keySet(), true));
+ }
+
+ public void setAttribute(String name, Object value)
+ {
+ // Name cannot be null
+ if (name == null)
+ throw new IllegalArgumentException
+ (sm.getString("clusteredSession.setAttribute.namenull"));
+
+ // Null value is the same as removeAttribute()
+ if (value == null)
+ {
+ removeAttribute(name);
+ return;
+ }
+
+ // Validate our current state
+ if (!isValid())
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.setAttribute.ise"));
+ if ((manager != null) && manager.getDistributable() &&
+ !(canAttributeBeReplicated(value)))
+ throw new IllegalArgumentException
+ (sm.getString("clusteredSession.setAttribute.iae"));
+
+ // Construct an event with the new value
+ HttpSessionBindingEvent event = null;
+
+ // Call the valueBound() method if necessary
+ if (value instanceof HttpSessionBindingListener)
+ {
+ event = new HttpSessionBindingEvent(getSession(), name, value);
+ try
+ {
+ ((HttpSessionBindingListener) value).valueBound(event);
+ }
+ catch (Throwable t)
+ {
+ manager.getContainer().getLogger().error(sm.getString("standardSession.bindingEvent"), t);
+ }
+ }
+
+ // Replace or add this attribute
+ Object unbound = setInternalAttribute(name, value);
+
+ // Call the valueUnbound() method if necessary
+ if ((unbound != null) && (unbound != value) &&
+ (unbound instanceof HttpSessionBindingListener))
+ {
+ try
+ {
+ ((HttpSessionBindingListener) unbound).valueUnbound
+ (new HttpSessionBindingEvent(getSession(), name));
+ }
+ catch (Throwable t)
+ {
+ manager.getContainer().getLogger().error(sm.getString("standardSession.bindingEvent"), t);
+ }
+ }
+
+ // Notify interested application event listeners
+ Context context = (Context) manager.getContainer();
+ Object listeners[] = context.getApplicationEventListeners();
+ if (listeners == null)
+ return;
+ for (int i = 0; i < listeners.length; i++)
+ {
+ if (!(listeners[i] instanceof HttpSessionAttributeListener))
+ continue;
+ HttpSessionAttributeListener listener =
+ (HttpSessionAttributeListener) listeners[i];
+ try
+ {
+ if (unbound != null)
+ {
+ fireContainerEvent(context,
+ "beforeSessionAttributeReplaced",
+ listener);
+ if (event == null)
+ {
+ event = new HttpSessionBindingEvent
+ (getSession(), name, unbound);
+ }
+ listener.attributeReplaced(event);
+ fireContainerEvent(context,
+ "afterSessionAttributeReplaced",
+ listener);
+ }
+ else
+ {
+ fireContainerEvent(context,
+ "beforeSessionAttributeAdded",
+ listener);
+ if (event == null)
+ {
+ event = new HttpSessionBindingEvent
+ (getSession(), name, value);
+ }
+ listener.attributeAdded(event);
+ fireContainerEvent(context,
+ "afterSessionAttributeAdded",
+ listener);
+ }
+ }
+ catch (Throwable t)
+ {
+ try
+ {
+ if (unbound != null)
+ {
+ fireContainerEvent(context,
+ "afterSessionAttributeReplaced",
+ listener);
+ }
+ else
+ {
+ fireContainerEvent(context,
+ "afterSessionAttributeAdded",
+ listener);
+ }
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
+ }
+ }
+ }
+
+
+ /**
+ * Returns whether the attribute's type is one that can be replicated.
+ *
+ * @param attribute the attribute
+ * @return <code>true</code> if <code>attribute</code> is <code>null</code>,
+ * <code>Serializable</code> or an array of primitives.
+ */
+ protected boolean canAttributeBeReplicated(Object attribute)
+ {
+ if (attribute instanceof Serializable || attribute == null)
+ return true;
+ Class clazz = attribute.getClass().getComponentType();
+ return (clazz != null && clazz.isPrimitive());
+ }
+
+ /**
+ * Invalidates this session and unbinds any objects bound to it.
+ * Overridden here to remove across the cluster instead of just expiring.
+ *
+ * @exception IllegalStateException if this method is called on
+ * an invalidated session
+ */
+ public void invalidate()
+ {
+ if (!isValid())
+ throw new IllegalStateException(sm.getString("clusteredSession.invalidate.ise"));
+
+ // Cause this session to expire globally
+ boolean notify = true;
+ boolean localCall = true;
+ boolean localOnly = false;
+ expire(notify, localCall, localOnly);
+ }
+
+
+ /**
+ * Overrides the {@link StandardSession#isValid() superclass method}
+ * to call {@ #isValid(boolean) isValid(true)}.
+ */
+ public boolean isValid()
+ {
+ return isValid(true);
+ }
+
+ /**
+ * Returns whether the current session is still valid, but
+ * only calls {@link #expire(boolean)} for timed-out sessions
+ * if <code>expireIfInvalid</code> is <code>true</code>.
+ *
+ * @param expireIfInvalid <code>true</code> if sessions that have
+ * been timed out should be expired
+ */
+ public boolean isValid(boolean expireIfInvalid)
+ {
+ if (this.expiring)
+ {
+ return true;
+ }
+
+ if (!this.isValid)
+ {
+ return false;
+ }
+
+ if (ACTIVITY_CHECK && accessCount.get() > 0)
+ {
+ return true;
+ }
+
+ if (maxInactiveInterval >= 0)
+ {
+ long timeNow = System.currentTimeMillis();
+ int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);
+ if (timeIdle >= maxInactiveInterval)
+ {
+ if (expireIfInvalid)
+ expire(true);
+ else
+ return false;
+ }
+ }
+
+ return (this.isValid);
+
+ }
+
+ /**
+ * Expires the session, but in such a way that other cluster nodes
+ * are unaware of the expiration.
+ *
+ * @param notify
+ */
+ public void expire(boolean notify)
+ {
+ boolean localCall = true;
+ boolean localOnly = true;
+ expire(notify, localCall, localOnly);
+ }
+
+ /**
+ * Expires the session, notifying listeners and possibly the manager.
+ * <p>
+ * <strong>NOTE:</strong> The manager will only be notified of the expiration
+ * if <code>localCall</code> is <code>true</code>; otherwise it is the
+ * responsibility of the caller to notify the manager that the session is
+ * expired. (In the case of JBossCacheManager, it is the manager itself
+ * that makes such a call, so it of course is aware).
+ * </p>
+ *
+ * @param notify whether servlet spec listeners should be notified
+ * @param localCall <code>true</code> if this call originated due to local
+ * activity (such as a session invalidation in user code
+ * or an expiration by the local background processing
+ * thread); <code>false</code> if the expiration
+ * originated due to some kind of event notification
+ * from the cluster.
+ * @param localOnly <code>true</code> if the expiration should not be
+ * announced to the cluster, <code>false</code> if other
+ * cluster nodes should be made aware of the expiration.
+ * Only meaningful if <code>localCall</code> is
+ * <code>true</code>.
+ */
+ public void expire(boolean notify, boolean localCall, boolean localOnly)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("The session has expired with id: " + id +
+ " -- is it local? " + localOnly);
+ }
+
+ // If another thread is already doing this, stop
+ if (expiring)
+ return;
+
+ synchronized (this)
+ {
+ // If we had a race to this sync block, another thread may
+ // have already completed expiration. If so, don't do it again
+ if (!isValid)
+ return;
+
+ if (manager == null)
+ return;
+
+ expiring = true;
+
+ // Notify interested application event listeners
+ // FIXME - Assumes we call listeners in reverse order
+ Context context = (Context) manager.getContainer();
+ Object listeners[] = context.getApplicationLifecycleListeners();
+ if (notify && (listeners != null))
+ {
+ HttpSessionEvent event =
+ new HttpSessionEvent(getSession());
+ for (int i = 0; i < listeners.length; i++)
+ {
+ int j = (listeners.length - 1) - i;
+ if (!(listeners[j] instanceof HttpSessionListener))
+ continue;
+ HttpSessionListener listener =
+ (HttpSessionListener) listeners[j];
+ try
+ {
+ fireContainerEvent(context,
+ "beforeSessionDestroyed",
+ listener);
+ listener.sessionDestroyed(event);
+ fireContainerEvent(context,
+ "afterSessionDestroyed",
+ listener);
+ }
+ catch (Throwable t)
+ {
+ try
+ {
+ fireContainerEvent(context,
+ "afterSessionDestroyed",
+ listener);
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("standardSession.sessionEvent"), t);
+ }
+ }
+ }
+ if (ACTIVITY_CHECK) {
+ accessCount.set(0);
+ }
+
+ // Notify interested session event listeners.
+ if (notify)
+ {
+ fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
+ }
+
+ // JBAS-1360 -- Unbind any objects associated with this session
+ String keys[] = keys();
+ for (int i = 0; i < keys.length; i++)
+ removeAttributeInternal(keys[i], localCall, localOnly, notify);
+
+ // Remove this session from our manager's active sessions
+ removeFromManager(localCall, localOnly);
+
+ // We have completed expire of this session
+ setValid(false);
+ expiring = false;
+ }
+
+ }
+
+ /**
+ * Advise our manager to remove this expired session.
+ *
+ * @param localCall whether this call originated from local activity
+ * or from a remote invalidation. In this default
+ * implementation, this parameter is ignored.
+ * @param localOnly whether the rest of the cluster should be made aware
+ * of the removal
+ */
+ protected void removeFromManager(boolean localCall, boolean localOnly)
+ {
+ if(localOnly)
+ {
+ ((AbstractJBossManager) manager).removeLocal(this);
+ }
+ else
+ {
+ manager.remove(this);
+ }
+ }
+
+ public void passivate()
+ {
+ // Notify interested session event listeners
+ fireSessionEvent(Session.SESSION_PASSIVATED_EVENT, null);
+
+ if (hasActivationListener != Boolean.FALSE)
+ {
+ boolean hasListener = false;
+
+ // Notify ActivationListeners
+ HttpSessionEvent event = null;
+ String keys[] = keys();
+ Map attrs = getAttributesInternal();
+ for (int i = 0; i < keys.length; i++)
+ {
+ Object attribute = attrs.get(keys[i]);
+ if (attribute instanceof HttpSessionActivationListener)
+ {
+ hasListener = true;
+
+ if (event == null)
+ event = new HttpSessionEvent(getSession());
+ try
+ {
+ ((HttpSessionActivationListener)attribute).sessionWillPassivate(event);
+ }
+ catch (Throwable t)
+ {
+ manager.getContainer().getLogger().error
+ (sm.getString("clusteredSession.attributeEvent"), t);
+ }
+ }
+ }
+
+ hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+ }
+ }
+
+ public void activate()
+ {
+ // Notify interested session event listeners
+ fireSessionEvent(Session.SESSION_ACTIVATED_EVENT, null);
+
+ if (hasActivationListener != Boolean.FALSE)
+ {
+ // Notify ActivationListeners
+
+ boolean hasListener = false;
+
+ HttpSessionEvent event = null;
+ String keys[] = keys();
+ Map attrs = getAttributesInternal();
+ for (int i = 0; i < keys.length; i++)
+ {
+ Object attribute = attrs.get(keys[i]);
+ if (attribute instanceof HttpSessionActivationListener)
+ {
+ hasListener = true;
+ if (event == null)
+ event = new HttpSessionEvent(getSession());
+ try
+ {
+ ((HttpSessionActivationListener)attribute).sessionDidActivate(event);
+ }
+ catch (Throwable t)
+ {
+ manager.getContainer().getLogger().error
+ (sm.getString("clusteredSession.attributeEvent"), t);
+ }
+ }
+ }
+
+ hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+ }
+ }
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// public void removeNote(String name)
+// {
+// // FormAuthenticator removes the username and password because
+// // it assumes they are not needed if the Principal is cached,
+// // but they are needed if the session fails over, so ignore
+// // the removal request.
+// // TODO discuss this on Tomcat dev list to see if a better
+// // way of handling this can be found
+// if (Constants.SESS_USERNAME_NOTE.equals(name)
+// || Constants.SESS_PASSWORD_NOTE.equals(name))
+// {
+// if (log.isDebugEnabled())
+// {
+// log.debug("removeNote(): ignoring removal of note " + name);
+// }
+// }
+// else
+// {
+// super.removeNote(name);
+// }
+//
+// }
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// public void setNote(String name, Object value)
+// {
+// super.setNote(name, value);
+//
+// if (Constants.SESS_USERNAME_NOTE.equals(name)
+// || Constants.SESS_PASSWORD_NOTE.equals(name))
+// {
+// sessionIsDirty();
+// }
+// }
+
+ /**
+ * Override the superclass to additionally reset this class' fields.
+ * <p>
+ * <strong>NOTE:</strong> It is not anticipated that this method will be
+ * called on a ClusteredSession, but we are overriding the method to be
+ * thorough.
+ * </p>
+ */
+ public void recycle()
+ {
+ super.recycle();
+
+ // Fields that the superclass isn't clearing
+ listeners.clear();
+ support = new PropertyChangeSupport(this);
+
+ invalidationPolicy = 0;
+ outdatedTime = 0;
+ outdatedVersion = 0;
+ sessionAttributesDirty = false;
+ sessionMetadataDirty = false;
+ realId = null;
+ useJK = false;
+ version = 0;
+ hasActivationListener = null;
+ lastReplicated = 0;
+ maxUnreplicatedFactor = 80;
+ calcMaxUnreplicatedInterval();
+ }
+
+ /**
+ * Set the creation time for this session. This method is called by the
+ * Manager when an existing Session instance is reused.
+ *
+ * @param time The new creation time
+ */
+ public void setCreationTime(long time)
+ {
+ super.setCreationTime(time);
+ sessionMetadataDirty();
+ }
+
+ /**
+ * Overrides the superclass method to also set the
+ * {@link #getRealId() realId} property.
+ */
+ public void setId(String id)
+ {
+ // Parse the real id first, as super.setId() calls add(),
+ // which depends on having the real id
+ parseRealId(id);
+ super.setId(id);
+ }
+
+ /**
+ * Set the authenticated Principal that is associated with this Session.
+ * This provides an <code>Authenticator</code> with a means to cache a
+ * previously authenticated Principal, and avoid potentially expensive
+ * <code>Realm.authenticate()</code> calls on every request.
+ *
+ * @param principal The new Principal, or <code>null</code> if none
+ */
+ public void setPrincipal(Principal principal)
+ {
+
+ Principal oldPrincipal = this.principal;
+ this.principal = principal;
+ support.firePropertyChange("principal", oldPrincipal, this.principal);
+
+ if ((oldPrincipal != null && !oldPrincipal.equals(principal)) ||
+ (oldPrincipal == null && principal != null))
+ sessionMetadataDirty();
+
+ }
+
+ public void setNew(boolean isNew)
+ {
+ super.setNew(isNew);
+ // Don't replicate metadata just 'cause its the second request
+ // The only effect of this is if someone besides a request
+ // deserializes metadata from the distributed cache, this
+ // field may be out of date.
+ // If a request accesses the session, the access() call will
+ // set isNew=false, so the request will see the correct value
+ // sessionMetadataDirty();
+ }
+
+ public void setValid(boolean isValid)
+ {
+ super.setValid(isValid);
+ sessionMetadataDirty();
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append("id: " +id).append(" lastAccessedTime: " +lastAccessedTime).append(
+ " version: " +version).append(" lastOutdated: " + outdatedTime);
+
+ return buf.toString();
+ }
+
+ // --------------------------------------------------------- Externalizable
+
+ /**
+ * Reads all non-transient state from the ObjectOutput <i>except
+ * the attribute map</i>. Subclasses that wish the attribute map
+ * to be read should override this method and
+ * {@link #writeExternal(ObjectOutput) writeExternal()}.
+ *
+ * <p>
+ * This method is deliberately public so it can be used to reset
+ * the internal state of a session object using serialized
+ * contents replicated from another JVM via JBossCache.
+ * </p>
+ *
+ * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+ */
+ public void readExternal(ObjectInput in)
+ throws IOException, ClassNotFoundException
+ {
+ synchronized (this)
+ {
+ // From StandardSession
+ id = in.readUTF();
+ creationTime = in.readLong();
+ lastAccessedTime = in.readLong();
+ maxInactiveInterval = in.readInt();
+ isNew = in.readBoolean();
+ isValid = in.readBoolean();
+ thisAccessedTime = in.readLong();
+
+ // From ClusteredSession
+ invalidationPolicy = in.readInt();
+ version = in.readInt();
+
+ // Get our id without any jvmRoute appended
+ parseRealId(id);
+
+ // We no longer know if we have an activationListener
+ hasActivationListener = null;
+
+ // If the session has been replicated, any subsequent
+ // access cannot be the first.
+ this.firstAccess = false;
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// // Session notes -- for FORM auth apps, allow replicated session
+// // to be used without requiring a new login
+// // We use the superclass set/removeNote calls here to bypass
+// // the custom logic we've added
+// String username = (String) in.readObject();
+// if (username != null)
+// {
+// super.setNote(Constants.SESS_USERNAME_NOTE, username);
+// }
+// else
+// {
+// super.removeNote(Constants.SESS_USERNAME_NOTE);
+// }
+// String password = (String) in.readObject();
+// if (password != null)
+// {
+// super.setNote(Constants.SESS_PASSWORD_NOTE, password);
+// }
+// else
+// {
+// super.removeNote(Constants.SESS_PASSWORD_NOTE);
+// }
+ }
+ }
+
+
+ /**
+ * Writes all non-transient state to the ObjectOutput <i>except
+ * the attribute map</i>. Subclasses that wish the attribute map
+ * to be written should override this method and append it.
+ *
+ * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+ */
+ public void writeExternal(ObjectOutput out)
+ throws IOException
+ {
+ synchronized (this)
+ {
+ // From StandardSession
+ out.writeUTF(id);
+ out.writeLong(creationTime);
+ out.writeLong(lastAccessedTime);
+ out.writeInt(maxInactiveInterval);
+ out.writeBoolean(isNew);
+ out.writeBoolean(isValid);
+ out.writeLong(thisAccessedTime);
+
+ // From ClusteredSession
+ out.writeInt(invalidationPolicy);
+ out.writeInt(version);
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// // Session notes -- for FORM auth apps, allow replicated session
+// // to be used without requiring a new login
+// String username = (String) getNote(Constants. SESS_USERNAME_NOTE);
+// log.debug(Constants.SESS_USERNAME_NOTE + " = " + username);
+// out.writeObject(username);
+// String password = (String) getNote(Constants.SESS_PASSWORD_NOTE);
+// log.debug(Constants.SESS_PASSWORD_NOTE + " = " + password);
+// out.writeObject(password);
+ }
+ }
+
+ // ----------------------------------------------------- Protected Methods
+
+ /**
+ * Removes any attribute whose name is found in {@link #excludedAttributes}
+ * from <code>attributes</code> and returns a Map of all such attributes.
+ *
+ * @param attributes source map from which excluded attributes are to be
+ * removed.
+ *
+ * @return Map that contains any attributes removed from
+ * <code>attributes</code>, or <code>null</code> if no attributes
+ * were removed.
+ */
+ protected static Map removeExcludedAttributes(Map attributes)
+ {
+ Map excluded = null;
+ for (int i = 0; i < excludedAttributes.length; i++) {
+ Object attr = attributes.remove(excludedAttributes[i]);
+ if (attr != null)
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("Excluding attribute " + excludedAttributes[i] +
+ " from replication");
+ }
+ if (excluded == null)
+ {
+ excluded = new HashMap();
+ }
+ excluded.put(excludedAttributes[i], attr);
+ }
+ }
+
+ return excluded;
+ }
+
+ // -------------------------------------- Internal protected method override
+
+ /**
+ * Method inherited from Tomcat. Return zero-length based string if not found.
+ */
+ protected String[] keys()
+ {
+ return ((String[]) getAttributesInternal().keySet().toArray(EMPTY_ARRAY));
+ }
+
+ /**
+ * Called by super.removeAttribute().
+ *
+ * @param name the attribute name
+ * @param notify <code>true</code> if listeners should be notified
+ */
+ protected void removeAttributeInternal(String name, boolean notify)
+ {
+ boolean localCall = true;
+ boolean localOnly = false;
+ removeAttributeInternal(name, localCall, localOnly, notify);
+ }
+
+ /**
+ * Remove the attribute from the local cache and possibly the distributed
+ * cache, plus notify any listeners
+ *
+ * @param name the attribute name
+ * @param localCall <code>true</code> if this call originated from local
+ * activity (e.g. a removeAttribute() in the webapp or a
+ * local session invalidation/expiration),
+ * <code>false</code> if it originated due to an remote
+ * event in the distributed cache.
+ * @param localOnly <code>true</code> if the removal should not be
+ * replicated around the cluster
+ * @param notify <code>true</code> if listeners should be notified
+ */
+ protected void removeAttributeInternal(String name,
+ boolean localCall,
+ boolean localOnly,
+ boolean notify)
+ {
+
+ // Remove this attribute from our collection
+ Object value = removeAttributeInternal(name, localCall, localOnly);
+
+ // Do we need to do valueUnbound() and attributeRemoved() notification?
+ if (!notify || (value == null))
+ {
+ return;
+ }
+
+ // Call the valueUnbound() method if necessary
+ HttpSessionBindingEvent event = null;
+ if (value instanceof HttpSessionBindingListener)
+ {
+ event = new HttpSessionBindingEvent(getSession(), name, value);
+ ((HttpSessionBindingListener) value).valueUnbound(event);
+ }
+
+ // Notify interested application event listeners
+ Context context = (Context) manager.getContainer();
+ Object listeners[] = context.getApplicationEventListeners();
+ if (listeners == null)
+ return;
+ for (int i = 0; i < listeners.length; i++)
+ {
+ if (!(listeners[i] instanceof HttpSessionAttributeListener))
+ continue;
+ HttpSessionAttributeListener listener =
+ (HttpSessionAttributeListener) listeners[i];
+ try
+ {
+ fireContainerEvent(context,
+ "beforeSessionAttributeRemoved",
+ listener);
+ if (event == null)
+ {
+ event = new HttpSessionBindingEvent
+ (getSession(), name, value);
+ }
+ listener.attributeRemoved(event);
+ fireContainerEvent(context,
+ "afterSessionAttributeRemoved",
+ listener);
+ }
+ catch (Throwable t)
+ {
+ try
+ {
+ fireContainerEvent(context,
+ "afterSessionAttributeRemoved",
+ listener);
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
+ }
+ }
+
+ }
+
+ /**
+ * Exists in this class solely to act as an API-compatible bridge to the
+ * deprecated {@link #removeJBossInternalAttribute(String)}.
+ * JBossCacheClusteredSession subclasses will override this to call their
+ * own methods that make use of localCall and localOnly
+ *
+ * @param name
+ * @param localCall
+ * @param localOnly
+ * @return
+ *
+ * @deprecated will be replaced by removeJBossInternalAttribute(String, boolean, boolean)
+ */
+ protected Object removeAttributeInternal(String name,
+ boolean localCall,
+ boolean localOnly)
+ {
+ return removeJBossInternalAttribute(name);
+ }
+
+ protected Object getAttributeInternal(String name)
+ {
+ return getJBossInternalAttribute(name);
+ }
+
+ protected Map getAttributesInternal()
+ {
+ return getJBossInternalAttributes();
+ }
+
+ protected Object setInternalAttribute(String name, Object value)
+ {
+ if (value instanceof HttpSessionActivationListener)
+ hasActivationListener = Boolean.TRUE;
+
+ return setJBossInternalAttribute(name, value);
+ }
+
+ // ------------------------------------------ JBoss internal abstract method
+
+ protected abstract Object getJBossInternalAttribute(String name);
+
+ /** @deprecated will be replaced by removeJBossInternalAttribute(String, boolean, boolean) */
+ protected abstract Object removeJBossInternalAttribute(String name);
+
+ protected abstract Map getJBossInternalAttributes();
+
+ protected abstract Object setJBossInternalAttribute(String name, Object value);
+
+ // ------------------------------------------------ Session Package Methods
+
+ protected void sessionAttributesDirty()
+ {
+ sessionAttributesDirty = true;
+ }
+
+ protected boolean getSessionAttributesDirty()
+ {
+ return sessionAttributesDirty;
+ }
+
+ protected void sessionMetadataDirty()
+ {
+ sessionMetadataDirty = true;
+ }
+
+ protected boolean getSessionMetadataDirty()
+ {
+ return sessionMetadataDirty;
+ }
+
+ /**
+ * Calls {@link #sessionAttributesDirty()} and
+ * {@link #sessionMetadataDirty()}.
+ *
+ * @deprecated use one of the more fine-grained methods.
+ */
+ protected void sessionDirty()
+ {
+ sessionAttributesDirty();
+ sessionMetadataDirty();
+ }
+
+ public boolean isSessionDirty()
+ {
+ return sessionAttributesDirty || sessionMetadataDirty;
+ }
+
+ public boolean getReplicateSessionBody()
+ {
+ return sessionMetadataDirty || getExceedsMaxUnreplicatedInterval();
+ }
+
+ protected boolean isGetDirty(Object attribute)
+ {
+ boolean result = false;
+ switch (invalidationPolicy)
+ {
+ case (WebMetaData.SESSION_INVALIDATE_SET_AND_GET):
+ result = true;
+ break;
+ case (WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET):
+ result = isMutable(attribute);
+ break;
+ default:
+ // result is false
+}
+ return result;
+ }
+
+ protected boolean isMutable(Object attribute)
+ {
+ return attribute != null &&
+ !(attribute instanceof String ||
+ attribute instanceof Number ||
+ attribute instanceof Character ||
+ attribute instanceof Boolean);
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,149 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.*;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+
+/**
+ * This Valve detects all sessions that were used in a request. All sessions are given to a snapshot
+ * manager that handles the distribution of modified sessions.
+ * <p/>
+ * TOMCAT 4.1.12 UPDATE: Added findLifecycleListeners() to comply with the latest
+ * Lifecycle interface.
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public class ClusteredSessionValve extends ValveBase implements Lifecycle
+{
+ // The info string for this Valve
+ private static final String info = "ClusteredSessionValve/1.0";
+
+ // Valve-lifecycle_ helper object
+ protected LifecycleSupport support = new LifecycleSupport(this);
+
+ /**
+ * Create a new Valve.
+ */
+ public ClusteredSessionValve()
+ {
+ super();
+ }
+
+ /**
+ * Get information about this Valve.
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ /**
+ * Valve-chain handler method.
+ * This method gets called when the request goes through the Valve-chain. Our session replication mechanism replicates the
+ * session after request got through the servlet code.
+ *
+ * @param request The request object associated with this request.
+ * @param response The response object associated with this request.
+ */
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+ // Initialize the context and store the request and response objects
+ // for any clustering code that has no direct access to these objects
+ SessionReplicationContext.enterWebapp(request, response, true);
+ try
+ {
+ // let the servlet invocation go through
+ getNext().invoke(request, response);
+ }
+ finally // We replicate no matter what
+ {
+ // --> We are now after the servlet invocation
+ try
+ {
+ SessionReplicationContext ctx = SessionReplicationContext.exitWebapp();
+
+ if (ctx.getSoleSnapshotManager() != null)
+ {
+ ctx.getSoleSnapshotManager().snapshot(ctx.getSoleSession());
+ }
+ else
+ {
+ // Cross-context request touched multiple sesssions;
+ // need to replicate them all
+ Map sessions = ctx.getCrossContextSessions();
+ if (sessions != null && sessions.size() > 0)
+ {
+ for (Iterator iter = sessions.entrySet().iterator(); iter.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ ((SnapshotManager) entry.getValue()).snapshot((ClusteredSession) entry.getKey());
+ }
+ }
+ }
+ }
+ finally
+ {
+ SessionReplicationContext.finishCacheActivity();
+ }
+
+ }
+ }
+
+ // Lifecylce-interface
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ support.addLifecycleListener(listener);
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ support.removeLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return support.findLifecycleListeners();
+ }
+
+ public void start() throws LifecycleException
+ {
+ support.fireLifecycleEvent(START_EVENT, this);
+ }
+
+ public void stop() throws LifecycleException
+ {
+ support.fireLifecycleEvent(STOP_EVENT, this);
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+
+/**
+ * This exception is thrown when the clustered HTTPSession-service
+ * is not found
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @version $Revision: 57206 $
+ */
+public class ClusteringNotSupportedException extends Exception
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -2365801349845482947L;
+
+ public ClusteringNotSupportedException(String message)
+ {
+ super(message);
+ }
+
+ public ClusteringNotSupportedException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,364 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.aspects.patterns.observable.Observer;
+import org.jboss.aspects.patterns.observable.Subject;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpSessionActivationListener;
+
+/**
+ * <p>
+ * Implementation of a clustered session for JBossCacheManager. The replication granularity
+ * level is field based; that is, we replicate only the dirty field in a POJO that is part of
+ * a session attribute. E.g., once a user do setAttribute("pojo", pojo), pojo will be monitored
+ * automatically for field changes and accessing. It offers couple of advantages:
+ * <ul>
+ * <li>pojo.setName(), for example, will only replicate the name field in the pojo. And thus is more efficient.</li>
+ * <li>If pojo has a complex object graph, we will handle that automtically providing that the
+ * children object is also aspectized.</li>
+ * </ul>
+ * Note that in current version, all the attributes and its associated childre graph objects are
+ * required to be aspectized. That is, you can't simply declare them as Serializable. This is restricted
+ * because of the marshalling/unmarshalling issue.</p>
+ *
+ * <p>We use JBossCache for our internal, replicated data store.
+ * The internal structure is like in JBossCache:
+ * <pre>
+ * /JSESSION
+ * /hostname
+ * /web_app_path (path + session id is unique)
+ * /id Map(id, session)
+ * (VERSION, version)
+ * /ATTRIBUTE Map(can be empty)
+ * /pojo Map(field name, field value) (pojo naming is by field.getName())
+ *
+ * </pre>
+ * <p/>
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 57206 $
+ */
+class FieldBasedClusteredSession
+ extends JBossCacheClusteredSession implements Observer
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8347544395334247623L;
+
+ /**
+ * Descriptive information describing this Session implementation.
+ */
+ protected static final String info = "FieldBasedClusteredSession/1.0";
+
+ protected transient Map attributes_ = Collections.synchronizedMap(new HashMap());
+
+ public FieldBasedClusteredSession(JBossCacheManager manager)
+ {
+ super(manager);
+ }
+
+ // ----------------------------------------------- Overridden Public Methods
+
+
+ /**
+ * Override the superclass to additionally reset this class' fields.
+ * <p>
+ * <strong>NOTE:</strong> It is not anticipated that this method will be
+ * called on a ClusteredSession, but we are overriding the method to be
+ * thorough.
+ * </p>
+ */
+ public void recycle()
+ {
+ super.recycle();
+
+ attributes_.clear();
+ }
+
+ /**
+ * Return a string representation of this object.
+ */
+ public String toString()
+ {
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("FieldBasedClusteredSession[");
+ sb.append(super.toString());
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+ // The superclass version of processSessionRepl is fine; it will remove
+ // the session metadata, and any attribute changes have been picked up
+ // for replication as they were made; no need to do anything here
+// public synchronized void processSessionRepl()
+// {
+// super.processSessionRepl();
+// }
+
+ public void removeMyself()
+ {
+ // This is a shortcut to remove session and it's child attributes.
+ // Note that there is no need to remove attribute first since caller
+ // will do that already.
+ proxy_.removeSession(realId);
+ }
+
+ public void removeMyselfLocal()
+ {
+ // Need to evict attribute first before session to clean up everything.
+ // Note that there is no need to remove attributes first since caller
+ // will do that already.
+ // BRIAN -- the attributes *are* already evicted, but we leave the
+ // removePojosLocal call here in order to evict the ATTRIBUTE node.
+ // Otherwise empty nodes for the session root and child ATTRIBUTE will
+ // remain in the tree and screw up our list of session names.
+ proxy_.removePojosLocal(realId);
+ proxy_.removeSessionLocal(realId);
+ }
+
+ // ------------------------------------------------ JBoss internal abstract method
+
+ /**
+ * Populate the attributes stored in the distributed store to the local
+ * transient map. Add ourself as an Observer to newly found attributes and
+ * remove ourself as an Observer to existing attributes that are no longer
+ * in the distributed store.
+ */
+ protected void populateAttributes()
+ {
+ // Preserve any local attributes that were excluded from replication
+ Map excluded = removeExcludedAttributes(attributes_);
+
+ Set keys = proxy_.getPojoKeys(realId);
+ Set oldKeys = new HashSet(attributes_.keySet());
+
+ // Since we are going to touch each attribute, might as well
+ // check if we have any HttpSessionActivationListener
+ boolean hasListener = false;
+
+ if (keys != null)
+ {
+ oldKeys.removeAll(keys); // only keys that no longer exist are left
+
+ for (Iterator it = keys.iterator(); it.hasNext(); )
+ {
+ String name = (String) it.next();
+
+ Object oldAttrib = null;
+ Object newAttrib = proxy_.getPojo(realId, name);
+ if (newAttrib != null)
+ {
+ oldAttrib = attributes_.put(name, newAttrib);
+
+ if (oldAttrib != newAttrib)
+ {
+ // Need to observe this pojo as well
+ // for any modification events.
+ proxy_.addObserver(this, newAttrib);
+
+ // Stop observing the old pojo
+ proxy_.removeObserver(this, oldAttrib); // null pojo OK :)
+ }
+
+ // Check if we have a listener
+ if (newAttrib instanceof HttpSessionActivationListener)
+ hasListener = true;
+ }
+ else
+ {
+ // This shouldn't happen -- if we had a key, newAttrib s/b not null
+
+ oldAttrib = attributes_.remove(name);
+ // Stop observing this pojo
+ proxy_.removeObserver(this, oldAttrib); // null pojo OK :)
+
+ }
+ }
+ }
+
+ hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+
+ // Cycle through remaining old keys and remove them
+ // and also remove ourself as Observer
+ for (Iterator it = oldKeys.iterator(); it.hasNext(); )
+ {
+ Object oldAttrib = attributes_.remove(it.next());
+ proxy_.removeObserver(this, oldAttrib);
+ }
+
+ // Restore any excluded attributes
+ if (excluded != null)
+ attributes.putAll(excluded);
+ }
+
+ protected Object getJBossInternalAttribute(String name)
+ {
+ // Check the local map first.
+ Object result = attributes_.get(name);
+
+ // NOTE -- we no longer check with the store. Attributes are only
+ // loaded from store during populateAttributes() call at beginning
+ // of request when we notice we are outdated.
+
+ // Do dirty check even if result is null, as w/ SET_AND_GET null
+ // still makes us dirty (ensures timely replication w/o using ACCESS)
+ if (isGetDirty(result))
+ {
+ sessionAttributesDirty();
+ }
+
+ return result;
+ }
+
+ /**
+ * Overrides the superclass to treat classes implementing Subject
+ * as "immutable", since as an Observer we will detect any changes
+ * to those types.
+ */
+ protected boolean isMutable(Object attribute)
+ {
+ boolean pojo = (attribute instanceof Subject);
+ boolean mutable = (!pojo && super.isMutable(attribute));
+ return mutable;
+ }
+
+ protected Object removeJBossInternalAttribute(String name, boolean localCall, boolean localOnly)
+ {
+ // Remove it from the underlying store
+ if (localCall && !replicationExcludes.contains(name))
+ {
+ if (localOnly)
+ proxy_.removePojoLocal(realId, name);
+ else
+ proxy_.removePojo(realId, name);
+
+ sessionAttributesDirty();
+ }
+ Object result = attributes_.remove(name);
+ if(result == null)
+ {
+ log.warn("removeJBossInternalAttribute(): null value to remove with key: "+ name);
+ return null;
+ }
+ proxy_.removeObserver(this, result);
+
+ return result;
+ }
+
+ protected Map getJBossInternalAttributes()
+ {
+ return attributes_;
+ }
+
+ protected Set getJBossInternalKeys()
+ {
+ return attributes_.keySet();
+ }
+
+ /**
+ * Method inherited from Tomcat. Return zero-length based string if not found.
+ */
+ protected String[] keys()
+ {
+ return ((String[]) getJBossInternalKeys().toArray(EMPTY_ARRAY));
+ }
+
+ /**
+ * Overrides the superclass to allow instrumented classes and
+ * non-serializable Collections and Maps.
+ */
+ protected boolean canAttributeBeReplicated(Object attribute)
+ {
+ return (Util.checkPojoType(attribute));
+ }
+
+ /**
+ * This is the hook for setAttribute. Note that in this FieldBasedClusteredSession using aop,
+ * user should not call setAttribute call too often since this will re-connect the attribute with the internal
+ * cache (and this is expensive).
+ * @param key
+ * @param value
+ * @return Object
+ */
+ protected Object setJBossInternalAttribute(String key, Object value)
+ {
+ Object oldVal = null;
+ if (!replicationExcludes.contains(key))
+ {
+ oldVal = proxy_.setPojo(realId, key, value);
+ if(oldVal != null)
+ { // We are done with the old one.
+ proxy_.removeObserver(this, oldVal);
+ }
+
+ if(value != null)
+ {
+ // Special case for Collection classes.
+ if( value instanceof Map || value instanceof Collection)
+ {
+ // We need to obtain the proxy first.
+ value = proxy_.getPojo(realId, key);
+ }
+
+ // Need to use obj since it can return as a proxy.
+ proxy_.addObserver(this, value);
+ }
+
+ // Only mark session dirty if we can replicate the attribute
+ sessionAttributesDirty();
+ }
+
+ // Still need to put it in the map to track locally.
+ oldVal = attributes_.put(key, value);
+
+ return oldVal;
+ }
+
+ /**
+ * Call back handler for the aop Subject/Observer pattern.
+ * We subscribe to the event of field write and mark ourself dirty.
+ *
+ * @param subject the object we are Observing
+ */
+ public void fireChange(Subject subject)
+ {
+ // Currently we don't care who is modified, we will simply mark session is dirty for replication purpose.
+ if(log.isTraceEnabled())
+ {
+ log.trace("fireChange(): subject has changed: " +subject);
+ }
+ sessionAttributesDirty();
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,31 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+/**
+ * Marker interface for use in aop annotation declaration. For example, can use this in pojo
+ * annotation instead of declaring jboss-aop.xml. For a pojo annotation declaration, just declare
+ * this in the pojo class level, and then there is a corresponding jboss-aop.xml that is needed.
+ * @author Ben Wang
+ */
+public interface InstanceOfAopMarker {
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+
+/**
+ * A concrete implementation of the snapshot manager interface
+ * that does instant replication of a modified session
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public class InstantSnapshotManager extends SnapshotManager
+{
+ public InstantSnapshotManager(AbstractJBossManager manager, String path)
+ {
+ super(manager, path);
+ }
+
+ /**
+ * Instant replication of the modified session
+ */
+ public void snapshot(ClusteredSession session)
+ {
+ if (session != null)
+ {
+ try
+ {
+ getManager().storeSession(session);
+ }
+ catch (Exception e)
+ {
+ getLog().warn("Failed to replicate session " + session.getIdInternal(), e);
+ }
+ }
+ }
+
+ public void start()
+ {
+ }
+
+ public void stop()
+ {
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,200 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.jboss.logging.Logger;
+
+/**
+ * A snapshot manager that collects all modified sessions over a given
+ * period of time and distributes them en bloc.
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public class IntervalSnapshotManager extends SnapshotManager implements Runnable
+{
+ static Logger log = Logger.getLogger(IntervalSnapshotManager.class);
+
+ // the interval in ms
+ protected int interval = 1000;
+
+ // the modified sessions
+ protected Set sessions = new LinkedHashSet();
+
+ // the distribute thread
+ protected Thread thread = null;
+
+ // Is session processing allowed?
+ protected boolean processingAllowed = false;
+
+ // has the thread finished?
+ protected boolean threadDone = false;
+
+ public IntervalSnapshotManager(AbstractJBossManager manager, String path)
+ {
+ super(manager, path);
+ }
+
+ public IntervalSnapshotManager(AbstractJBossManager manager, String path, int interval)
+ {
+ super(manager, path);
+ this.interval = interval;
+ }
+
+ /**
+ * Store the modified session in a hashmap for the distributor thread
+ */
+ public void snapshot(ClusteredSession session)
+ {
+ try
+ {
+ // Don't hold a ref to the session for a long time
+ synchronized (sessions)
+ {
+ sessions.add(session);
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("Failed to queue session " + session + " for replication", e);
+ }
+ }
+
+ /**
+ * Distribute all modified sessions
+ */
+ protected void processSessions()
+ {
+ ClusteredSession[] toProcess = null;
+ synchronized (sessions)
+ {
+ toProcess = new ClusteredSession[sessions.size()];
+ toProcess = (ClusteredSession[]) sessions.toArray(toProcess);
+ sessions.clear();
+ }
+
+ AbstractJBossManager mgr = getManager();
+ for (int i = 0; i < toProcess.length; i++)
+ {
+ // Confirm we haven't been stopped
+ if (!processingAllowed)
+ break;
+
+ try
+ {
+ mgr.storeSession(toProcess[i]);
+ }
+ catch (Exception e)
+ {
+ getLog().error("Caught exception processing session " + toProcess[i].getRealId(), e);
+ }
+ }
+ }
+
+ /**
+ * Start the snapshot manager
+ */
+ public void start()
+ {
+ processingAllowed = true;
+ startThread();
+ }
+
+ /**
+ * Stop the snapshot manager
+ */
+ public void stop()
+ {
+ processingAllowed = false;
+ stopThread();
+ synchronized (sessions)
+ {
+ sessions.clear();
+ }
+ }
+
+ /**
+ * Start the distributor thread
+ */
+ protected void startThread()
+ {
+ if (thread != null)
+ {
+ return;
+ }
+
+ thread = new Thread(this, "ClusteredSessionDistributor[" + getContextPath() + "]");
+ thread.setDaemon(true);
+ thread.setContextClassLoader(getManager().getContainer().getLoader().getClassLoader());
+ threadDone = false;
+ thread.start();
+ }
+
+ /**
+ * Stop the distributor thread
+ */
+ protected void stopThread()
+ {
+ if (thread == null)
+ {
+ return;
+ }
+ threadDone = true;
+ thread.interrupt();
+ try
+ {
+ thread.join();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ thread = null;
+ }
+
+ /**
+ * Thread-loop
+ */
+ public void run()
+ {
+ while (!threadDone)
+ {
+ try
+ {
+ Thread.sleep(interval);
+ processSessions();
+ }
+ catch (InterruptedException ie)
+ {
+ if (!threadDone)
+ getLog().error("Caught exception processing sessions", ie);
+ }
+ catch (Exception e)
+ {
+ getLog().error("Caught exception processing sessions", e);
+ }
+ }
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,959 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.BatchModeTransactionManagerLookup;
+import org.jboss.cache.PropertyConfigurator;
+import org.jboss.cache.aop.PojoCache;
+import org.jboss.cache.aop.PojoCacheMBean;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.web.tomcat.service.JBossWeb;
+
+/**
+ * A Tomcat <code>Cluster</code> implementation that uses a JBoss
+ * <code>TreeCache</code> to support intra-cluster session replication.
+ * <p>
+ * This class registers a <code>TreeCache</code> in JMX, making it
+ * available to other users who wish to replicate data within the cluster.
+ * </p>
+ *
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public class JBossCacheCluster
+ implements JBossCacheClusterMBean, Lifecycle
+{
+ // ------------------------------------------------------- Static Fields
+
+ protected static final String info = "JBossCacheCluster/2.1";
+
+ public static Log log = LogFactory.getLog(JBossCacheCluster.class);
+
+ public static final String DEFAULT_CLUSTER_NAME = "Tomcat-Cluster";
+
+ /** TreeCache's isolation level */
+ public static final String DEFAULT_ISOLATION_LEVEL = "REPEATABLE_READ";
+
+ /** TreeCache's cache mode */
+ public static final String DEFAULT_CACHE_MODE = "REPL_ASYNC";
+
+ /** TreeCache's lock aquisition timeout */
+ public static final long DEFAULT_LOCK_TIMEOUT = 15000;
+
+ /** TransactionManagerLookup implementation that the TreeCache should use. */
+ public static final String DEFAULT_TM_LOOKUP =
+ BatchModeTransactionManagerLookup.class.getName();
+
+ public static final String DEFAULT_CACHE_CONFIG_PATH = "conf/cluster-cache.xml";
+
+ // ------------------------------------------------------- Instance Fields
+
+ /** Parent container of this cluster. */
+ private Container container = null;
+
+ /** Our JMX Server. */
+ private MBeanServer mserver = null;
+
+ /** Name under which we are registered in JMX */
+ private ObjectName objectName = null;
+
+ /** Are we started? */
+ private boolean started = false;
+
+ /** The lifecycle event support for this component. */
+ private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+ /** Our tree cache */
+ private PojoCacheMBean treeCache = null;
+
+ /** Name under which our TreeCache is registered in JMX */
+ private String treeCacheObjectName = JBossWeb.DEFAULT_CACHE_NAME;
+
+ /** Did we create the tree cache, or was it already registered in JMX? */
+ private boolean treeCacheLocal = false;
+
+ /** Name of the tree cache's JGroups channel */
+ private String clusterName = null;
+
+ /** File name, URL or String to use to configure JGroups. */
+ private String cacheConfigPath = null;
+
+ /**
+ * Implementation of Manager to instantiate when
+ * createManager() is called.
+ */
+ private String managerClassName = JBossCacheManager.class.getName();
+
+ /** Does the Engine in which we are running use mod_jk? */
+ private boolean useJK = false;
+
+ /** Whether our Managers should use a local cache. */
+ private boolean useLocalCache = false;
+
+ /**
+ * Default replication trigger to assign to our
+ * Managers that haven't had this property set.
+ */
+ private String defaultReplicationTrigger = null;
+
+ /**
+ * Default replication granularity to assign to our Managers
+ * that haven't had this property set.
+ */
+ private String defaultReplicationGranularity = null;
+
+ /**
+ * JBossCacheManager's snapshot mode.
+ */
+ private String snapshotMode = null;
+
+ /**
+ * JBossCacheManager's snapshot interval.
+ */
+ private int snapshotInterval = 0;
+
+ /** Whether we use batch mode replication for field level granularity */
+ private boolean replicationFieldBatchMode;
+
+ // ---------------------------------------------------------- Constructors
+
+ /**
+ * Default constructor.
+ */
+ public JBossCacheCluster()
+ {
+ super();
+ }
+
+ // ------------------------------------------------------------ Properties
+
+ /**
+ * Gets a String representation of the JMX <code>ObjectName</code> under
+ * which our <code>TreeCache</code> is registered.
+ * <p>
+ * If this property is not explicitly set, the <code>TreeCache</code> will
+ * be registered under
+ * @{@link Tomcat5.DEFAULT_CACHE_NAME the default name used in
+ * embedded JBoss/Tomcat}.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public String getCacheObjectName()
+ {
+ return treeCacheObjectName;
+ }
+
+ /**
+ * Sets the JMX <code>ObjectName</code> under which our
+ * <code>TreeCache</code> is registered, if already created, or under
+ * which it should be registered if this object creates it.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setCacheObjectName(String objectName)
+ {
+ this.treeCacheObjectName = objectName;
+ }
+
+ /**
+ * Sets the name of the <code>TreeCache</code>'s JGroups channel.
+ * <p>
+ * This property is ignored if a <code>TreeCache</code> is already
+ * registered under the provided
+ * {@link #setCacheObjectName cache object name}.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public void setClusterName(String clusterName)
+ {
+ this.clusterName = clusterName;
+ }
+
+ /**
+ * Gets the filesystem path, which can either be absolute or a path
+ * relative to <code>$CATALINA_BASE</code>, where a
+ * a JBossCache configuration file can be found.
+ *
+ * @return a path, either absolute or relative to
+ * <code>$CATALINA_BASE</code>. Will return
+ * <code>null</code> if no such path was configured.
+ *
+ * @jmx.managed-attribute
+ */
+ public String getCacheConfigPath()
+ {
+ return cacheConfigPath;
+ }
+
+ /**
+ * Sets the filesystem path, which can either be absolute or a path
+ * relative to <code>$CATALINA_BASE</code>, where a
+ * a JBossCache configuration file can be found.
+ * <p>
+ * This property is ignored if a <code>TreeCache</code> is already
+ * registered under the provided
+ * {@link #setCacheObjectName cache object name}.
+ * </p>
+ *
+ * @param cacheConfigPath a path, absolute or relative to
+ * <code>$CATALINA_BASE</code>,
+ * pointing to a JBossCache configuration file.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setCacheConfigPath(String cacheConfigPath)
+ {
+ this.cacheConfigPath = cacheConfigPath;
+ }
+
+ /**
+ * Gets the name of the implementation of Manager to instantiate when
+ * createManager() is called.
+ *
+ * @jmx.managed-attribute
+ */
+ public String getManagerClassName()
+ {
+ return managerClassName;
+ }
+
+ /**
+ * Sets the name of the implementation of Manager to instantiate when
+ * createManager() is called.
+ * <p>
+ * This should be {@link JBossCacheManager} (the default) or a subclass
+ * of it.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public void setManagerClassName(String managerClassName)
+ {
+ this.managerClassName = managerClassName;
+ }
+
+ public void registerManager(Manager arg0)
+ {
+ // TODO tie this into the managerClassName
+ }
+
+ public void removeManager(Manager arg0)
+ {
+ // TODO tie this into the managerClassName
+ }
+
+ /**
+ * Gets whether the <code>Engine</code> in which we are running
+ * uses <code>mod_jk</code>.
+ *
+ * @jmx.managed-attribute
+ */
+ public boolean isUseJK()
+ {
+ return useJK;
+ }
+
+ /**
+ * Sets whether the <code>Engine</code> in which we are running
+ * uses <code>mod_jk</code>.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK = useJK;
+ }
+
+ /**
+ * Gets the <code>JBossCacheManager</code>'s <code>useLocalCache</code>
+ * property.
+ *
+ * @jmx.managed-attribute
+ */
+ public boolean isUseLocalCache()
+ {
+ return useLocalCache;
+ }
+
+ /**
+ * Sets the <code>JBossCacheManager</code>'s <code>useLocalCache</code>
+ * property.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setUseLocalCache(boolean useLocalCache)
+ {
+ this.useLocalCache = useLocalCache;
+ }
+
+ /**
+ * Gets the default granularity of session data replicated across the
+ * cluster; i.e. whether the entire session should be replicated when
+ * replication is triggered, or only modified attributes.
+ * <p>
+ * The value of this property is applied to <code>Manager</code> instances
+ * that did not have an equivalent property explicitly set in
+ * <code>context.xml</code> or <code>server.xml</code>.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public String getDefaultReplicationGranularity()
+ {
+ return defaultReplicationGranularity;
+ }
+
+ /**
+ * Sets the granularity of session data replicated across the cluster.
+ * Valid values are:
+ * <ul>
+ * <li>SESSION</li>
+ * <li>ATTRIBUTE</li>
+ * <li>FIELD</li>
+ * </ul>
+ * @jmx.managed-attribute
+ */
+ public void setDefaultReplicationGranularity(
+ String defaultReplicationGranularity)
+ {
+ this.defaultReplicationGranularity = defaultReplicationGranularity;
+ }
+
+ /**
+ * Gets the type of operations on a <code>HttpSession</code> that
+ * trigger replication.
+ * <p>
+ * The value of this property is applied to <code>Manager</code> instances
+ * that did not have an equivalent property explicitly set in
+ * <code>context.xml</code> or <code>server.xml</code>.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public String getDefaultReplicationTrigger()
+ {
+ return defaultReplicationTrigger;
+ }
+
+ /**
+ * Sets the type of operations on a <code>HttpSession</code> that
+ * trigger replication. Valid values are:
+ * <ul>
+ * <li>SET_AND_GET</li>
+ * <li>SET_AND_NON_PRIMITIVE_GET</li>
+ * <li>SET</li>
+ * </ul>
+ *
+ * @jmx.managed-attribute
+ */
+ public void setDefaultReplicationTrigger(String defaultTrigger)
+ {
+ this.defaultReplicationTrigger = defaultTrigger;
+ }
+
+ /**
+ * Gets whether Managers should use batch mode replication.
+ * Only meaningful if replication granularity is set to <code>FIELD</code>.
+ *
+ * @jmx.managed-attribute
+ */
+ public boolean getDefaultReplicationFieldBatchMode()
+ {
+ return replicationFieldBatchMode;
+ }
+
+ /**
+ * Sets whether Managers should use batch mode replication.
+ * Only meaningful if replication granularity is set to <code>FIELD</code>.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setDefaultReplicationFieldBatchMode(boolean replicationFieldBatchMode)
+ {
+ this.replicationFieldBatchMode = replicationFieldBatchMode;
+ }
+
+ /**
+ * Gets when sessions are replicated to the other nodes.
+ * The default value, "instant", synchronously replicates changes
+ * to the other nodes. In this case, the "SnapshotInterval" attribute
+ * is not used.
+ * The "interval" mode, in association with the "SnapshotInterval"
+ * attribute, indicates that Tomcat will only replicate modified
+ * sessions every "SnapshotInterval" miliseconds at most.
+ *
+ * @see #getSnapshotInterval()
+ *
+ * @jmx.managed-attribute
+ */
+ public String getSnapshotMode()
+ {
+ return snapshotMode;
+ }
+
+ /**
+ * Sets when sessions are replicated to the other nodes. Valid values are:
+ * <ul>
+ * <li>instant</li>
+ * <li>interval</li>
+ * </ul>
+ *
+ * @jmx.managed-attribute
+ */
+ public void setSnapshotMode(String snapshotMode)
+ {
+ this.snapshotMode = snapshotMode;
+ }
+
+ /**
+ * Gets how often session changes should be replicated to other nodes.
+ * Only relevant if property {@link #getSnapshotMode() snapshotMode} is
+ * set to <code>interval</code>.
+ *
+ * @return the number of milliseconds between session replications.
+ *
+ * @jmx.managed-attribute
+ */
+ public int getSnapshotInterval()
+ {
+ return snapshotInterval;
+ }
+
+ /**
+ * Sets how often session changes should be replicated to other nodes.
+ *
+ * @param snapshotInterval the number of milliseconds between
+ * session replications.
+ * @jmx.managed-attribute
+ */
+ public void setSnapshotInterval(int snapshotInterval)
+ {
+ this.snapshotInterval = snapshotInterval;
+ }
+
+ // ---------------------------------------------------------------- Cluster
+
+ /**
+ * Gets the name of the <code>TreeCache</code>'s JGroups channel.
+ *
+ * @see org.apache.catalina.Cluster#getClusterName()
+ */
+ public String getClusterName()
+ {
+ return clusterName;
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Cluster#getContainer()
+ */
+ public Container getContainer()
+ {
+ return container;
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Cluster#setContainer()
+ */
+ public void setContainer(Container container)
+ {
+ this.container = container;
+ }
+
+ /**
+ * @see org.apache.catalina.Cluster#getInfo()
+ *
+ * @jmx.managed-attribute access="read-only"
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ /**
+ * @see org.apache.catalina.Cluster#createManager(java.lang.String)
+ */
+ public Manager createManager(String name)
+ {
+ if (log.isDebugEnabled())
+ log.debug("Creating ClusterManager for context " + name
+ + " using class " + getManagerClassName());
+ Manager manager = null;
+ try
+ {
+ manager = (Manager) getClass().getClassLoader().loadClass(
+ getManagerClassName()).newInstance();
+ }
+ catch (Exception x)
+ {
+ log.error("Unable to load class for replication manager", x);
+ manager = new JBossCacheManager();
+ }
+ finally
+ {
+ manager.setDistributable(true);
+ }
+
+ if (manager instanceof JBossCacheManager)
+ {
+ configureManager((JBossCacheManager) manager);
+ }
+
+ return manager;
+ }
+
+ /**
+ * Does nothing; tracking the status of other members of the cluster is
+ * provided by the JGroups layer.
+ *
+ * @see org.apache.catalina.Cluster#backgroundProcess()
+ */
+ public void backgroundProcess()
+ {
+ ; // no-op
+ }
+
+ // --------------------------------------------- Deprecated Cluster Methods
+
+ /**
+ * Returns <code>null</code>; method is deprecated.
+ *
+ * @return <code>null</code>, always.
+ *
+ * @see org.apache.catalina.Cluster#getProtocol()
+ */
+ public String getProtocol()
+ {
+ return null;
+ }
+
+ /**
+ * Does nothing; method is deprecated.
+ *
+ * @see org.apache.catalina.Cluster#setProtocol(java.lang.String)
+ */
+ public void setProtocol(String protocol)
+ {
+ ; // no-op
+ }
+
+ /**
+ * Does nothing; method is deprecated.
+ *
+ * @see org.apache.catalina.Cluster#startContext(java.lang.String)
+ */
+ public void startContext(String contextPath) throws IOException
+ {
+ ; // no-op
+ }
+
+ /**
+ * Does nothing; method is deprecated.
+ *
+ * @see org.apache.catalina.Cluster#installContext(java.lang.String, java.net.URL)
+ */
+ public void installContext(String contextPath, URL war)
+ {
+ ; // no-op
+ }
+
+ /**
+ * Does nothing; method is deprecated.
+ *
+ * @see org.apache.catalina.Cluster#stop(java.lang.String)
+ */
+ public void stop(String contextPath) throws IOException
+ {
+ ; // no-op
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * Sets the cluster-wide properties of a <code>Manager</code> to
+ * match those of this cluster. Does not override
+ * <code>Manager</code>-specific properties with cluster-wide defaults
+ * if the <code>Manager</code>-specfic properties have already been set.
+ */
+ public void configureManager(JBossCacheManager manager)
+ {
+ manager.setSnapshotMode(snapshotMode);
+ manager.setSnapshotInterval(snapshotInterval);
+ manager.setUseJK(useJK);
+ manager.setUseLocalCache(useLocalCache);
+ manager.setCacheObjectNameString(treeCacheObjectName);
+
+ // Only set replication attributes if they were not
+ // already set via a <Manager> element in an XML config file
+
+ if (manager.getReplicationGranularityString() == null)
+ {
+ manager.setReplicationGranularityString(defaultReplicationGranularity);
+ }
+
+ if (manager.getReplicationTriggerString() == null)
+ {
+ manager.setReplicationTriggerString(defaultReplicationTrigger);
+ }
+
+ if (manager.isReplicationFieldBatchMode() == null)
+ {
+ manager.setReplicationFieldBatchMode(replicationFieldBatchMode);
+ }
+ }
+
+ // --------------------------------------------------------------- Lifecyle
+
+ /**
+ * Finds or creates a {@link TreeCache}; if created, starts the
+ * cache and registers it with our JMX server.
+ *
+ * @see org.apache.catalina.Lifecycle#start()
+ */
+ public void start() throws LifecycleException
+ {
+ if (started)
+ {
+ throw new LifecycleException("Cluster already started");
+ }
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, this);
+
+ try
+ {
+ // Tell the JBoss MBeanServerLocator utility
+ // that Tomcat's MBean server is 'jboss'
+ MBeanServerLocator.setJBoss(getMBeanServer());
+
+ // Initialize the tree cache
+ PojoCacheMBean cache = getTreeCache();
+
+ if (treeCacheLocal)
+ {
+ cache.createService();
+ cache.startService();
+ }
+
+ registerMBeans();
+
+ started = true;
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_START_EVENT, this);
+
+ }
+ catch (LifecycleException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ log.error("Unable to start cluster.", e);
+ throw new LifecycleException(e);
+ }
+ }
+
+ /**
+ * If this object created its own {@link TreeCache}, stops it
+ * and unregisters it with JMX.
+ *
+ * @see org.apache.catalina.Lifecycle#stop()
+ */
+ public void stop() throws LifecycleException
+ {
+ if (!started)
+ {
+ throw new IllegalStateException("Cluster not started");
+ }
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+
+ if (treeCacheLocal)
+ {
+ treeCache.stopService();
+ treeCache.destroyService();
+ }
+
+ started = false;
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+
+ unregisterMBeans();
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Lifecycle#addLifecycleListener()
+ */
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.addLifecycleListener(listener);
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Lifecycle#findLifecycleListeners()
+ */
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return lifecycle.findLifecycleListeners();
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Lifecycle#removeLifecycleListener()
+ */
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.removeLifecycleListener(listener);
+ }
+
+ // -------------------------------------------------------- Private Methods
+
+ /**
+ * Gets our TreeCache, either from a local reference or the JMX
+ * server. If one is not found, creates and configures it.
+ */
+ private PojoCacheMBean getTreeCache() throws Exception
+ {
+ if (treeCache == null) {
+
+ MBeanServer server = getMBeanServer();
+ ObjectName objName = new ObjectName(treeCacheObjectName);
+ if (server.isRegistered(objName))
+ {
+ // Get a proxy to the existing TreeCache
+ treeCache = (PojoCacheMBean)
+ MBeanProxyExt.create(PojoCacheMBean.class, objName);
+ }
+ else
+ {
+ // Create our own tree cache
+ treeCache = new PojoCache();
+
+ // See if there is an XML descriptor file to configure the cache
+ InputStream configIS = getCacheConfigStream();
+
+ if (configIS != null)
+ {
+ PropertyConfigurator config = new PropertyConfigurator();
+ config.configure(treeCache, configIS);
+ try
+ {
+ configIS.close();
+ }
+ catch (IOException io)
+ {
+ // ignore
+ }
+
+ if (clusterName != null)
+ {
+ // Override the XML config with the name provided in
+ // server.xml. Method setClusterName is specified in the
+ // Cluster interface, otherwise we would not do this
+ treeCache.setClusterName(clusterName);
+ }
+ }
+ else
+ {
+ // User did not try to configure the cache.
+ // Configure it using defaults. Only exception
+ // is the clusterName, which user can specify in server.xml.
+ String channelName = (clusterName == null) ? DEFAULT_CLUSTER_NAME
+ : clusterName;
+ treeCache.setClusterName(channelName);
+ treeCache.setIsolationLevel(DEFAULT_ISOLATION_LEVEL);
+ treeCache.setCacheMode(DEFAULT_CACHE_MODE);
+ treeCache.setLockAcquisitionTimeout(DEFAULT_LOCK_TIMEOUT);
+ treeCache.setTransactionManagerLookupClass(DEFAULT_TM_LOOKUP);
+ }
+
+ treeCacheLocal = true;
+ }
+ }
+ return treeCache;
+ }
+
+
+
+ private InputStream getCacheConfigStream() throws FileNotFoundException
+ {
+ boolean useDefault = (this.cacheConfigPath == null);
+ String path = (useDefault) ? DEFAULT_CACHE_CONFIG_PATH : cacheConfigPath;
+ // See if clusterProperties points to a file relative
+ // to $CATALINA_BASE
+ File file = new File(path);
+ if (!file.isAbsolute())
+ {
+ file = new File(System.getProperty("catalina.base"), path);
+ }
+
+ try
+ {
+ return new FileInputStream(file);
+ }
+ catch (FileNotFoundException fnf)
+ {
+ if (useDefault)
+ {
+ // Not a problem, just means user did not try to
+ // configure the cache. Return null and let the cache
+ // be configured from defaults.
+ return null;
+ }
+ else
+ {
+ // User provided config was invalid; throw the exception
+ log.error("No tree cache config file found at " +
+ file.getAbsolutePath());
+ throw fnf;
+ }
+ }
+ }
+
+ /**
+ * Registers this object and the tree cache (if we created it) with JMX.
+ */
+ private void registerMBeans()
+ {
+ try
+ {
+ MBeanServer server = getMBeanServer();
+
+ String domain;
+ if (container instanceof ContainerBase)
+ {
+ domain = ((ContainerBase) container).getDomain();
+ }
+ else
+ {
+ domain = server.getDefaultDomain();
+ }
+
+ String name = ":type=Cluster";
+ if (container instanceof Host) {
+ name += ",host=" + container.getName();
+ }
+ else if (container instanceof Engine)
+ {
+ name += ",engine=" + container.getName();
+ }
+
+ ObjectName clusterName = new ObjectName(domain + name);
+
+ if (server.isRegistered(clusterName))
+ {
+ log.warn("MBean " + clusterName + " already registered");
+ }
+ else
+ {
+ this.objectName = clusterName;
+ server.registerMBean(this, objectName);
+ }
+
+ if (treeCacheLocal)
+ {
+ // Register the treeCache
+ ObjectName treeCacheName = new ObjectName(treeCacheObjectName);
+ server.registerMBean(getTreeCache(), treeCacheName);
+ }
+
+ }
+ catch (Exception ex)
+ {
+ log.error(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Unregisters this object and the tree cache (if we created it) with JMX.
+ */
+ private void unregisterMBeans()
+ {
+ if (mserver != null)
+ {
+ try
+ {
+ if (objectName != null) {
+ mserver.unregisterMBean(objectName);
+ }
+ if (treeCacheLocal)
+ {
+ mserver.unregisterMBean(new ObjectName(treeCacheObjectName));
+ }
+ }
+ catch (Exception e)
+ {
+ log.error(e);
+ }
+ }
+ }
+
+ /**
+ * Get the current Catalina MBean Server.
+ *
+ * @return
+ * @throws Exception
+ */
+ private MBeanServer getMBeanServer() throws Exception
+ {
+ if (mserver == null)
+ {
+ ArrayList servers = MBeanServerFactory.findMBeanServer(null);
+ if (servers.size() > 0)
+ {
+ mserver = (MBeanServer) servers.get(0);
+ }
+ else
+ {
+ mserver = MBeanServerFactory.createMBeanServer();
+ }
+ }
+ return mserver;
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.LifecycleException;
+
+/**
+ * The MBean interface for the JBossCacheCluster.
+ *
+ * @see org.jboss.web.tomcat.tc5.JBossCacheCluster
+ *
+ * @author Brian Stansberry
+ * @version $Revision: 57206 $
+ */
+public interface JBossCacheClusterMBean extends Cluster
+{
+ public abstract boolean isUseJK();
+
+ public abstract void setUseJK(boolean useJK);
+
+ public abstract boolean isUseLocalCache();
+
+ public abstract void setUseLocalCache(boolean useLocalCache);
+
+ public abstract String getManagerClassName();
+
+ public abstract void setManagerClassName(String managerClassName);
+
+ public abstract String getDefaultReplicationGranularity();
+
+ public abstract void setDefaultReplicationGranularity(String defaultGran);
+
+ public abstract String getDefaultReplicationTrigger();
+
+ public abstract void setDefaultReplicationTrigger(String defaultTrigger);
+
+ public boolean getDefaultReplicationFieldBatchMode();
+
+ public void setDefaultReplicationFieldBatchMode(boolean replicationFieldBatchMode);
+
+ public abstract int getSnapshotInterval();
+
+ public abstract void setSnapshotInterval(int snapshotInterval);
+
+ public abstract String getSnapshotMode();
+
+ public abstract void setSnapshotMode(String snapshotMode);
+
+ public abstract String getCacheObjectName();
+
+ public abstract void setCacheObjectName(String objectName);
+
+ public abstract void start() throws LifecycleException;
+
+ /**
+ * Does nothing.
+ */
+ public abstract void stop() throws LifecycleException;
+
+}
\ No newline at end of file
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,159 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+/**
+ * Common superclass of ClusteredSession types that use JBossCache
+ * as their distributed cache.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision$
+ */
+public abstract class JBossCacheClusteredSession extends ClusteredSession
+{
+ /**
+ * Our proxy to the cache.
+ */
+ protected transient JBossCacheService proxy_;
+
+ /**
+ * Create a new JBossCacheClusteredSession.
+ *
+ * @param manager
+ * @param useJK
+ */
+ public JBossCacheClusteredSession(JBossCacheManager manager)
+ {
+ super(manager, manager.getUseJK());
+ establishProxy();
+ }
+
+ /**
+ * Initialize fields marked as transient after loading this session
+ * from the distributed store
+ *
+ * @param manager the manager for this session
+ */
+ public void initAfterLoad(AbstractJBossManager manager)
+ {
+ // Our manager and proxy may have been lost if we were recycled,
+ // so reestablish them
+ setManager(manager);
+ establishProxy();
+
+ // Since attribute map may be transient, we may need to populate it
+ // from the underlying store.
+ populateAttributes();
+
+ // Notify all attributes of type HttpSessionActivationListener (SRV 7.7.2)
+ this.activate();
+
+ // We are no longer outdated vis a vis distributed cache
+ clearOutdated();
+ }
+
+ /**
+ * Gets a reference to the JBossCacheService.
+ */
+ protected void establishProxy()
+ {
+ if (proxy_ == null)
+ {
+ proxy_ = ((JBossCacheManager) manager).getCacheService();
+
+ // still null???
+ if (proxy_ == null)
+ {
+ throw new RuntimeException("JBossCacheClusteredSession: Cache service is null.");
+ }
+ }
+ }
+
+ protected abstract void populateAttributes();
+
+ /**
+ * Override the superclass to additionally reset this class' fields.
+ * <p>
+ * <strong>NOTE:</strong> It is not anticipated that this method will be
+ * called on a ClusteredSession, but we are overriding the method to be
+ * thorough.
+ * </p>
+ */
+ public void recycle()
+ {
+ super.recycle();
+
+ proxy_ = null;
+ }
+
+ /**
+ * Increment our version and place ourself in the cache.
+ */
+ public synchronized void processSessionRepl()
+ {
+ // Replicate the session.
+ if (log.isTraceEnabled())
+ {
+ log.trace("processSessionRepl(): session is dirty. Will increment " +
+ "version from: " + getVersion() + " and replicate.");
+ }
+ this.incrementVersion();
+ proxy_.putSession(realId, this);
+
+ sessionAttributesDirty = false;
+ sessionMetadataDirty = false;
+
+ updateLastReplicated();
+ }
+
+ /**
+ * Overrides the superclass impl by doing nothing if <code>localCall</code>
+ * is <code>false</code>. The JBossCacheManager will already be aware of
+ * a remote invalidation and will handle removal itself.
+ */
+ protected void removeFromManager(boolean localCall, boolean localOnly)
+ {
+ if (localCall)
+ {
+ super.removeFromManager(localCall, localOnly);
+ }
+ }
+
+
+ protected Object removeAttributeInternal(String name, boolean localCall, boolean localOnly)
+ {
+ return removeJBossInternalAttribute(name, localCall, localOnly);
+ }
+
+ protected Object removeJBossInternalAttribute(String name)
+ {
+ throw new UnsupportedOperationException("removeJBossInternalAttribute(String) " +
+ "is not supported by JBossCacheClusteredSession; use " +
+ "removeJBossInternalAttribute(String, boolean, boolean");
+ }
+
+ protected abstract Object removeJBossInternalAttribute(String name,
+ boolean localCall,
+ boolean localOnly);
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1786 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+import javax.transaction.RollbackException;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.ContainerBase;
+import org.jboss.cache.CacheException;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.web.tomcat.service.JBossWeb;
+
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implementation of a clustered session manager for
+ * catalina using JBossCache replication.
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 57357 $
+ */
+public class JBossCacheManager
+ extends JBossManager
+ implements JBossCacheManagerMBean
+{
+
+ /**
+ * Informational name for this Catalina component
+ */
+ static final String info_ = "JBossCacheManager/1.0";
+
+ // -- Class attributes ---------------------------------
+
+ /**
+ * The transaction manager.
+ */
+ protected TransactionManager tm;
+
+ /**
+ * Proxy-object for the JBossCacheService
+ */
+ private JBossCacheService proxy_;
+
+ /**
+ * Id/timestamp of sessions in cache that we haven't loaded
+ */
+ private Map unloadedSessions_ = new ConcurrentHashMap();
+
+ /** Our TreeCache's ObjectName */
+ private String cacheObjectNameString_ = JBossWeb.DEFAULT_CACHE_NAME;
+
+ /**
+ * If set to true, will add a JvmRouteFilter to the request.
+ */
+ protected boolean useJK_ = false;
+
+ /** Are we running embedded in JBoss? */
+ private boolean embedded_ = false;
+
+ /** Our JMX Server */
+ private MBeanServer mserver_ = null;
+
+ /** Our ClusteredSessionValve's snapshot mode. */
+ private String snapshotMode_ = null;
+
+ /** Our ClusteredSessionValve's snapshot interval. */
+ private int snapshotInterval_ = 0;
+
+ /** String form of invalidateSessionPolicy_ */
+ private String replTriggerString_ = null;
+
+ /** String form of replGranularityString_ */
+ private String replGranularityString_ = null;
+
+ /**
+ * Whether we use batch mode replication for field level granularity.
+ * We store this in a Boolean rather than a primitive so JBossCacheCluster
+ * can determine if this was set via a <Manager> element.
+ */
+ private Boolean replicationFieldBatchMode_;
+
+ /** Class loader for this web app. */
+ private ClassLoader tcl_;
+
+ /**
+ * The snapshot manager we are using.
+ */
+ private SnapshotManager snapshotManager_;
+
+ // ---------------------------------------------------------- Constructors
+
+ public JBossCacheManager()
+ {
+ super();
+ }
+
+ /**
+ * Initializes this Manager when running in embedded mode.
+ * <p>
+ * <strong>NOTE:</strong> This method should not be called when
+ * running unembedded.
+ * </p>
+ */
+ public void init(String name, WebMetaData webMetaData,
+ boolean useJK, boolean useLocalCache)
+ throws ClusteringNotSupportedException
+ {
+ super.init(name, webMetaData, useJK, useLocalCache);
+ this.useJK_ = useJK;
+ this.replicationFieldBatchMode_ =
+ webMetaData.getReplicationFieldBatchMode() ? Boolean.TRUE : Boolean.FALSE;
+
+ proxy_ = new JBossCacheService(cacheObjectNameString_);
+
+ // Confirm our replication granularity is compatible with the cache
+ // Throws ISE if not
+ validateFieldMarshalling();
+
+ embedded_ = true;
+ }
+
+ // ------------------------------------------------------------- Properties
+
+ /**
+ * Gets the <code>JBossCacheService</code> through which we interact
+ * with the <code>TreeCache</code>.
+ */
+ public JBossCacheService getCacheService()
+ {
+ return proxy_;
+ }
+
+ /**
+ * Gets a String representation of the JMX <code>ObjectName</code> under
+ * which our <code>TreeCache</code> is registered.
+ */
+ public String getCacheObjectNameString()
+ {
+ return cacheObjectNameString_;
+ }
+
+ /**
+ * Sets the JMX <code>ObjectName</code> under which our
+ * <code>TreeCache</code> is registered.
+ */
+ public void setCacheObjectNameString(String treeCacheObjectName)
+ {
+ this.cacheObjectNameString_ = treeCacheObjectName;
+ }
+
+ /**
+ * Gets when sessions are replicated to the other nodes.
+ * The default value, "instant", synchronously replicates changes
+ * to the other nodes. In this case, the "SnapshotInterval" attribute
+ * is not used.
+ * The "interval" mode, in association with the "SnapshotInterval"
+ * attribute, indicates that Tomcat will only replicate modified
+ * sessions every "SnapshotInterval" miliseconds at most.
+ *
+ * @see #getSnapshotInterval()
+ */
+ public String getSnapshotMode()
+ {
+ return snapshotMode_;
+ }
+
+ /**
+ * Sets when sessions are replicated to the other nodes. Valid values are:
+ * <ul>
+ * <li>instant</li>
+ * <li>interval</li>
+ * </ul>
+ */
+ public void setSnapshotMode(String snapshotMode)
+ {
+ this.snapshotMode_ = snapshotMode;
+ }
+
+ /**
+ * Gets how often session changes should be replicated to other nodes.
+ * Only relevant if property {@link #getSnapshotMode() snapshotMode} is
+ * set to <code>interval</code>.
+ *
+ * @return the number of milliseconds between session replications.
+ */
+ public int getSnapshotInterval()
+ {
+ return snapshotInterval_;
+ }
+
+ /**
+ * Sets how often session changes should be replicated to other nodes.
+ *
+ * @param snapshotInterval the number of milliseconds between
+ * session replications.
+ */
+ public void setSnapshotInterval(int snapshotInterval)
+ {
+ this.snapshotInterval_ = snapshotInterval;
+ }
+
+ /**
+ * Gets whether the <code>Engine</code> in which we are running
+ * uses <code>mod_jk</code>.
+ */
+ public boolean getUseJK()
+ {
+ return useJK_;
+ }
+
+ /**
+ * Sets whether the <code>Engine</code> in which we are running
+ * uses <code>mod_jk</code>.
+ */
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK_ = useJK;
+ }
+
+ /**
+ * Returns the replication granularity expressed as an int.
+ *
+ * @see WebMetaData#REPLICATION_GRANULARITY_ATTRIBUTE
+ * @see WebMetaData#REPLICATION_GRANULARITY_FIELD
+ * @see WebMetaData#REPLICATION_GRANULARITY_SESSION
+ */
+ public int getReplicationGranularity()
+ {
+ return replicationGranularity_;
+ }
+
+ /**
+ * Gets the granularity of session data replicated across the
+ * cluster; i.e. whether the entire session should be replicated when
+ * replication is triggered, only modified attributes, or only
+ * modified fields of attributes.
+ */
+ public String getReplicationGranularityString()
+ {
+ // Only lazy-set this if we are started;
+ // otherwise screws up standalone TC integration!!
+ if (started_ && this.replGranularityString_ == null)
+ {
+ switch (this.replicationGranularity_)
+ {
+ case WebMetaData.REPLICATION_GRANULARITY_ATTRIBUTE:
+ this.replGranularityString_ = "ATTRIBUTE";
+ break;
+ case WebMetaData.REPLICATION_GRANULARITY_SESSION:
+ this.replGranularityString_ = "SESSION";
+ break;
+ case WebMetaData.REPLICATION_GRANULARITY_FIELD:
+ this.replGranularityString_ = "FIELD";
+ }
+ }
+ return replGranularityString_;
+ }
+
+ /**
+ * Sets the granularity of session data replicated across the cluster.
+ * Valid values are:
+ * <ul>
+ * <li>SESSION</li>
+ * <li>ATTRIBUTE</li>
+ * <li>FIELD</li>
+ * </ul>
+ */
+ public void setReplicationGranularityString(String granularity)
+ {
+ this.replGranularityString_ = granularity;
+ }
+
+ /**
+ * Gets the type of operations on a <code>HttpSession</code> that
+ * trigger replication.
+ */
+ public String getReplicationTriggerString()
+ {
+ // Only lazy-set this if we are started;
+ // otherwise screws up standalone TC integration!!
+ if (started_ && this.replTriggerString_ == null)
+ {
+ switch (this.invalidateSessionPolicy_)
+ {
+ case WebMetaData.SESSION_INVALIDATE_SET:
+ this.replTriggerString_ = "SET";
+ break;
+ case WebMetaData.SESSION_INVALIDATE_SET_AND_GET:
+ this.replTriggerString_ = "SET_AND_GET";
+ break;
+ case WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET:
+ this.replTriggerString_ = "SET_AND_NON_PRIMITIVE_GET";
+ }
+ }
+ return this.replTriggerString_;
+ }
+
+ /**
+ * Sets the type of operations on a <code>HttpSession</code> that
+ * trigger replication. Valid values are:
+ * <ul>
+ * <li>SET_AND_GET</li>
+ * <li>SET_AND_NON_PRIMITIVE_GET</li>
+ * <li>SET</li>
+ * </ul>
+ */
+ public void setReplicationTriggerString(String trigger)
+ {
+ this.replTriggerString_ = trigger;
+ }
+
+ /**
+ * Gets whether, if replication granularity is set to <code>FIELD</code>,
+ * replication should be done in batch mode. Ignored if field-level
+ * granularity is not used.
+ */
+ public Boolean isReplicationFieldBatchMode()
+ {
+ return replicationFieldBatchMode_;
+ }
+
+ /**
+ * Sets whether, if replication granularity is set to <code>FIELD</code>,
+ * replication should be done in batch mode. Ignored if field-level
+ * granularity is not used.
+ */
+ public void setReplicationFieldBatchMode(boolean replicationFieldBatchMode)
+ {
+ this.replicationFieldBatchMode_ = Boolean.valueOf(replicationFieldBatchMode);
+ }
+
+ public void setUseLocalCache(boolean useLocalCache)
+ {
+ this.useLocalCache_ = useLocalCache;
+ }
+
+ // JBossCacheManagerMBean-methods -------------------------------------
+
+ public void expireSession(String sessionId)
+ {
+ Session session = findSession(sessionId);
+ if (session != null)
+ session.expire();
+ }
+
+ public String getLastAccessedTime(String sessionId)
+ {
+ Session session = findSession(sessionId);
+ if(session == null) {
+ log_.debug("getLastAccessedTime(): Session " + sessionId +
+ " not found");
+ return "";
+ }
+ return new Date(session.getLastAccessedTime()).toString();
+ }
+
+ public Object getSessionAttribute(String sessionId, String key)
+ {
+ ClusteredSession session = (ClusteredSession) findSession(sessionId);
+ return (session == null) ? null : session.getAttribute(key);
+ }
+
+ public String getSessionAttributeString(String sessionId, String key)
+ {
+ Object attr = getSessionAttribute(sessionId, key);
+ return (attr == null) ? null : attr.toString();
+ }
+
+ public String listLocalSessionIds()
+ {
+ return reportSessionIds(sessions_.keySet());
+ }
+
+ public String listSessionIds()
+ {
+ Set ids = new HashSet(sessions_.keySet());
+ ids.addAll(unloadedSessions_.keySet());
+ return reportSessionIds(ids);
+ }
+
+ private String reportSessionIds(Set ids)
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean added = false;
+ for (Iterator it = ids.iterator(); it.hasNext(); )
+ {
+ if (added)
+ {
+ sb.append(',');
+ }
+ else
+ {
+ added = true;
+ }
+
+ sb.append(it.next());
+ }
+ return sb.toString();
+ }
+
+ // Manager-methods -------------------------------------
+
+ /**
+ * Start this Manager
+ *
+ * @throws org.apache.catalina.LifecycleException
+ *
+ */
+ public void start() throws LifecycleException
+ {
+ if (embedded_)
+ {
+ startEmbedded();
+ }
+ else
+ {
+ startUnembedded();
+ }
+ }
+
+ public void stop() throws LifecycleException
+ {
+ if (!started_)
+ {
+ throw new IllegalStateException("Manager not started");
+ }
+
+ log_.debug("Stopping");
+
+ resetStats();
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+
+ clearSessions();
+
+ // Don't leak the classloader
+ tcl_ = null;
+
+ proxy_.stop();
+ tm = null;
+
+ snapshotManager_.stop();
+
+ started_ = false;
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+
+ try
+ {
+ unregisterMBeans();
+ }
+ catch (Exception e)
+ {
+ log_.error("Could not unregister ManagerMBean from MBeanServer", e);
+ }
+ }
+
+ /**
+ * Clear the underlying cache store and also pojo that has the observers.
+ */
+ protected void clearSessions()
+ {
+ // First, the sessions we have actively loaded
+ ClusteredSession[] sessions = findLocalSessions();
+ for(int i=0; i < sessions.length; i++)
+ {
+ ClusteredSession ses = sessions[i];
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("clearSessions(): clear session by expiring: " + ses);
+ }
+ boolean notify = true;
+ boolean localCall = true;
+ boolean localOnly = true;
+ try
+ {
+ ses.expire(notify, localCall, localOnly);
+ }
+ catch (Throwable t)
+ {
+ log_.warn("clearSessions(): Caught exception expiring session " +
+ ses.getIdInternal(), t);
+ }
+ finally
+ {
+ // Guard against leaking memory if anything is holding a
+ // ref to the session by clearing its internal state
+ ses.recycle();
+ }
+ }
+
+ // Next, the local copy of the distributed cache
+ Map unloaded = new HashMap(unloadedSessions_);
+ Set keys = unloaded.keySet();
+ for (Iterator it = keys.iterator(); it.hasNext(); )
+ {
+ String realId = (String) it.next();
+ proxy_.removeSessionLocal(realId);
+ unloadedSessions_.remove(realId);
+ }
+ }
+
+ /**
+ * Create a new session with a generated id.
+ */
+ public Session createSession()
+ {
+ return createSession(null);
+ }
+
+ /**
+ * Create a new session.
+ *
+ * @param sessionId the id to use, or <code>null</code> if we should
+ * generate a new id
+ *
+ * @return the session
+ *
+ * @throws IllegalStateException if the current number of active sessions
+ * exceeds the maximum number allowed
+ */
+ public Session createSession(String sessionId)
+ {
+ // We check here for maxActive instead of in add(). add() gets called
+ // when we load an already existing session from the distributed cache
+ // (e.g. in a failover) and we don't want to fail in that situation.
+
+ // maxActive_ -1 is unlimited
+ if (maxActive_ != -1 && activeCounter_ >= maxActive_)
+ {
+ // Exceeds limit. We need to reject it.
+ rejectedCounter_++;
+ // Catalina api does not specify what happens
+ // but we will throw a runtime exception for now.
+ String msgEnd = (sessionId == null) ? "" : " id " + sessionId;
+ throw new IllegalStateException("JBossCacheManager.add(): number of " +
+ "active sessions exceeds the maximum limit: " +
+ maxActive_ + " when trying to add session" + msgEnd);
+ }
+
+ ClusteredSession session = createEmptyClusteredSession();
+
+ session.setNew(true);
+ session.setCreationTime(System.currentTimeMillis());
+ session.setMaxInactiveInterval(this.maxInactiveInterval_);
+ session.setValid(true);
+
+ if (sessionId == null)
+ {
+ sessionId = this.getNextId();
+
+ // We are using mod_jk for load balancing. Append the JvmRoute.
+ if (useJK_)
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("createSession(): useJK is true. Will append JvmRoute: " + this.getJvmRoute());
+ }
+ sessionId += "." + this.getJvmRoute();
+ }
+ }
+
+ session.setId(sessionId); // Setting the id leads to a call to add()
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Created a ClusteredSession with id: " + sessionId);
+ }
+
+ createdCounter_++;
+
+ // Add this session to the set of those potentially needing replication
+ SessionReplicationContext.bindSession(session, snapshotManager_);
+
+ return session;
+ }
+
+ public boolean storeSession(Session baseSession)
+ {
+ boolean stored = false;
+ if(baseSession != null && started_)
+ {
+ ClusteredSession session = (ClusteredSession) baseSession;
+
+ synchronized (session)
+ {
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("check to see if needs to store and replicate " +
+ "session with id " + session.getIdInternal());
+ }
+
+ if (session.isValid() &&
+ (session.isSessionDirty() || session.getExceedsMaxUnreplicatedInterval()))
+ {
+ String realId = session.getRealId();
+
+ // Notify all session attributes that they get serialized (SRV 7.7.2)
+ long begin = System.currentTimeMillis();
+ session.passivate();
+ long elapsed = System.currentTimeMillis() - begin;
+ stats_.updatePassivationStats(realId, elapsed);
+
+ // Do the actual replication
+ begin = System.currentTimeMillis();
+ processSessionRepl(session);
+ elapsed = System.currentTimeMillis() - begin;
+ stored = true;
+ stats_.updateReplicationStats(realId, elapsed);
+ }
+ }
+ }
+
+ return stored;
+ }
+
+ public void add(Session session)
+ {
+ if (session == null)
+ return;
+
+ if (!(session instanceof ClusteredSession))
+ {
+ throw new IllegalArgumentException("You can only add instances of " +
+ "type ClusteredSession to this Manager. Session class name: " +
+ session.getClass().getName());
+ }
+
+// add((ClusteredSession) session, true);
+ add((ClusteredSession) session, false);
+ }
+
+ /**
+ * Adds the given session to the collection of those being managed by this
+ * Manager.
+ *
+ * @param session the session. Cannot be <code>null</code>.
+ * @param replicate whether the session should be replicated
+ *
+ * @throws NullPointerException if <code>session</code> is <code>null</code>.
+ */
+ private void add(ClusteredSession session, boolean replicate)
+ {
+ if (!session.isValid())
+ {
+ log_.error("Cannot add session with id=" + session.getIdInternal() +
+ " because it is invalid");
+ return;
+ }
+
+ String realId = session.getRealId();
+ Object existing = sessions_.put(realId, session);
+ unloadedSessions_.remove(realId);
+
+ if (!session.equals(existing))
+ {
+ if (replicate)
+ {
+ storeSession(session);
+ }
+
+ activeCounter_++;
+ if (activeCounter_ > maxActiveCounter_)
+ maxActiveCounter_++;
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Session with id=" + session.getIdInternal() + " added. " +
+ "Current active sessions " + activeCounter_);
+ }
+ }
+ }
+
+ // Satisfy the Manager interface. Internally we use
+ // createEmptyClusteredSession to avoid a cast
+ public Session createEmptySession()
+ {
+ return createEmptyClusteredSession();
+ }
+
+ private ClusteredSession createEmptyClusteredSession()
+ {
+ log_.debug("Creating an empty ClusteredSession");
+
+ ClusteredSession session = null;
+ switch (replicationGranularity_)
+ {
+ case (WebMetaData.REPLICATION_GRANULARITY_ATTRIBUTE):
+ session = new AttributeBasedClusteredSession(this);
+ break;
+ case (WebMetaData.REPLICATION_GRANULARITY_FIELD):
+ session = new FieldBasedClusteredSession(this);
+ break;
+ default:
+ session = new SessionBasedClusteredSession(this);
+ break;
+ }
+ return session;
+ }
+
+ /**
+ * Attempts to find the session in the collection of those being managed
+ * locally, and if not found there, in the distributed cache of sessions.
+ * <p>
+ * If a session is found in the distributed cache, it is added to the
+ * collection of those being managed locally.
+ * </p>
+ *
+ * @param id the session id, which may include an appended jvmRoute
+ *
+ * @return the session, or <code>null</code> if no such session could
+ * be found
+ */
+ public Session findSession(String id)
+ {
+ String realId = getRealId(id);
+ // Find it from the local store first
+ ClusteredSession session = findLocalSession(realId);
+
+ // If we didn't find it locally, only check the distributed cache
+ // if we haven't previously handled this session id on this request.
+ // If we handled it previously but it's no longer local, that means
+ // it's been invalidated. If we request an invalidated session from
+ // the distributed cache, it will be missing from the local cache but
+ // may still exist on other nodes (i.e. if the invalidation hasn't
+ // replicated yet because we are running in a tx). With buddy replication,
+ // asking the local cache for the session will cause the out-of-date
+ // session from the other nodes to be gravitated, thus resuscitating
+ // the session.
+ if (session == null
+ && !SessionReplicationContext.isSessionBoundAndExpired(realId, snapshotManager_))
+ {
+ if (log_.isTraceEnabled())
+ log_.trace("Checking for session " + realId + " in the distributed cache");
+
+ session = loadSession(realId);
+ if (session != null)
+ {
+ add(session);
+ // TODO should we advise of a new session?
+ //tellNew();
+ }
+ }
+ else if (session != null && session.isOutdated())
+ {
+ if (log_.isTraceEnabled())
+ log_.trace("Updating session " + realId + " from the distributed cache");
+
+ // Need to update it from the cache
+ loadSession(realId);
+ }
+
+ if (session != null)
+ {
+ // Add this session to the set of those potentially needing replication
+ SessionReplicationContext.bindSession(session, snapshotManager_);
+ }
+
+ return session;
+ }
+
+ /**
+ * Return the sessions. Note that this will return not only the local
+ * in-memory sessions, but also any sessions that are in the distributed
+ * cache but have not previously been accessed on this server. Invoking
+ * this method will bring all such sessions into local memory and can
+ * potentially be quite expensive.
+ *
+ * <p>
+ * Note also that when sessions are loaded from the distributed cache, no
+ * check is made as to whether the number of local sessions will thereafter
+ * exceed the maximum number allowed on this server.
+ * </p>
+ *
+ * @return an array of all the sessions
+ */
+ public Session[] findSessions()
+ {
+ // Need to load all the unloaded sessions
+ if(unloadedSessions_.size() > 0)
+ {
+ // Make a thread-safe copy of the new id list to work with
+ Set ids = new HashSet(unloadedSessions_.keySet());
+
+ if(log_.isDebugEnabled()) {
+ log_.debug("findSessions: loading sessions from distributed cache: " + ids);
+ }
+
+ for(Iterator it = ids.iterator(); it.hasNext();) {
+ loadSession((String) it.next());
+ }
+ }
+
+ // All sessions are now "local" so just return the local sessions
+ return findLocalSessions();
+ }
+
+ /**
+ * Returns all the sessions that are being actively managed by this manager.
+ * This includes those that were created on this server, those that were
+ * brought into local management by a call to
+ * {@link #findLocalSession(String)} as well as all sessions brought into
+ * local management by a call to {@link #findSessions()}.
+ */
+ public ClusteredSession[] findLocalSessions()
+ {
+ Collection coll = sessions_.values();
+ ClusteredSession[] sess = new ClusteredSession[coll.size()];
+ sess = (ClusteredSession[]) coll.toArray(sess);
+ return sess;
+ }
+
+ /**
+ * Returns the given session if it is being actively managed by this manager.
+ * An actively managed session is on that was either created on this server,
+ * brought into local management by a call to
+ * {@link #findLocalSession(String)} or brought into local management by a
+ * call to {@link #findSessions()}.
+ *
+ * @param realId the session id, with any trailing jvmRoute removed.
+ *
+ * @see #getRealId(String)
+ */
+ public ClusteredSession findLocalSession(String realId)
+ {
+ return (ClusteredSession) sessions_.get(realId);
+ }
+
+ /**
+ * Removes the session from this Manager's collection of actively managed
+ * sessions. Also removes the session from the distributed cache, both
+ * on this server and on all other server to which this one replicates.
+ */
+ public void remove(Session session)
+ {
+ ClusteredSession clusterSess = (ClusteredSession) session;
+ synchronized (clusterSess)
+ {
+ String realId = clusterSess.getRealId();
+ if (realId == null)
+ return;
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Removing session from store with id: " + realId);
+ }
+
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ clusterSess.removeMyself();
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+
+ // We don't want to replicate this session at the end
+ // of the request; the removal process took care of that
+ SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
+
+ sessions_.remove(realId);
+ stats_.removeStats(realId);
+ activeCounter_--;
+ }
+ }
+ }
+
+ /**
+ * Removes the session from this Manager's collection of actively managed
+ * sessions. Also removes the session from this server's copy of the
+ * distributed cache (but does not remove it from other servers'
+ * distributed cache).
+ */
+ public void removeLocal(Session session)
+ {
+ ClusteredSession clusterSess = (ClusteredSession) session;
+ synchronized (clusterSess)
+ {
+ String realId = clusterSess.getRealId();
+ if (realId == null) return;
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Removing session from local store with id: " + realId);
+ }
+
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ clusterSess.removeMyselfLocal();
+ }
+ finally
+ {
+ SessionReplicationContext.finishCacheActivity();
+
+ // We don't want to replicate this session at the end
+ // of the request; the removal process took care of that
+ SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
+
+ sessions_.remove(realId);
+ stats_.removeStats(realId);
+
+ // Update counters.
+ // It's a bit ad-hoc to do it here. But since we currently call
+ // this when session expires ...
+ expiredCounter_++;
+ activeCounter_--;
+ }
+ }
+ }
+
+ /**
+ * Loads a session from the distributed store. If an existing session with
+ * the id is already under local management, that session's internal state
+ * will be updated from the distributed store. Otherwise a new session
+ * will be created and added to the collection of those sessions under
+ * local management.
+ *
+ * @param realId id of the session-id with any jvmRoute removed
+ *
+ * @return the session or <code>null</code> if the session cannot be found
+ * in the distributed store
+ *
+ * TODO refactor this into 2 overloaded methods -- one that takes a ClusteredSession
+ * and populates it and one that takes an id, creates the session and calls the first
+ */
+ protected ClusteredSession loadSession(String realId)
+ {
+ if (realId == null)
+ {
+ return null;
+ }
+
+ long begin = System.currentTimeMillis();
+ boolean mustAdd = false;
+ ClusteredSession session = (ClusteredSession) sessions_.get(realId);
+ if (session == null)
+ {
+ // This is either the first time we've seen this session on this
+ // server, or we previously expired it and have since gotten
+ // a replication message from another server
+ mustAdd = true;
+ session = createEmptyClusteredSession();
+ }
+
+ synchronized (session)
+ {
+ boolean doTx = false;
+ try
+ {
+ // We need transaction so any data gravitation replication
+ // is sent in batch.
+ // Don't do anything if there is already transaction context
+ // associated with this thread.
+ if(tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ // Ignore cache notifications we may generate for this
+ // session if data gravitation occurs.
+ SessionReplicationContext.startCacheActivity();
+
+ session = proxy_.loadSession(realId, session);
+ }
+ catch (Exception ex)
+ {
+ try
+ {
+// if(doTx)
+ // Let's set it no matter what.
+ tm.setRollbackOnly();
+ }
+ catch (Exception exn)
+ {
+ exn.printStackTrace();
+ }
+
+ // We will need to alert Tomcat of this exception.
+ if (ex instanceof RuntimeException)
+ throw (RuntimeException) ex;
+
+ throw new RuntimeException("loadSession(): failed to load session " +
+ realId, ex);
+ }
+ finally
+ {
+ try {
+ if(doTx)
+ endTransaction(realId);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ if (session != null)
+ {
+ // Need to initialize.
+ session.initAfterLoad(this);
+ if (mustAdd)
+ add(session, false); // don't replicate
+ long elapsed = System.currentTimeMillis() - begin;
+ stats_.updateLoadStats(realId, elapsed);
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("loadSession(): id= " + realId + ", session=" + session);
+ }
+ }
+ else if (log_.isDebugEnabled())
+ {
+ log_.debug("loadSession(): session " + realId +
+ " not found in distributed cache");
+ }
+ }
+
+ return session;
+ }
+
+ /**
+ * Places the current session contents in the distributed cache and
+ * replicates them to the cluster
+ *
+ * @param session the session. Cannot be <code>null</code>.
+ */
+ protected void processSessionRepl(ClusteredSession session)
+ {
+ // If we are using SESSION granularity, we don't want to initiate a TX
+ // for a single put
+ boolean notSession = (replicationGranularity_ != WebMetaData.REPLICATION_GRANULARITY_SESSION);
+ boolean doTx = false;
+ try
+ {
+ // We need transaction so all the replication are sent in batch.
+ // Don't do anything if there is already transaction context
+ // associated with this thread.
+ if(notSession && tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ // Tell the proxy to ignore cache notifications we are about
+ // to generate for this session. We have to do this
+ // at this level because we don't want to resume handling
+ // notifications until any compensating changes resulting
+ // from a tx rollback are done.
+ SessionReplicationContext.startCacheActivity();
+
+ session.processSessionRepl();
+ }
+ catch (Exception ex)
+ {
+ if (log_.isDebugEnabled())
+ log_.debug("processSessionRepl(): failed with exception", ex);
+
+ try
+ {
+// if(doTx)
+ // Let's set it no matter what.
+ tm.setRollbackOnly();
+ }
+ catch (Exception exn)
+ {
+ exn.printStackTrace();
+ }
+ // We will need to alert Tomcat of this exception.
+ if (ex instanceof RuntimeException)
+ throw (RuntimeException) ex;
+
+ throw new RuntimeException("JBossCacheManager.processSessionRepl(): " +
+ "failed to replicate session.", ex);
+ }
+ finally
+ {
+ try {
+ if(doTx)
+ endTransaction(session.getId());
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+ }
+
+ protected void endTransaction(String id)
+ {
+ if (tm == null)
+ {
+ log_.warn("JBossCacheManager.endTransaction(): tm is null for id: " +id);
+ return;
+ }
+
+
+ try
+ {
+ if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
+ {
+ tm.commit();
+ }
+ else
+ {
+ log_.info("JBossCacheManager.endTransaction(): rolling back tx for id: " +id);
+ tm.rollback();
+ }
+ }
+ catch (RollbackException re)
+ {
+ // Do nothing here since cache may rollback automatically.
+ log_.warn("JBossCacheManager.endTransaction(): rolling back transaction with exception: " +re);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheManager.endTransaction(): Exception for id: " +id, e);
+ }
+ }
+
+ /**
+ * Gets the classloader of the webapp we are managing.
+ */
+ protected ClassLoader getWebappClassLoader()
+ {
+ return tcl_;
+ }
+
+ /**
+ * Goes through all sessions and look if they have expired.
+ * Note this overrides the method in JBossManager.
+ */
+ protected void processExpires()
+ {
+ if (maxInactiveInterval_ < 0)
+ {
+ return;
+ }
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Looking for sessions that have expired ...");
+ }
+
+ try
+ {
+ // First, handle the sessions we are actively managing
+ Session sessions[] = findLocalSessions();
+ for (int i = 0; i < sessions.length; ++i)
+ {
+ try
+ {
+ ClusteredSession session = (ClusteredSession) sessions[i];
+ if(session == null)
+ {
+ log_.warn("processExpires(): processing null session at index " +i);
+ continue;
+ }
+
+ // JBAS-2403. Check for outdated sessions where we think
+ // the local copy has timed out. If found, refresh the
+ // session from the cache in case that might change the timeout
+ if (session.isOutdated() && !(session.isValid(false)))
+ {
+ // JBAS-2792 don't assign the result of loadSession to session
+ // just update the object from the cache or fall through if
+ // the session has been removed from the cache
+ loadSession(session.getRealId());
+ }
+
+ // Do a normal invalidation check that will expire any
+ // sessions that have timed out
+ // DON'T SYNCHRONIZE on session here -- isValid() and
+ // expire() are meant to be multi-threaded and synchronize
+ // properly internally; synchronizing externally can lead
+ // to deadlocks!!
+ if (!session.isValid()) continue;
+ }
+ catch (Exception ex)
+ {
+ log_.error("processExpires(): failed expiring " +
+ sessions[i].getIdInternal() + " with exception: " +
+ ex, ex);
+ }
+ }
+
+ // Next, handle any unloaded sessions that are stale
+
+ long now = System.currentTimeMillis();
+ Map unloaded = new HashMap(unloadedSessions_);
+ Set entries = unloaded.entrySet();
+ for (Iterator it = entries.iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) it.next();
+ OwnedSessionUpdate osu = (OwnedSessionUpdate) entry.getValue();
+ int elapsed = (int) ((now - osu.updateTime) / 1000L);
+ if (elapsed >= maxInactiveInterval_)
+ {
+ String realId = (String) entry.getKey();
+ try
+ {
+ proxy_.removeSessionLocal(realId, osu.owner);
+ unloadedSessions_.remove(realId);
+ }
+ catch (Exception ex)
+ {
+ log_.error("processExpire(): failed removing unloaded session " +
+ realId + " with exception: " +
+ ex, ex);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ log_.error("processExpires: failed with exception: " + ex, ex);
+ }
+ }
+
+ public void processRemoteAttributeRemoval(String realId, String attrKey)
+ {
+
+ ClusteredSession session = findLocalSession(realId);
+ if (session != null)
+ {
+ boolean localCall = false; // call is due to remote event
+ boolean localOnly = true; // don't call back into cache
+ boolean notify = false; // SRV.10.7 gives us leeway
+ // not to notify listeners,
+ // which is safer
+
+ // Ensure the correct TCL is in place
+ ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ Thread.currentThread().setContextClassLoader(tcl_);
+ synchronized (session)
+ {
+ session.removeAttributeInternal(attrKey, localCall, localOnly, notify);
+ }
+ if (log_.isTraceEnabled())
+ log_.trace("processRemoteAttributeRemoval: removed attribute " +
+ attrKey + " from " + realId);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(prevTcl);
+ }
+ }
+ }
+
+ public void processRemoteInvalidation(String realId)
+ {
+ // Remove the session from our local map
+ ClusteredSession session = (ClusteredSession) sessions_.remove(realId);
+ if (session == null)
+ {
+ // We weren't managing the session anyway. But remove it
+ // from the list of cached sessions we haven't loaded
+ if (unloadedSessions_.remove(realId) != null)
+ {
+ if (log_.isTraceEnabled())
+ log_.trace("Removed entry for session " + realId + " from unloaded session map");
+ }
+ }
+ else
+ {
+ // Expire the session
+ // DON'T SYNCHRONIZE ON SESSION HERE -- isValid() and
+ // expire() are meant to be multi-threaded and synchronize
+ // properly internally; synchronizing externally can lead
+ // to deadlocks!!
+ boolean notify = false; // Don't notify listeners. SRV.10.7
+ // allows this, and sending notifications
+ // leads to all sorts of issues; e.g.
+ // circular calls with ClusteredSSO
+ boolean localCall = false; // this call originated from the cache;
+ // we have already removed session
+ boolean localOnly = true; // Don't pass attr removals to cache
+
+ // Ensure the correct TCL is in place
+ ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ Thread.currentThread().setContextClassLoader(tcl_);
+ session.expire(notify, localCall, localOnly);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(prevTcl);
+ }
+
+ // Remove any stats for this session
+ stats_.removeStats(realId);
+
+ // Update counter.
+ activeCounter_--;
+ }
+ }
+
+ /**
+ * Gets the session id with any jvmRoute removed.
+ *
+ * @param id a session id with or without an appended jvmRoute.
+ * Cannot be <code>null</code>.
+ */
+ protected String getRealId(String id)
+ {
+ return (useJK_ ? Util.getRealId(id) : id);
+ }
+
+ /**
+ * Callback from the CacheListener to notify us that a session
+ * we haven't loaded has been changed.
+ *
+ * @param realId the session id, without any trailing jvmRoute
+ * @param dataOwner the owner of the session. Can be <code>null</code> if
+ * the owner is unknown.
+ */
+ protected void unloadedSessionChanged(String realId, String dataOwner)
+ {
+ Object obj = unloadedSessions_.put(realId,
+ new OwnedSessionUpdate(dataOwner, System.currentTimeMillis()));
+ if (log_.isTraceEnabled())
+ {
+ if (obj == null)
+ {
+ log_.trace("New session " + realId + " added to unloaded session map");
+ }
+ else
+ {
+ log_.trace("Updated timestamp for unloaded session " + realId);
+ }
+ }
+ }
+
+ // ---------------------------------------------------- Lifecyle Unembedded
+
+ /**
+ * Start this Manager when running embedded in JBoss AS.
+ *
+ * @throws org.apache.catalina.LifecycleException
+ */
+ private void startEmbedded() throws LifecycleException
+ {
+ super.start();
+
+ // Start the JBossCacheService
+ // Will need to pass the classloader that is associated with this
+ //web app so de-serialization will work correctly.
+ tcl_ = super.getContainer().getLoader().getClassLoader();
+
+ proxy_.start(tcl_, this);
+
+ tm = proxy_.getTransactionManager();
+ if(tm == null)
+ {
+ throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
+ }
+
+ try
+ {
+ initializeUnloadedSessions();
+
+ // Setup our SnapshotManager
+ initSnapshotManager();
+
+ // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
+ installValves();
+
+ log_.debug("start(): JBossCacheService started");
+ }
+ catch (Exception e)
+ {
+ log_.error("Unable to start manager.", e);
+ throw new LifecycleException(e);
+ }
+ }
+
+ // ----------------------------------------------- Lifecyle When Unembedded
+
+ /**
+ * Start this Manager when running in standalone Tomcat.
+ */
+ private void startUnembedded() throws LifecycleException
+ {
+ if (started_)
+ {
+ return;
+ }
+
+ if (log_.isInfoEnabled())
+ {
+ log_.info("Manager is about to start");
+ }
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(BEFORE_START_EVENT, this);
+
+ if (snapshotMode_ == null)
+ {
+ // We were not instantiated by a JBossCacheCluster, so we need to
+ // find one and let it configure our cluster-wide properties
+ try
+ {
+ JBossCacheCluster cluster = (JBossCacheCluster) container_.getCluster();
+ cluster.configureManager(this);
+ }
+ catch (ClassCastException e)
+ {
+ String msg = "Cluster is not an instance of JBossCacheCluster";
+ log_.error(msg, e);
+ throw new LifecycleException(msg, e);
+ }
+ }
+
+ // Validate attributes
+
+ if ("SET_AND_GET".equalsIgnoreCase(replTriggerString_))
+ this.invalidateSessionPolicy_ = WebMetaData.SESSION_INVALIDATE_SET_AND_GET;
+ else if ("SET_AND_NON_PRIMITIVE_GET".equalsIgnoreCase(replTriggerString_))
+ this.invalidateSessionPolicy_ = WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET;
+ else if ("SET".equalsIgnoreCase(replTriggerString_))
+ this.invalidateSessionPolicy_ = WebMetaData.SESSION_INVALIDATE_SET;
+ else
+ throw new LifecycleException("replication-trigger value set to a " +
+ "non-valid value: '" +
+ replTriggerString_ +
+ "' (should be ['SET_AND_GET', " +
+ "'SET_AND_NON_PRIMITIVE_GET', 'SET'])");
+
+ if ("SESSION".equalsIgnoreCase(replGranularityString_))
+ this.replicationGranularity_ = WebMetaData.REPLICATION_GRANULARITY_SESSION;
+ else if ("ATTRIBUTE".equalsIgnoreCase(replGranularityString_))
+ this.replicationGranularity_ = WebMetaData.REPLICATION_GRANULARITY_ATTRIBUTE;
+ else if ("FIELD".equalsIgnoreCase(replGranularityString_))
+ this.replicationGranularity_ = WebMetaData.REPLICATION_GRANULARITY_FIELD;
+ else
+ throw new LifecycleException("replication-granularity value set to " +
+ "a non-valid value: '" +
+ replGranularityString_ +
+ "' (should be ['SESSION', " +
+ "'ATTRIBUTE' or 'FIELD'])");
+
+ // Create the JBossCacheService
+ try
+ {
+ proxy_ = new JBossCacheService(cacheObjectNameString_);
+
+ // Confirm our replication granularity is compatible with the cache
+ // Throws ISE if not
+ validateFieldMarshalling();
+
+ // We need to pass the classloader that is associated with this
+ // web app so de-serialization will work correctly.
+ tcl_ = container_.getLoader().getClassLoader();
+ proxy_.start(tcl_, this);
+ }
+ catch (Throwable t)
+ {
+ String str = "Problem starting JBossCacheService for Tomcat clustering";
+ log_.error(str, t);
+ throw new LifecycleException(str, t);
+ }
+
+ tm = proxy_.getTransactionManager();
+ if(tm == null)
+ {
+ throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
+ }
+
+ try
+ {
+ initializeUnloadedSessions();
+
+ // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
+ installValves();
+
+ started_ = true;
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(AFTER_START_EVENT, this);
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("start(): JBossCacheService started");
+ }
+ }
+ catch (Exception e)
+ {
+ log_.error("Unable to start manager.", e);
+ throw new LifecycleException(e);
+ }
+
+ try
+ {
+ registerMBeans();
+ }
+ catch (Exception e)
+ {
+ log_.error("Could not register ManagerMBean with MBeanServer", e);
+ }
+ }
+
+ /**
+ * Register this Manager with JMX.
+ */
+ private void registerMBeans()
+ {
+ try
+ {
+ MBeanServer server = getMBeanServer();
+
+ String domain;
+ if (container_ instanceof ContainerBase)
+ {
+ domain = ((ContainerBase) container_).getDomain();
+ }
+ else
+ {
+ domain = server.getDefaultDomain();
+ }
+ String hostName = ((Host) container_.getParent()).getName();
+ hostName = (hostName == null) ? "localhost" : hostName;
+ ObjectName clusterName = new ObjectName(domain
+ + ":service=ClusterManager,WebModule=//" + hostName
+ + ((Context) container_).getPath());
+
+ if (server.isRegistered(clusterName))
+ {
+ log_.warn("MBean " + clusterName + " already registered");
+ return;
+ }
+
+ objectName_ = clusterName;
+ server.registerMBean(this, clusterName);
+
+ }
+ catch (Exception ex)
+ {
+ log_.error(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Unregister this Manager from the JMX server.
+ */
+ private void unregisterMBeans()
+ {
+ if (mserver_ != null)
+ {
+ try
+ {
+ mserver_.unregisterMBean(objectName_);
+ }
+ catch (Exception e)
+ {
+ log_.error(e);
+ }
+ }
+ }
+
+ /**
+ * Get the current MBean Server.
+ *
+ * @return
+ * @throws Exception
+ */
+ private MBeanServer getMBeanServer() throws Exception
+ {
+ if (mserver_ == null)
+ {
+ mserver_ = MBeanServerLocator.locateJBoss();
+ }
+ return (mserver_);
+ }
+
+ /**
+ * Gets the ids of all sessions in the distributed cache and adds
+ * them to the unloaded sessions map, with the current time as the
+ * last replication time. This means these sessions may not be
+ * evicted from the cache for a period well beyond when they would
+ * normally expire, but this is a necessary tradeoff to avoid
+ * deserializing them all to check their lastAccessedTime.
+ */
+ private void initializeUnloadedSessions() throws CacheException
+ {
+ Map sessions = proxy_.getSessionIds();
+ if (sessions != null)
+ {
+ long now = System.currentTimeMillis();
+ for (Iterator it = sessions.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Entry) it.next();
+ unloadedSessions_.put(entry.getKey(),
+ new OwnedSessionUpdate((String) entry.getValue(), now));
+ }
+ }
+ }
+
+ /**
+ * Instantiate a SnapshotManager and ClusteredSessionValve and add
+ * the valve to our parent Context's pipeline.
+ * Add a JvmRouteValve and BatchReplicationClusteredSessionValve if needed.
+ *
+ */
+ private void installValves()
+ {
+ if (useJK_)
+ {
+ log_.info("We are using mod_jk(2) for load-balancing. " +
+ "Will add JvmRouteValve.");
+
+ installContextValve(new JvmRouteValve(this));
+ }
+
+ // Add batch replication valve if needed.
+ // TODO -- should we add this even if not FIELD in case a cross-context
+ // call traverses a field-based webapp?
+ if (replicationGranularity_ == WebMetaData.REPLICATION_GRANULARITY_FIELD &&
+ Boolean.TRUE.equals(replicationFieldBatchMode_))
+ {
+ Valve batchValve = new BatchReplicationClusteredSessionValve(this);
+ log_.debug("Adding BatchReplicationClusteredSessionValve for batch replication.");
+ installContextValve(batchValve);
+ }
+
+ // Add clustered session valve
+ ClusteredSessionValve valve = new ClusteredSessionValve();
+ installContextValve(valve);
+ }
+
+ /**
+ * Create and start a snapshot manager.
+ */
+ private void initSnapshotManager()
+ {
+ String ctxPath = ((Context) container_).getPath();
+ if ("instant".equals(snapshotMode_) || replicationGranularity_
+ == WebMetaData.REPLICATION_GRANULARITY_FIELD)
+ {
+ snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
+ }
+ else if ("interval".equals(snapshotMode_))
+ {
+ snapshotManager_ = new IntervalSnapshotManager(this, ctxPath, snapshotInterval_);
+ }
+ else
+ {
+ log_.error("Snapshot mode must be 'instant' or 'interval' - " +
+ "using 'instant'");
+ snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
+ }
+
+ snapshotManager_.start();
+ }
+
+ private void installContextValve(Valve valve)
+ {
+ boolean installed = false;
+
+ // In embedded mode, install the valve via JMX to be consistent
+ // with the way the overall context is created in TomcatDeployer.
+ // We can't do this in unembedded mode because we are called
+ // before our Context is registered with the MBean server
+ if (embedded_ && getContextObjectName() != null) {
+ try
+ {
+ getMBeanServer().invoke(getContextObjectName(), "addValve",
+ new Object[]{valve},
+ new String[]{"org.apache.catalina.Valve"});
+ installed = true;
+ }
+ catch (Exception e)
+ {
+ // JBAS-2422. If the context is restarted via JMX, the above
+ // JMX call will fail as the context will not be registered
+ // when it's made. So we catch the exception and fall back
+ // to adding the valve directly.
+ // TODO consider skipping adding via JMX and just do it directly
+ log_.debug("Caught exception installing valve to Context", e);
+ }
+ }
+
+ if (!installed)
+ {
+ // If possible install via the ContainerBase.addValve() API.
+ if (container_ instanceof ContainerBase)
+ {
+ ((ContainerBase) container_).addValve(valve);
+ }
+ else
+ {
+ // No choice; have to add it to the context's pipeline
+ container_.getPipeline().addValve(valve);
+ }
+ }
+ }
+
+ /**
+ * If we are using FIELD granularity, checks that the TreeCache
+ * supports marshalling.
+ *
+ * @throws IllegalStateException if not
+ */
+ private void validateFieldMarshalling()
+ {
+ if (replicationGranularity_ == WebMetaData.REPLICATION_GRANULARITY_FIELD
+ && !proxy_.isMarshallingAvailable())
+ {
+ // BES 16/8/2006 -- throw ISE, not ClusteringNotSupportedException, as a
+ // misconfig should be treated differently from the absence of clustering
+ // services
+ throw new IllegalStateException("replication-granularity value is set to " +
+ "'FIELD' but is not supported by the cache service configuration. " +
+ "Must set 'UseRegionBasedMarshalling' to 'true' in the tc5-cluster.sar jboss-service.xml");
+ }
+ }
+
+ private ObjectName getContextObjectName()
+ {
+ String oname = container_.getObjectName();
+ try
+ {
+ return (oname == null) ? null : new ObjectName(oname);
+ }
+ catch (MalformedObjectNameException e)
+ {
+ log_.warn("Error creating object name from string " + oname, e);
+ return null;
+ }
+ }
+
+
+ private class OwnedSessionUpdate
+ {
+ String owner;
+ long updateTime;
+
+ OwnedSessionUpdate(String owner, long updateTime)
+ {
+ this.owner = owner;
+ this.updateTime = updateTime;
+ }
+ }
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,128 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+public interface JBossCacheManagerMBean extends JBossManagerMBean
+{
+ /**
+ * Gets the value of the attribute with the given key from the given
+ * session. If the session is in the distributed store but hasn't been
+ * loaded on this node, invoking this method will cause it to be loaded.
+ *
+ * @param sessionId the id of the session
+ * @param key the attribute key
+ * @return the value, or <code>null</code> if the session or
+ * key does not exist.
+ */
+ Object getSessionAttribute(String sessionId, String key);
+
+ /**
+ * Same as <code>getSessionAttribute(sessionId, key).toString()</code>.
+ *
+ */
+ String getSessionAttributeString(String sessionId, String key);
+
+ /**
+ * Expires the given session. If the session is in the distributed store
+ * but hasn't been loaded on this node, invoking this method will cause it
+ * to be loaded.
+ *
+ * @param sessionId the id of the session
+ */
+ void expireSession(String sessionId);
+
+ /**
+ * Gets the last time the given session was accessed on this node.
+ * Information about sessions stored in the distributed store but never
+ * accessed on this node will not be made available.
+ *
+ * @param sessionId
+ * @return the last accessed time, or <code>null</code> if the session
+ * has expired or has never been accessed on this node.
+ */
+ String getLastAccessedTime(String sessionId);
+
+ /**
+ * Gets the JMX ObjectName of the distributed session cache as a string.
+ */
+ String getCacheObjectNameString();
+
+ /**
+ * Gets the replication granularity.
+ *
+ * @return SESSION, ATTRIBUTE or FIELD, or <code>null</code> if this
+ * has not yet been configured.
+ */
+ String getReplicationGranularityString();
+
+ /**
+ * Gets the replication trigger.
+ *
+ * @return SET, SET_AND_GET, SET_AND_NON_PRIMITIVE_GET or <code>null</code>
+ * if this has not yet been configured.
+ */
+ String getReplicationTriggerString();
+
+ /**
+ * Gets whether batching of field granularity changes will be done. Only
+ * relevant with replication granularity FIELD.
+ *
+ * @return <code>true</code> if per-request batching will be done,
+ * <code>false</code> if not, <code>null</code> if not configured
+ */
+ Boolean isReplicationFieldBatchMode();
+
+ /**
+ * Gets whether JK is being used and special handling of a jvmRoute
+ * portion of session ids is needed.
+ */
+ boolean getUseJK();
+
+ /**
+ * Gets the snapshot mode.
+ *
+ * @return "instant" or "interval"
+ */
+ String getSnapshotMode();
+
+ /**
+ * Gets the number of milliseconds between replications if "interval" mode
+ * is used.
+ */
+ int getSnapshotInterval();
+
+ /**
+ * Lists all session ids known to this manager, including those in the
+ * distributed store that have not been accessed on this node.
+ *
+ * @return a comma-separated list of session ids
+ */
+ String listSessionIds();
+
+ /**
+ * Lists all session ids known to this manager, excluding those in the
+ * distributed store that have not been accessed on this node.
+ *
+ * @return a comma-separated list of session ids
+ */
+ String listLocalSessionIds();
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1043 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.WeakHashMap;
+
+import javax.management.ObjectName;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.Context;
+import org.jboss.aspects.patterns.observable.Observer;
+import org.jboss.aspects.patterns.observable.Subject;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.aop.PojoCacheMBean;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.transaction.BatchModeTransactionManager;
+import org.jboss.invocation.MarshalledValue;
+import org.jboss.invocation.MarshalledValueInputStream;
+import org.jboss.invocation.MarshalledValueOutputStream;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanProxyExt;
+
+/**
+ * A wrapper class to JBossCache. This is currently needed to handle various operations such as
+ * <ul>
+ * <li>Using MarshalledValue to replace Serializable used inside different web app class loader context.</li>
+ * <li>Stripping out any id string after ".". This is to handle the JK failover properly with
+ * Tomcat JvmRoute.</li>
+ * <li>Cache exception retry.</li>
+ * <li>Helper APIS.</li>
+ * </ul>
+ */
+public class JBossCacheService
+{
+ protected static Logger log_ = Logger.getLogger(JBossCacheService.class);
+ public static final String BUDDY_BACKUP = BuddyManager.BUDDY_BACKUP_SUBTREE;
+ public static final Fqn BUDDY_BACKUP_FQN = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN;
+ public static final String SESSION = "JSESSION";
+ public static final String ATTRIBUTE = "ATTRIBUTE";
+ // Needed for cache invalidation
+ static final String VERSION_KEY = "VERSION";
+ static final String FQN_DELIMITER = "/";
+
+ private PojoCacheMBean proxy_;
+ private ObjectName cacheServiceName_;
+
+ // name of webapp's virtual host(JBAS-2194).
+ // Idea is host_name + web_app_path + session id is a unique combo.
+ private String hostName_;
+ // web app path (JBAS-1367 and JBAS-2194).
+ // Idea is host_name + web_app_path + session id is a unique combo.
+ private String webAppPath_;
+ private TransactionManager tm;
+
+ private JBossCacheManager manager_;
+ private CacheListener cacheListener_;
+ private JBossCacheWrapper cacheWrapper_;
+
+ // Do we have to marshall attributes ourself or can we let
+ // the TreeCache do it?
+ private boolean useTreeCacheMarshalling_ = false;
+
+ private WeakHashMap typeMap = new WeakHashMap();
+
+ public JBossCacheService(String treeCacheObjectName) throws ClusteringNotSupportedException
+ {
+ // Find JBossCacheService
+ try
+ {
+ cacheServiceName_ = new ObjectName(treeCacheObjectName);
+ // Create Proxy-Object for this service
+ proxy_ = (PojoCacheMBean) MBeanProxyExt.create(PojoCacheMBean.class,
+ cacheServiceName_);
+ }
+ catch (Throwable t)
+ {
+
+ String str = "Could not access TreeCache service " +
+ (cacheServiceName_ == null ? "<null>" : cacheServiceName_.toString()) +
+ " for Tomcat clustering";
+ log_.debug(str);
+ throw new ClusteringNotSupportedException(str, t);
+ }
+
+ if (proxy_ == null)
+ {
+ String str = "Could not access TreeCache service " +
+ (cacheServiceName_ == null ? "<null>" : cacheServiceName_.toString()) +
+ " for Tomcat clustering";
+ log_.debug(str);
+ throw new ClusteringNotSupportedException(str);
+ }
+
+ cacheWrapper_ = new JBossCacheWrapper(proxy_);
+
+ useTreeCacheMarshalling_ = proxy_.getUseRegionBasedMarshalling();
+ }
+
+ public void start(ClassLoader tcl, JBossCacheManager manager)
+ {
+ manager_ = manager;
+
+ Context webapp = (Context) manager_.getContainer();
+ String path = webapp.getName();
+ if( path.length() == 0 || path.equals("/")) {
+ // If this is root.
+ webAppPath_ = "ROOT";
+ } else if ( path.startsWith("/") ) {
+ webAppPath_ = path.substring(1);
+ } else {
+ webAppPath_ = path;
+ }
+ log_.debug("Old and new web app path are: " +path + ", " +webAppPath_);
+
+ String host = webapp.getParent().getName();
+ if( host == null || host.length() == 0) {
+ hostName_ = "localhost";
+ }else {
+ hostName_ = host;
+ }
+ log_.debug("Old and new virtual host name are: " + host + ", " + hostName_);
+
+
+ // Listen for cache changes
+ cacheListener_ = new CacheListener(cacheWrapper_, manager_, hostName_, webAppPath_);
+ proxy_.addTreeCacheListener(cacheListener_);
+
+ // register the tcl and bring over the state for the webapp
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+ Fqn pathFqn = new Fqn( objs );
+ String fqnStr = pathFqn.toString();
+ try {
+ if(useTreeCacheMarshalling_)
+ {
+ log_.debug("UseMarshalling is true. We will register the fqn: " +
+ fqnStr + " with class loader" +tcl +
+ " and activate the webapp's Region");
+ proxy_.registerClassLoader(fqnStr, tcl);
+ proxy_.activateRegion(fqnStr);
+ }
+ } catch (Exception ex)
+ {
+ throw new RuntimeException("Can't register class loader", ex);
+ }
+
+ // We require the cache tm to be BatchModeTransactionManager now.
+ tm = proxy_.getTransactionManager();
+ if( ! (tm instanceof BatchModeTransactionManager) )
+ {
+ throw new RuntimeException("JBossCacheService.start(): JBossCacheAop transaction manager is not type BatchModeTransactionManager." +
+ " Please check the tc5-cluster-service.xml TransactionManagerClassLookup field.");
+ }
+ }
+
+ public void stop()
+ {
+ proxy_.removeTreeCacheListener(cacheListener_);
+
+ // Construct the fqn
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+ Fqn pathFqn = new Fqn( objs );
+
+ String fqnStr = pathFqn.toString();
+ if(useTreeCacheMarshalling_)
+ {
+ log_.debug("UseMarshalling is true. We will inactivate the fqn: " +
+ fqnStr + " and un-register its classloader");
+
+ try {
+ proxy_.inactivateRegion(fqnStr);
+ proxy_.unregisterClassLoader(fqnStr);
+ }
+ catch (Exception e)
+ {
+ log_.error("Exception during inactivation of webapp region " + fqnStr +
+ " or un-registration of its class loader", e);
+ }
+ }
+
+ // remove session data
+ cacheWrapper_.evictSubtree(pathFqn);
+ }
+
+ /**
+ * Get specfically the BatchModeTransactionManager.
+ */
+ public TransactionManager getTransactionManager()
+ {
+ return tm;
+ }
+
+
+ /**
+ * Gets whether TreeCache-based marshalling is available
+ */
+ public boolean isMarshallingAvailable()
+ {
+ return useTreeCacheMarshalling_;
+ }
+
+ /**
+ * Loads any serialized data in the cache into the given session
+ * using its <code>readExternal</code> method.
+ *
+ * @return the session passed as <code>toLoad</code>, or
+ * <code>null</code> if the cache had no data stored
+ * under the given session id.
+ */
+ public ClusteredSession loadSession(String realId, ClusteredSession toLoad)
+ {
+ Fqn fqn = getSessionFqn(realId);
+
+
+ Object sessionData = cacheWrapper_.get(fqn, realId, true);
+
+ if (sessionData == null) {
+ // Requested session is no longer in the cache; return null
+ return null;
+ }
+
+ boolean firstLoad = (toLoad.getVersion() == 0);
+
+// if (useTreeCacheMarshalling_)
+// {
+// toLoad.update((ClusteredSession) sessionData);
+// }
+// else
+// {
+ byte[] sessionBytes = (byte[]) sessionData;
+
+ // Swap in/out the webapp classloader so we can deserialize
+ // attributes whose classes are only available to the webapp
+ ClassLoader prevTCL = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(manager_.getWebappClassLoader());
+ try
+ {
+ ByteArrayInputStream bais = new ByteArrayInputStream(sessionBytes);
+ // Use MarshalledValueInputStream instead of superclass ObjectInputStream
+ // or else there are problems finding classes with scoped loaders
+ MarshalledValueInputStream input = new MarshalledValueInputStream(bais);
+ toLoad.readExternal(input);
+ input.close();
+ }
+ catch (Exception e)
+ {
+ log_.error("loadSession(): id: " + realId + "exception occurred during serialization: " +e);
+ return null;
+ }
+ finally {
+ Thread.currentThread().setContextClassLoader(prevTCL);
+ }
+// }
+
+ // The internal version of the serialized session may be less than the
+ // real one due to not replicating metadata. If our listener hasn't
+ // been keeping the outdatedVersion of the session up to date because
+ // the session has never been loaded into the JBCManager cache, we
+ // need to fix the version
+ if (firstLoad)
+ {
+ Integer ver = (Integer) cacheWrapper_.get(fqn, VERSION_KEY);
+ if (ver != null)
+ toLoad.setVersion(ver.intValue());
+ }
+
+ return toLoad;
+ }
+
+ public void putSession(String realId, ClusteredSession session)
+ {
+ Fqn fqn = getSessionFqn(realId);
+
+ if (session.getReplicateSessionBody())
+ {
+ Map map = new HashMap();
+// if (useTreeCacheMarshalling_)
+// map.put(realId, session);
+// else
+ map.put(realId, externalizeSession(session));
+ // Put in (VERSION_KEY, version) after the real put for cache invalidation
+ map.put(VERSION_KEY, new Integer(session.getVersion()));
+ cacheWrapper_.put(fqn, map);
+ }
+ else
+ {
+ // Invalidate the remote caches
+ cacheWrapper_.put(fqn, VERSION_KEY, new Integer(session.getVersion()));
+ }
+ }
+
+ public void removeSession(String realId)
+ {
+ Fqn fqn = getSessionFqn(realId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Remove session from distributed store. Fqn: " + fqn);
+ }
+ //Object obj = getUnMarshalledValue(cacheWrapper_.remove(fqn, realId));
+ cacheWrapper_.remove(fqn, realId);
+ // This needs to go after object removal to support correct cache invalidation.
+// _remove(fqn, VERSION_KEY);
+ // Let just remove the whole thing (including the fqn)
+ cacheWrapper_.remove(fqn);
+ //return obj;
+ }
+
+ public void removeSessionLocal(String realId)
+ {
+ Fqn fqn = getSessionFqn(realId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Remove session from my own distributed store only. Fqn: " + fqn);
+ }
+ cacheWrapper_.evictSubtree(fqn);
+ }
+
+ public void removeSessionLocal(String realId, String dataOwner)
+ {
+ if (dataOwner == null)
+ {
+ removeSessionLocal(realId);
+ }
+ else
+ {
+ Fqn fqn = getSessionFqn(realId, dataOwner);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Remove session from my own distributed store only. Fqn: " + fqn);
+ }
+ cacheWrapper_.evictSubtree(fqn);
+ }
+ }
+
+ public boolean exists(String realId)
+ {
+ Fqn fqn = getSessionFqn(realId);
+ return proxy_.exists(fqn);
+ }
+
+ public Object getAttribute(String realId, String key)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ return getUnMarshalledValue(cacheWrapper_.get(fqn, key));
+ }
+
+ public void putAttribute(String realId, String key, Object value)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ cacheWrapper_.put(fqn, key, getMarshalledValue(value));
+ }
+
+ public void putAttribute(String realId, Map map)
+ {
+ // Duplicate the map with marshalled values
+ Map marshalled = new HashMap(map.size());
+ Set entries = map.entrySet();
+ for (Iterator it = entries.iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) it.next();
+ marshalled.put(entry.getKey(), getMarshalledValue(entry.getValue()));
+ }
+
+ Fqn fqn = getAttributeFqn(realId);
+ cacheWrapper_.put(fqn, marshalled);
+
+ }
+
+ public void removeAttributes(String realId)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ cacheWrapper_.remove(fqn);
+ }
+
+ public Object removeAttribute(String realId, String key)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("Remove attribute from distributed store. Fqn: " + fqn + " key: " + key);
+ }
+ return getUnMarshalledValue(cacheWrapper_.remove(fqn, key));
+ }
+
+ public void removeAttributesLocal(String realId)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Remove attributes from my own distributed store only. Fqn: " + fqn);
+ }
+ cacheWrapper_.evict(fqn);
+ }
+
+ /**
+ * Obtain the keys associated with this fqn. Note that it is not the fqn children.
+ *
+ */
+ public Set getAttributeKeys(String realId)
+ {
+ Set keys = null;
+ Fqn fqn = getAttributeFqn(realId);
+ try
+ {
+ keys = proxy_.getKeys(fqn);
+ }
+ catch (CacheException e)
+ {
+ log_.error("getAttributeKeys(): Exception getting keys for session " + realId, e);
+ }
+
+ return keys;
+ }
+
+ /**
+ * Return all attributes associated with this session id.
+ *
+ * @param realId the session id with any jvmRoute removed
+ * @return the attributes, or any empty Map if none are found.
+ */
+ public Map getAttributes(String realId)
+ {
+ if (realId == null || realId.length() == 0) return new HashMap();
+
+ Map map = new HashMap();
+ Set set = getAttributeKeys(realId);
+ if(set != null)
+ {
+ for (Iterator it = set.iterator(); it.hasNext();)
+ {
+ String key = (String) it.next();
+ Object value = getAttribute(realId, key);
+ map.put(key, value);
+ }
+ }
+ return map;
+ }
+
+ /**
+ * Gets the ids of all sessions in the underlying cache.
+ *
+ * @return Set containing all of the session ids of sessions in the cache
+ * (with any jvmRoute removed) or <code>null</code> if there
+ * are no sessions in the cache.
+ */
+ public Map getSessionIds() throws CacheException
+ {
+ Map result = new HashMap();
+ Set owners = proxy_.getChildrenNames(BUDDY_BACKUP_FQN);
+ if (owners != null)
+ {
+ for (Iterator it = owners.iterator(); it.hasNext();)
+ {
+ Object owner = it.next();
+ Set ids = proxy_.getChildrenNames(getWebappFqn(owner));
+ storeSessionOwners(ids, owner, result);
+ }
+ }
+ storeSessionOwners(proxy_.getChildrenNames(getWebappFqn()), null, result);
+
+ return result;
+ }
+
+ private void storeSessionOwners(Set ids, Object owner, Map map)
+ {
+ if (ids != null)
+ {
+ for (Iterator it = ids.iterator(); it.hasNext();)
+ {
+ map.put(it.next(), owner);
+ }
+ }
+ }
+
+ /**
+ * store the pojo instance in the cache. Note that this is for the aop cache.
+ * THe pojo needs to be "aspectized".
+ *
+ * @param realId the session id with any jvmRoute removed
+ * @param key the attribute key
+ * @param pojo
+ */
+ public Object setPojo(String realId, String key, Object pojo)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("setPojo(): session id: " + realId + " key: " + key +
+ " object: " + pojo.toString());
+ }
+ // Construct the fqn.
+ Fqn fqn = getFieldFqn(realId, key);
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ return proxy_.putObject(fqn, pojo);
+ } catch (CacheException e) {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache setPojo ... ", e);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ /**
+ * Remove pojo from the underlying cache store.
+ * @param realId the session id with any jvmRoute removed
+ * @param key the attribute key
+ * @return pojo that just removed. Null if there none.
+ */
+ public Object removePojo(String realId, String key)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("removePojo(): session id: " +realId + " key: " +key);
+ }
+ // Construct the fqn.
+ Fqn fqn = getFieldFqn(realId, key);
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ return proxy_.removeObject(fqn);
+ } catch (CacheException e) {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache removePojo ... ", e);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ /**
+ * Remove all the pojos from the underlying cache store locally
+ * without replication.
+ *
+ * @param realId the session id with any jvmRoute removed
+ */
+ public void removePojosLocal(String realId)
+ {
+ if(log_.isDebugEnabled())
+ {
+ log_.debug("removePojoLocal(): session id: " +realId);
+ }
+ // Construct the fqn.
+ Fqn fqn = getAttributeFqn(realId);
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ cacheWrapper_.evictSubtree(fqn);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ /**
+ * Remove all the pojos from the underlying cache store locally
+ * without replication.
+ *
+ * @param realId the session id with any jvmRoute removed
+ */
+ public void removePojoLocal(String realId, String key)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("removePojoLocal(): session id: " + realId + " key: " +key);
+ }
+ // Construct the fqn.
+ Fqn fqn = getFieldFqn(realId, key);
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ cacheWrapper_.evictSubtree(fqn);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ public Set getPojoKeys(String realId)
+ {
+ Set keys = null;
+ Fqn fqn = getAttributeFqn(realId);
+ try
+ {
+ keys = proxy_.getChildrenNames(fqn);
+ }
+ catch (CacheException e)
+ {
+ log_.error("getPojoKeys(): Exception getting keys for session " + realId, e);
+ }
+
+ return keys;
+ }
+
+
+ /**
+ *
+ * @param realId the session id with any jvmRoute removed
+ * @param key the attribute key
+ * @return Pojo that is associated with the attribute
+ */
+ public Object getPojo(String realId, String key)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("getPojo(): session id: " +realId + " key: " +key);
+ }
+ // Construct the fqn.
+ Fqn fqn = getFieldFqn(realId, key);
+
+ try
+ {
+ return proxy_.getObject(fqn);
+ }
+ catch (CacheException e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache getPojo ... ", e);
+ }
+ }
+
+ /**
+ * Recursively adds session as observer to the pojo graph. Assumes the
+ * whole object graph has Subject "introduction" declared. If a portion
+ * of the graph isn't a Subject, the recursion does not continue below
+ * that part of the graph.
+ *
+ * @param session the session
+ * @param pojo the pojo. Can be <code>null</code>.
+ */
+ public void addObserver(Observer session, Object pojo)
+ {
+ addObserver(session, pojo, new HashSet());
+ }
+
+ private void addObserver(Observer session, Object pojo, Set processed)
+ {
+ if ( pojo instanceof Collection )
+ {
+ Collection col = (Collection)pojo;
+ for (Iterator i = col.iterator(); i.hasNext();) {
+ // If not a managed pojo, will return anyway
+ addObserver(session, i.next(), processed);
+ }
+
+ return;
+ }
+ else if (pojo instanceof Map)
+ {
+ for (Iterator i = ((Map)pojo).entrySet().iterator(); i.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+
+ // Walk thru key and value
+ addObserver(session, entry.getKey(), processed);
+ addObserver(session, entry.getValue(), processed);
+ }
+
+ return;
+ }
+
+ if(! (pojo instanceof Subject) )
+ {
+ return; // No need to add observer since it is primitive.
+ }
+
+ Subject subject = (Subject)pojo;
+ subject.addObserver(session);
+
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("addObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
+ }
+
+ // Examine each field of the type and its superclasses to see if
+ // we need to add the observer to the pojo held by that field
+ // Traverse recursively
+
+ // First identify and cache the names of all the class'
+ // non-immediate fields
+ Class type = pojo.getClass();
+ Set complexFields = (Set) typeMap.get(type);
+ if (complexFields == null)
+ {
+ complexFields = Util.parseComplexFields(type);
+ typeMap.put(type, complexFields);
+ }
+
+ if (complexFields.size() == 0)
+ return;
+
+ // Store a ref to the pojo to avoid cyclic additions
+ processed.add(pojo);
+
+ for (Iterator iter = complexFields.iterator(); iter.hasNext();)
+ {
+ String fieldName = (String) iter.next();
+ Class curType = type;
+ while (curType != null)
+ {
+ try
+ {
+ Field field = curType.getDeclaredField(fieldName);
+ boolean accessible = field.isAccessible();
+ Object value = null;
+ try
+ {
+ field.setAccessible(true);
+
+ value=field.get(pojo);
+ // Continue recursively unless we've already handled this value
+ if (value != null && !processed.contains(value))
+ addObserver(session, value, processed);
+ break;
+ }
+ catch(IllegalAccessException e)
+ {
+ throw new RuntimeException("field access failed", e);
+ }
+ finally
+ {
+ field.setAccessible(accessible);
+ }
+ }
+ catch (NoSuchFieldException e)
+ {
+ // Check if the field is declared in a superclass
+ curType = curType.getSuperclass();
+ if (curType == null)
+ throw new RuntimeException("Field " + fieldName +
+ " does not exist", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Recursively removes session as observer to the pojo graph. Assumes the
+ * whole object graph has Subject "introduction" declared. If a portion
+ * of the graph isn't a Subject, the recursion does not continue below
+ * that part of the graph.
+ *
+ * @param session the session
+ * @param pojo the pojo to stop observing. Can be <code>null</code>.
+ */
+ public void removeObserver(Observer session, Object pojo)
+ {
+ removeObserver(session, pojo, new HashSet());
+ }
+
+ private void removeObserver(Observer session, Object pojo, Set stack)
+ {
+ if ( pojo instanceof Collection )
+ {
+ Collection col = (Collection)pojo;
+ for (Iterator i = col.iterator(); i.hasNext();) {
+ Object obj = i.next();
+ // If not a managed pojo, will return anyway
+ removeObserver(session, obj, stack);
+ }
+
+ return;
+ }
+ else if (pojo instanceof Map)
+ {
+ Map map = (Map)pojo;
+ for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+ Object key = i.next();
+ Object value = map.get(key);
+
+ // Walk thru key and value
+ removeObserver(session, key, stack);
+ removeObserver(session, value, stack);
+ }
+
+ return;
+ }
+ // BRIAN 3/14 changed this from checking Advised to checking Subject
+ // since that is what we cast to below
+ if(! (pojo instanceof Subject) )
+ {
+ return; // No need to add observer since it is primitive.
+ }
+
+ Subject subject = (Subject)pojo;
+ subject.removeObserver(session);
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("removeObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
+ }
+
+ // Examine each field of the type and its superclasses to see if
+ // we need to remove the observer from the pojo held by that field
+ // Traverse recursively
+
+ // First identify and cache the names of all the class'
+ // non-immediate fields
+ Class type = pojo.getClass();
+ Set complexFields = (Set) typeMap.get(type);
+ if (complexFields == null)
+ {
+ complexFields = Util.parseComplexFields(type);
+ typeMap.put(type, complexFields);
+ }
+
+ if (complexFields.size() == 0)
+ return;
+
+ // Store a ref to the pojo to avoid cyclic removals
+ stack.add(pojo);
+
+ for (Iterator iter = complexFields.iterator(); iter.hasNext();)
+ {
+ String fieldName = (String) iter.next();
+ Class curType = type;
+ while (curType != null)
+ {
+ try
+ {
+ Field field = curType.getDeclaredField(fieldName);
+ boolean accessible = field.isAccessible();
+ Object value = null;
+ try
+ {
+ field.setAccessible(true);
+
+ value=field.get(pojo);
+ // Continue recursively unless we've already handled this value
+ if (value != null && !stack.contains(value))
+ removeObserver(session, value, stack);
+ break;
+ }
+ catch(IllegalAccessException e)
+ {
+ throw new RuntimeException("field access failed", e);
+ }
+ finally
+ {
+ field.setAccessible(accessible);
+ }
+ }
+ catch (NoSuchFieldException e)
+ {
+ // Check if the field is declared in a superclass
+ curType = curType.getSuperclass();
+ if (curType == null)
+ throw new RuntimeException("Field " + fieldName +
+ " does not exist", e);
+ }
+ }
+ }
+ }
+
+ private Fqn getFieldFqn(String id, String key)
+ {
+ // /SESSION/id/ATTR/key
+ // Guard against string with delimiter.
+ List list = new ArrayList(6);
+ list.add(SESSION);
+ list.add(hostName_);
+ list.add(webAppPath_);
+ list.add(id);
+ list.add(ATTRIBUTE);
+ breakKeys(key, list);
+ return new Fqn(list);
+ }
+
+ private void breakKeys(String key, List list)
+ {
+ StringTokenizer token = new StringTokenizer(key, FQN_DELIMITER);
+ while(token.hasMoreTokens())
+ {
+ list.add(token.nextToken());
+ }
+ }
+
+ private Fqn getWebappFqn()
+ {
+ // /SESSION/hostname/webAppPath
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+ return new Fqn(objs);
+ }
+
+ private Fqn getWebappFqn(Object dataOwner)
+ {
+ if (dataOwner == null)
+ return getWebappFqn();
+
+ // /SESSION/hostname/webAppPath
+ Object[] objs = new Object[]{BUDDY_BACKUP, dataOwner, SESSION, hostName_, webAppPath_};
+ return new Fqn(objs);
+ }
+
+ private Fqn getSessionFqn(String id)
+ {
+ // /SESSION/hostname/webAppPath/id
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_, id};
+ return new Fqn(objs);
+ }
+
+ private Fqn getSessionFqn(String id, String dataOwner)
+ {
+ // /_BUDDY_BACKUP_/dataOwner/SESSION/hostname/webAppPath/id
+ Object[] objs = new Object[]{BUDDY_BACKUP, dataOwner, SESSION, hostName_, webAppPath_, id};
+ return new Fqn(objs);
+ }
+
+ private Fqn getAttributeFqn(String id)
+ {
+ // /SESSION/hostName/webAppPath/id/ATTR
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_, id, ATTRIBUTE};
+ return new Fqn(objs);
+ }
+
+ private Object getMarshalledValue(Object value)
+ {
+ // JBAS-2920. For now, continue using MarshalledValue, as
+ // it allows lazy deserialization of the attribute on remote nodes
+ // For Branch_4_0 this is what we have to do anyway for backwards
+ // compatibility. For HEAD we'll follow suit for now.
+ // TODO consider only using MV for complex objects (i.e. not primitives)
+ // and Strings longer than X.
+
+// if (useTreeCacheMarshalling_)
+// {
+// return value;
+// }
+// else
+// {
+ try
+ {
+ MarshalledValue mv = new MarshalledValue(value);
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("marshalled object to size " + mv.size() + " bytes");
+ }
+ return mv;
+ }
+ catch (IOException e)
+ {
+ log_.error("IOException occurred marshalling value ", e);
+ return null;
+ }
+// }
+ }
+
+ private Object getUnMarshalledValue(Object mv)
+ {
+ // JBAS-2920. For now, continue using MarshalledValue, as
+ // it allows lazy deserialization of the attribute on remote nodes
+ // For Branch_4_0 this is what we have to do anyway for backwards
+ // compatibility. For HEAD we'll follow suit for now.
+// if (useTreeCacheMarshalling_)
+// {
+// return mv;
+// }
+// else
+// {
+ if (mv == null) return null;
+ // Swap in/out the tcl for this web app. Needed only for un marshalling.
+ ClassLoader prevTCL = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(manager_.getWebappClassLoader());
+ try
+ {
+ return ((MarshalledValue) mv).get();
+ }
+ catch (IOException e)
+ {
+ log_.error("IOException occurred unmarshalling value ", e);
+ return null;
+ }
+ catch (ClassNotFoundException e)
+ {
+ log_.error("ClassNotFoundException occurred unmarshalling value ", e);
+ return null;
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(prevTCL);
+ }
+// }
+ }
+
+ private byte[] externalizeSession(ClusteredSession session)
+ {
+ try
+ {
+ // Write the contents of session to a byte array and store that
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ // Use MarshalledValueOutputStream instead of superclass ObjectOutputStream
+ // or else there are problems finding classes with scoped loaders
+ MarshalledValueOutputStream oos = new MarshalledValueOutputStream(baos);
+ session.writeExternal(oos);
+ oos.close(); // flushes bytes to baos
+
+ byte[] bytes = baos.toByteArray();
+
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("marshalled object to size " + bytes.length + " bytes");
+ }
+
+ return bytes;
+ }
+ catch (Exception e)
+ {
+ log_.error("externalizeSession(): exception occurred externalizing session " + session, e);
+ return null;
+ }
+
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,280 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.aop.PojoCacheMBean;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.lock.TimeoutException;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class JBossCacheWrapper
+{
+ static final Option GRAVITATE_OPTION = new Option();
+
+ static
+ {
+ GRAVITATE_OPTION.setForceDataGravitation(true);
+ }
+
+ private static final int RETRY = 3;
+ private PojoCacheMBean proxy_;
+
+ JBossCacheWrapper(PojoCacheMBean cache)
+ {
+ proxy_ = cache;
+ }
+
+ /**
+ * Wrapper to embed retyr logic.
+ *
+ * @param fqn
+ * @param id
+ * @return
+ */
+ Object get(Fqn fqn, String id)
+ {
+ return get(fqn, id, false);
+ }
+
+ /**
+ * Wrapper to embed retyr logic.
+ *
+ * @param fqn
+ * @param id
+ * @return
+ */
+ Object get(Fqn fqn, String id, boolean gravitate)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ Object value = null;
+ if (gravitate)
+ value = proxy_.get(fqn, id, GRAVITATE_OPTION);
+ else
+ value = proxy_.get(fqn, id);
+ return value;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ if (e instanceof RuntimeException)
+ throw (RuntimeException) e;
+ throw new RuntimeException("JBossCacheService: exception occurred in cache get ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache get after retry ... ", ex);
+ }
+
+ /**
+ * Wrapper to embed retry logic.
+ *
+ * @param fqn
+ * @param id
+ * @param value
+ * @return
+ */
+ void put(Fqn fqn, String id, Object value)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ proxy_.put(fqn, id, value);
+ return;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache put ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache put after retry ... ", ex);
+ }
+
+
+ /**
+ * Wrapper to embed retry logic.
+ *
+ * @param fqn
+ * @param map
+ */
+ void put(Fqn fqn, Map map)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ proxy_.put(fqn, map);
+ return;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache put ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache put after retry ... ", ex);
+ }
+
+ /**
+ * Wrapper to embed retyr logic.
+ *
+ * @param fqn
+ * @param id
+ * @return
+ */
+ Object remove(Fqn fqn, String id)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ return proxy_.remove(fqn, id);
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache remove ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache remove after retry ... ", ex);
+ }
+
+ /**
+ * Wrapper to embed retry logic.
+ *
+ * @param fqn
+ */
+ void remove(Fqn fqn)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ proxy_.remove(fqn);
+ return;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache remove ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache remove after retry ... ", ex);
+ }
+
+ /**
+ * Wrapper to embed retry logic.
+ *
+ * @param fqn
+ */
+ void evict(Fqn fqn)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ proxy_.evict(fqn);
+ return;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache evict ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache evict after retry ... ", ex);
+ }
+
+ void evictSubtree(Fqn fqn)
+ {
+
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ // Evict the node itself first, since if it stores a Pojo
+ // that will do everything
+ proxy_.evict(fqn);
+
+ // next do a depth first removal; this ensure all nodes
+ // are removed, not just their data map
+ Set children = proxy_.getChildrenNames(fqn);
+ if (children != null)
+ {
+ for (Iterator it = children.iterator(); it.hasNext(); )
+ {
+ Fqn child = new Fqn(fqn, it.next());
+ proxy_.evict(child);
+ }
+
+ proxy_.evict(fqn);
+ }
+ return;
+
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache evict ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache evictSubtree after retry ... ", ex);
+
+
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,899 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+import org.apache.catalina.*;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.web.tomcat.statistics.ReplicationStatistics;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Base abstract implementation of Tomcat manager without the concept of
+ * session operations, e.g., add, remove, etc.
+ *
+ * @author Ben Wang
+ * @version $Revision: 57206 $
+ */
+public abstract class JBossManager
+ implements AbstractJBossManager, Lifecycle,
+ JBossManagerMBean, PropertyChangeListener
+{
+
+ // -- Constants ----------------------------------------
+ /**
+ * Informational name for this Catalina component
+ */
+ private static final String info_ = "JBossManager/1.0";
+
+ // -- Class attributes ---------------------------------
+ protected ReplicationStatistics stats_ = new ReplicationStatistics();
+
+ /**
+ * Policy to determine if a session is dirty
+ */
+ protected int invalidateSessionPolicy_ = WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET;
+ /**
+ * Replication granulairty.
+ */
+ protected int replicationGranularity_ = WebMetaData.REPLICATION_GRANULARITY_SESSION;
+ /**
+ * The lifecycle_ event support for this component.
+ */
+ protected LifecycleSupport lifecycle_ = new LifecycleSupport(this);
+ /**
+ * Has this component been started_ yet?
+ */
+ protected boolean started_ = false;
+ /**
+ * The objectname this Manager is associated with
+ */
+ protected ObjectName objectName_;
+ /**
+ * The Log-object for this class
+ */
+ protected Logger log_ = Logger.getLogger(this.getClass().getName());
+ /**
+ * The Container with which this Manager is associated.
+ */
+ protected Container container_;
+ /**
+ /**
+ * The distributable flag for Sessions created by this Manager. If this
+ * flag is set to <code>true</code>, any user attributes added to a
+ * session controlled by this Manager must be Serializable.
+ */
+ protected boolean distributable_ = true;
+ /**
+ * The default maximum inactive interval for Sessions created by
+ * this Manager.
+ */
+ protected int maxInactiveInterval_ = 60;
+ /**
+ * The session id length of Sessions created by this Manager.
+ */
+ protected int sessionIdLength_ = 16;
+
+ // Maximum of ative sessions allowed. -1 is unlimited.
+ protected int maxActive_ = -1;
+
+ // Number of sessions created by this manager
+ protected int createdCounter_ = 0;
+
+ // number of rejected sessions because the number active sessions exceeds maxActive
+ protected int rejectedCounter_ = 0;
+
+ // Number of active sessions
+ protected int activeCounter_ = 0;
+
+ // Maximum number of active sessions seen so far
+ protected int maxActiveCounter_ = 0;
+
+ // number of expired session ids. Not sure what exactly does it mean in our clustered case.
+ protected int expiredCounter_ = 0;
+
+ protected long timeSinceLastReset_ = 0;
+
+ // Cumulative time spent in backgroundProcess
+ protected long processingTime_ = 0;
+
+ /**
+ * Map<String,ClusteredSession>. Store the local sessions.
+ */
+ protected final Map sessions_ = new ConcurrentHashMap();
+
+ /**
+ * If set to true, it will not replicate the access time stamp unless attributes are dirty.
+ */
+ protected boolean useLocalCache_ = false;
+
+ /**
+ * The property change support for this component.
+ */
+ protected PropertyChangeSupport support_ = new PropertyChangeSupport(this);
+
+ protected SessionIDGenerator sessionIDGenerator_;
+
+ protected String jvmRoute_;
+
+ // TODO Need a string manager to handle exception localization
+
+ public JBossManager()
+ {
+ sessionIDGenerator_ = SessionIDGenerator.getInstance();
+
+ }
+
+ public void init(String name, WebMetaData webMetaData, boolean useJK, boolean useLocalCache)
+ throws ClusteringNotSupportedException
+ {
+ replicationGranularity_ = webMetaData.getReplicationGranularity();
+ invalidateSessionPolicy_ = webMetaData.getInvalidateSessionPolicy();
+ useLocalCache_ = useLocalCache;
+ log_.info("init(): replicationGranularity_ is " + replicationGranularity_ +
+ " and invaldateSessionPolicy is " + invalidateSessionPolicy_);
+
+ try
+ {
+ // Give this manager a name
+ objectName_ = new ObjectName("jboss.web:service=ClusterManager,WebModule=" + name);
+ }
+ catch (Throwable e)
+ {
+ log_.error("Could not create ObjectName", e);
+ throw new ClusteringNotSupportedException(e.toString());
+ }
+ }
+
+ public int getInvalidateSessionPolicy()
+ {
+ return this.invalidateSessionPolicy_;
+ }
+
+ /**
+ * Retrieve the enclosing Engine for this Manager.
+ *
+ * @return an Engine object (or null).
+ */
+ public Engine getEngine()
+ {
+ Engine e = null;
+ for (Container c = getContainer(); e == null && c != null; c = c.getParent())
+ {
+ if (c != null && c instanceof Engine)
+ {
+ e = (Engine) c;
+ }
+ }
+ return e;
+ }
+
+ /**
+ * Retrieve the JvmRoute for the enclosing Engine.
+ *
+ * @return the JvmRoute or null.
+ */
+ public String getJvmRoute()
+ {
+ if (jvmRoute_ == null)
+ {
+ Engine e = getEngine();
+ jvmRoute_= (e == null ? null : e.getJvmRoute());
+ }
+ return jvmRoute_;
+ }
+
+ /**
+ * Get a new session-id from the distributed store
+ *
+ * @return new session-id
+ */
+ protected String getNextId()
+ {
+ return sessionIDGenerator_.getSessionId();
+ }
+
+ /**
+ * Gets the JMX <code>ObjectName</code> under
+ * which our <code>TreeCache</code> is registered.
+ */
+ public ObjectName getObjectName()
+ {
+ return objectName_;
+ }
+
+ public boolean isUseLocalCache()
+ {
+ return useLocalCache_;
+ }
+
+ /**
+ * Sets a new cookie for the given session id and response
+ *
+ * @param sessionId The session id
+ */
+ public void setSessionCookie(String sessionId)
+ {
+ HttpServletResponse response = SessionReplicationContext.getOriginalResponse();
+ setNewSessionCookie(sessionId, response);
+ }
+
+ public void setNewSessionCookie(String sessionId, HttpServletResponse response)
+ {
+ if (response != null)
+ {
+ Context context = (Context) container_;
+ Connector connector = ((Response)response).getConnector();
+ if (context.getCookies())
+ {
+ // set a new session cookie
+ Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME, sessionId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Setting cookie with session id:" + sessionId + " & name:" + Globals.SESSION_COOKIE_NAME);
+ }
+
+ String contextPath = null;
+ if (!connector.getEmptySessionPath() && (context != null)) {
+ contextPath = context.getEncodedPath();
+ }
+
+ if ((contextPath != null) && (contextPath.length() > 0)) {
+ newCookie.setPath(contextPath);
+ } else {
+ newCookie.setPath("/");
+ }
+
+ if (connector.getSecure()) {
+ newCookie.setSecure(true);
+ }
+
+ response.addCookie(newCookie);
+ }
+ }
+ }
+
+ // JBossManagerMBean-methods -------------------------------------
+
+ // A better property name for the MBean API
+ public int getMaxActiveAllowed()
+ {
+ return getMaxActive();
+ }
+
+ // A better property name for the MBean API
+ public void setMaxActiveAllowed(int maxActive)
+ {
+ setMaxActive(maxActive);
+ }
+
+ public long getMaxActiveSessionCount()
+ {
+ return this.maxActiveCounter_;
+ }
+
+ public ReplicationStatistics getReplicationStatistics()
+ {
+ return stats_;
+ }
+
+ public void resetStats()
+ {
+ stats_.resetStats();
+ activeCounter_ = 0;
+ maxActiveCounter_ = 0;
+ rejectedCounter_ = 0;
+ createdCounter_ = 0;
+ expiredCounter_ = 0;
+ processingTime_ = 0;
+ timeSinceLastReset_ = System.currentTimeMillis();
+ }
+
+ public long timeInSecondsSinceLastReset()
+ {
+ return (System.currentTimeMillis() - timeSinceLastReset_) / (1000L);
+ }
+
+ public long getActiveSessionCount()
+ {
+ return getActiveSessions();
+ }
+
+ public long getCreatedSessionCount()
+ {
+ return createdCounter_;
+ }
+
+ public long getExpiredSessionCount()
+ {
+ return expiredCounter_;
+ }
+
+ public long getRejectedSessionCount()
+ {
+ return rejectedCounter_;
+ }
+
+ public int getSessionMaxAliveTime()
+ {
+ return 0;
+ }
+
+ public void setSessionMaxAliveTime(int sessionMaxAliveTime)
+ {
+ }
+
+ public int getSessionAverageAliveTime()
+ {
+ return 0;
+ }
+
+ public void setSessionAverageAliveTime(int sessionAverageAliveTime)
+ {
+ }
+
+ public String reportReplicationStatistics()
+ {
+ StringBuffer tmp = new StringBuffer();
+ HashMap copy = new HashMap(stats_.getStats());
+ Iterator iter = copy.entrySet().iterator();
+ tmp.append("<table><tr>");
+ tmp.append("<th>sessionID</th>");
+ tmp.append("<th>replicationCount</th>");
+ tmp.append("<th>minPassivationTime</th>");
+ tmp.append("<th>maxPassivationTime</th>");
+ tmp.append("<th>totalPassivationTime</th>");
+ tmp.append("<th>minReplicationTime</th>");
+ tmp.append("<th>maxReplicationTime</th>");
+ tmp.append("<th>totalReplicationlTime</th>");
+ tmp.append("<th>loadCount</th>");
+ tmp.append("<th>minLoadTime</th>");
+ tmp.append("<th>maxLoadTime</th>");
+ tmp.append("<th>totalLoadTime</th>");
+ while (iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ ReplicationStatistics.TimeStatistic stat = (ReplicationStatistics.TimeStatistic) entry.getValue();
+ if (stat != null)
+ {
+ tmp.append("<tr><td>");
+ tmp.append(entry.getKey());
+ tmp.append("</td><td>");
+ tmp.append(stat.replicationCount);
+ tmp.append("</td><td>");
+ tmp.append(stat.minPassivationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.maxPassivationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.totalPassivationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.minReplicationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.maxReplicationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.totalReplicationlTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.loadCount);
+ tmp.append("</td><td>");
+ tmp.append(stat.minLoadTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.maxLoadTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.totalLoadlTime);
+ tmp.append("</td></tr>");
+ }
+ }
+ tmp.append("</table>");
+ copy.clear();
+ return tmp.toString();
+
+ }
+
+ public String reportReplicationStatisticsCSV()
+ {
+ StringBuffer tmp = createCSVHeader();
+ HashMap copy = new HashMap(stats_.getStats());
+ Iterator iter = copy.entrySet().iterator();
+ while (iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ ReplicationStatistics.TimeStatistic stat = (ReplicationStatistics.TimeStatistic) entry.getValue();
+ if (stat != null)
+ {
+ tmp.append("\n");
+ tmp.append(entry.getKey());
+ tmp.append(",");
+ tmp.append(stat.replicationCount);
+ tmp.append(",");
+ tmp.append(stat.minPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.maxPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.totalPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.minReplicationTime);
+ tmp.append(",");
+ tmp.append(stat.maxReplicationTime);
+ tmp.append(",");
+ tmp.append(stat.totalReplicationlTime);
+ tmp.append(",");
+ tmp.append(stat.loadCount);
+ tmp.append(",");
+ tmp.append(stat.minLoadTime);
+ tmp.append(",");
+ tmp.append(stat.maxLoadTime);
+ tmp.append(",");
+ tmp.append(stat.totalLoadlTime);
+ }
+ }
+ copy.clear();
+ return tmp.toString();
+
+ }
+
+ public String reportReplicationStatisticsCSV(String sessionId)
+ {
+ StringBuffer tmp = createCSVHeader();
+ Map stats = stats_.getStats();
+ ReplicationStatistics.TimeStatistic stat =
+ (ReplicationStatistics.TimeStatistic) stats.get(sessionId);
+ if (stat != null)
+ {
+ tmp.append("\n");
+ tmp.append(sessionId);
+ tmp.append(",");
+ tmp.append(stat.replicationCount);
+ tmp.append(",");
+ tmp.append(stat.minPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.maxPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.totalPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.minReplicationTime);
+ tmp.append(",");
+ tmp.append(stat.maxReplicationTime);
+ tmp.append(",");
+ tmp.append(stat.totalReplicationlTime);
+ tmp.append(",");
+ tmp.append(stat.loadCount);
+ tmp.append(",");
+ tmp.append(stat.minLoadTime);
+ tmp.append(",");
+ tmp.append(stat.maxLoadTime);
+ tmp.append(",");
+ tmp.append(stat.totalLoadlTime);
+ }
+ return tmp.toString();
+ }
+
+ private StringBuffer createCSVHeader()
+ {
+ StringBuffer tmp = new StringBuffer();
+ tmp.append("sessionID,");
+ tmp.append("replicationCount,");
+ tmp.append("minPassivationTime,");
+ tmp.append("maxPassivationTime,");
+ tmp.append("totalPassivationTime,");
+ tmp.append("minReplicationTime,");
+ tmp.append("maxReplicationTime,");
+ tmp.append("totalReplicationlTime,");
+ tmp.append("loadCount,");
+ tmp.append("minLoadTime,");
+ tmp.append("maxLoadTime,");
+ tmp.append("totalLoadTime");
+
+ return tmp;
+ }
+
+ // Lifecycle-methods -------------------------------------
+
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle_.addLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return lifecycle_.findLifecycleListeners();
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle_.removeLifecycleListener(listener);
+ }
+
+ /**
+ * Start this Manager
+ *
+ * @throws org.apache.catalina.LifecycleException
+ *
+ */
+ public void start() throws LifecycleException
+ {
+ startManager();
+ }
+
+ /**
+ * Stop this Manager
+ *
+ * @throws org.apache.catalina.LifecycleException
+ *
+ */
+ public void stop() throws LifecycleException
+ {
+ resetStats();
+ stopManager();
+ }
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after <code>configure()</code>,
+ * and before any of the public methods of the component are utilized.
+ *
+ * @throws IllegalStateException if this component has already been
+ * started_
+ * @throws org.apache.catalina.LifecycleException
+ * if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ protected void startManager() throws LifecycleException
+ {
+ log_.info("Starting JBossManager");
+
+ // Validate and update our current component state
+ if (started_)
+ throw new LifecycleException
+ ("JBossManager alreadyStarted");
+ lifecycle_.fireLifecycleEvent(START_EVENT, null);
+ started_ = true;
+
+ // register ClusterManagerMBean to the MBeanServer
+ try
+ {
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ server.registerMBean(this, objectName_);
+ }
+ catch (Exception e)
+ {
+ log_.error("Could not register ClusterManagerMBean to MBeanServer", e);
+ }
+ }
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component.
+ *
+ * @throws IllegalStateException if this component has not been started_
+ * @throws org.apache.catalina.LifecycleException
+ * if this component detects a fatal error
+ * that needs to be reported
+ */
+ protected void stopManager() throws LifecycleException
+ {
+ log_.info("Stopping JBossManager");
+
+ // Validate and update our current component state
+ if (!started_)
+ throw new LifecycleException
+ ("JBossManager notStarted");
+ lifecycle_.fireLifecycleEvent(STOP_EVENT, null);
+ started_ = false;
+
+ // unregister ClusterManagerMBean from the MBeanServer
+ try
+ {
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ server.unregisterMBean(objectName_);
+ }
+ catch (Exception e)
+ {
+ log_.error("Could not unregister ClusterManagerMBean from MBeanServer", e);
+ }
+ }
+
+ // Manager-methods -------------------------------------
+ public Container getContainer()
+ {
+ return container_;
+ }
+
+ public void setContainer(Container container)
+ {
+
+ // De-register from the old Container (if any)
+ if ((this.container_ != null) && (this.container_ instanceof Context))
+ this.container_.removePropertyChangeListener(this);
+
+ // Default processing provided by our superclass
+ this.container_ = container;
+
+ // Register with the new Container (if any)
+ if ((this.container_ != null) && (this.container_ instanceof Context))
+ {
+ setMaxInactiveInterval
+ (((Context) this.container_).getSessionTimeout() * 60);
+ this.container_.addPropertyChangeListener(this);
+ }
+ }
+
+ public boolean getDistributable()
+ {
+ return distributable_;
+ }
+
+ public void setDistributable(boolean distributable)
+ {
+ this.distributable_ = distributable;
+ }
+
+ public String getInfo()
+ {
+ return info_;
+ }
+
+ public int getMaxInactiveInterval()
+ {
+ return maxInactiveInterval_;
+ }
+
+ public void setMaxInactiveInterval(int interval)
+ {
+ this.maxInactiveInterval_ = interval;
+ }
+
+ public int getSessionIdLength()
+ {
+ return sessionIdLength_;
+ }
+
+ public void setSessionIdLength(int idLength)
+ {
+ this.sessionIdLength_ = idLength;
+ }
+
+ public int getSessionCounter()
+ {
+ return createdCounter_;
+ }
+
+ public void setSessionCounter(int sessionCounter)
+ {
+ this.createdCounter_ = sessionCounter;
+ }
+
+ public int getMaxActive()
+ {
+ return maxActive_;
+ }
+
+ public void setMaxActive(int maxActive)
+ {
+ this.maxActive_ = maxActive;
+ }
+
+ public int getExpiredSessions()
+ {
+ return expiredCounter_;
+ }
+
+ public void setExpiredSessions(int expiredSessions)
+ {
+ this.expiredCounter_ = expiredSessions;
+ }
+
+ public int getRejectedSessions()
+ {
+ return rejectedCounter_;
+ }
+
+ public void setRejectedSessions(int rejectedSessions)
+ {
+ this.rejectedCounter_ = rejectedSessions;
+ }
+
+ public long getProcessingTime()
+ {
+ return this.processingTime_;
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ support_.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Remove the active session locally from the manager without replicating to the cluster. This can be
+ * useful when the session is exipred, for example, where there is not need to propagate the expiration.
+ *
+ * @param session
+ */
+ public abstract void removeLocal(Session session);
+
+ /**
+ * Store the modified session.
+ *
+ * @param session
+ */
+ public abstract boolean storeSession(Session session);
+
+ public int getActiveSessions()
+ {
+ return activeCounter_;
+ }
+
+/*
+ public void add(Session session)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Session createEmptySession()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Session createSession()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Session findSession(String id) throws IOException
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Session[] findSessions()
+ {
+ return new Session[0]; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void remove(Session session)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+*/
+
+ public void load() throws ClassNotFoundException, IOException
+ {
+ // TODO. Implement persistence layer.
+ throw new RuntimeException("JBossManager.load(): Method not implemented.");
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ support_.removePropertyChangeListener(listener);
+ }
+
+ public void unload() throws IOException
+ {
+ // TODO. Implement persistence layer.
+ throw new RuntimeException("JBossManager.load(): Method not implemented.");
+ }
+
+
+ public void backgroundProcess()
+ {
+ // Called from Catalina StandardEngine for every 60 seconds.
+
+ long start = System.currentTimeMillis();
+
+ processExpires();
+
+ long elapsed = System.currentTimeMillis() - start;
+
+ processingTime_ += elapsed;
+ }
+
+ /**
+ * Go through all sessions and look if they have expired
+ */
+ protected void processExpires()
+ {
+ // What's the time?
+// long timeNow = System.currentTimeMillis();
+
+ // Get all sessions
+ Session sessions[] = findSessions();
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Looking for sessions that have expired ...");
+ }
+
+ for (int i = 0; i < sessions.length; ++i)
+ {
+ ClusteredSession session = (ClusteredSession) sessions[i];
+
+ // We only look at valid sessions. This will remove session if not valid already.
+ if (!session.isValid())
+ {
+ continue;
+ }
+
+ /* I don't think it is right to check idle time based on lastAccessedTime since it may
+ // remove some request that is currently in progress!!!
+ // How long are they allowed to be idle?
+ int maxInactiveInterval = session.getMaxInactiveInterval();
+
+ // Negative values = never expire
+ if( maxInactiveInterval < 0 )
+ {
+ continue;
+ }
+
+ // How long has this session been idle?
+ int timeIdle =
+ (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+
+ // Too long?
+ if( timeIdle >= maxInactiveInterval )
+ {
+ try
+ {
+ log_.debug("Session with id = " + session.getId() + " has expired on local node");
+ remove(session);
+ }
+ catch(Throwable t)
+ {
+ log_.error("Problems while expiring session with id = " + session.getId(), t);
+ }
+ }
+ */
+ }
+ }
+
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // TODO Need to handle it here.
+ }
+
+ /**
+ * Find in-memory sessions, if any.
+ * @return local session found. Sessions of size 0, if not found.
+ */
+ abstract public ClusteredSession[] findLocalSessions();
+
+ /**
+ * Find in-memory sessions, if any.
+ * @param realId the Session id without JvmRoute tag.
+ * @return local session found. Null if not found.
+ */
+ abstract public ClusteredSession findLocalSession(String realId);
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,159 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.web.tomcat.statistics.ReplicationStatistics;
+
+/**
+ * The MBean-interface for the JBossManager
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 57206 $
+ */
+public interface JBossManagerMBean
+{
+ /**
+ * Gets the replication statistics for the sessions managed
+ * by this manager.
+ *
+ * @return the statistics
+ */
+ ReplicationStatistics getReplicationStatistics();
+
+ /**
+ * Resets all statistics.
+ */
+ void resetStats();
+
+ /**
+ * Gets the elapsed time since this manager was instantiated or the
+ * last call to resetStats()
+ */
+ long timeInSecondsSinceLastReset();
+
+ /**
+ * Gets the number of sessions active on this node. Does not include
+ * replicated sessions that have not been accessed on this node.
+ */
+ long getActiveSessionCount();
+
+ /**
+ * Gets the number of times session creation has failed because the
+ * number of active sessions exceeds
+ * {@link #getMaxActiveAllowed() maxActiveAllowed}
+ */
+ long getRejectedSessionCount();
+
+ /**
+ * Gets the number of sessions created on this node. Does not include
+ * sessions initially created on other nodes, even if those sessions
+ * were accessed on this node.
+ */
+ long getCreatedSessionCount();
+
+ /**
+ * Gets the number of sessions that have been expired on this node.
+ */
+ long getExpiredSessionCount();
+
+ /**
+ * Gets the highest number of sessions concurrently active on this node.
+ * Does not include replicated sessions that have not been accessed on
+ * this node.
+ */
+ long getMaxActiveSessionCount();
+
+ /**
+ * Gets the maximum number of active sessions that will concurrently be
+ * allowed on this node. Does not include replicated sessions that have
+ * not been accessed on this node.
+ */
+ int getMaxActiveAllowed();
+
+ /**
+ * Sets the maximum number of active sessions that will concurrently be
+ * allowed on this node. Does not include replicated sessions that have
+ * not been accessed on this node.
+ *
+ * <p>
+ * Note that if sessions fail over to this node from other nodes, the max
+ * number of active sessions may exceed this value.
+ * </p>
+ *
+ * @param max the max number of sessions, or <code>-1</code> if there is
+ * no limit.
+ */
+ void setMaxActiveAllowed(int max);
+
+ /**
+ * Gets the maximum time interval, in seconds, between client requests
+ * after which sessions created by this manager should be expired. A
+ * negative time indicates that the session should never time out.
+ */
+ int getMaxInactiveInterval();
+
+ /**
+ * Sets the maximum time interval, in seconds, between client requests
+ * after which sessions created by this manager should be expired. A
+ * negative time indicates that the session should never time out.
+ *
+ * @param interval The new maximum interval
+ */
+ void setMaxInactiveInterval(int minutes);
+
+ /**
+ * Gets whether this manager's sessions are distributable.
+ */
+ boolean getDistributable();
+
+ /**
+ * Gets the cumulative number of milliseconds spent in the
+ * <code>Manager.backgroundProcess()</code> method.
+ */
+ long getProcessingTime();
+
+ /**
+ * Outputs the replication statistics as an HTML table, with one row
+ * per session.
+ */
+ String reportReplicationStatistics();
+
+ /**
+ * Outputs the replication statistics as a comma-separated-values, with one
+ * row per session. First row is a header listing field names.
+ */
+ String reportReplicationStatisticsCSV();
+
+ /**
+ * Outputs the replication statistics for the given session as a set of
+ * comma-separated-values. First row is a header listing field names.
+ */
+ String reportReplicationStatisticsCSV(String sessionId);
+
+ /**
+ * Gets the number of characters used in creating a session id. Excludes
+ * any jvmRoute.
+ */
+ int getSessionIdLength();
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,237 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.*;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+import org.jboss.logging.Logger;
+
+/**
+ * Web request valve to specifically handle Tomcat jvmRoute using mod_jk(2)
+ * module. We assume that the session is set by cookie only for now, i.e., no
+ * support of that from URL. Furthermore, the session id has a format of
+ * id.jvmRoute where jvmRoute is used by JK module to determine sticky session
+ * during load balancing.
+ *
+ * @author Ben Wang
+ * @version $Revision: 59035 $
+ */
+public class JvmRouteValve extends ValveBase implements Lifecycle
+{
+ // The info string for this Valve
+ private static final String info = "JvmRouteValve/1.0";
+
+ protected static Logger log_ = Logger.getLogger(JvmRouteValve.class);
+
+ // Valve-lifecycle_ helper object
+ protected LifecycleSupport support = new LifecycleSupport(this);
+
+ protected AbstractJBossManager manager_;
+
+ /**
+ * Create a new Valve.
+ *
+ */
+ public JvmRouteValve(AbstractJBossManager manager)
+ {
+ super();
+ manager_ = manager;
+ }
+
+ /**
+ * Get information about this Valve.
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+
+ // Need to check it before let it through. This is ok because this
+ // valve is inserted only when mod_jk option is configured.
+ checkJvmRoute(request, response);
+
+ // let the servlet invokation go through
+ getNext().invoke(request, response);
+ }
+
+ public void checkJvmRoute(Request req, Response res)
+ throws IOException, ServletException
+ {
+ String oldsessionId = req.getRequestedSessionId();
+ HttpSession session = req.getSession(false);
+ if (session != null)
+ {
+ String sessionId = session.getId();
+
+ // Obtain JvmRoute
+ String jvmRoute = manager_.getJvmRoute();
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("checkJvmRoute(): check if need to re-route based on JvmRoute. Session id: " +
+ sessionId + " jvmRoute: " + jvmRoute);
+ }
+
+ if (jvmRoute == null)
+ {
+ throw new RuntimeException("JvmRouteValve.checkJvmRoute(): Tomcat JvmRoute is null. " +
+ "Need to assign a value in Tomcat server.xml for load balancing.");
+ }
+
+ // Check if incoming session id has JvmRoute appended. If not, append it.
+ boolean setCookie = !req.isRequestedSessionIdFromURL();
+ handleJvmRoute(oldsessionId, sessionId, jvmRoute, res, setCookie);
+ }
+ }
+
+ protected void handleJvmRoute(String oldsessionId,
+ String sessionId,
+ String jvmRoute,
+ HttpServletResponse response,
+ boolean setCookie)
+ {
+ // Get requested jvmRoute.
+ // TODO. The current format is assumed to be id.jvmRoute. Can be generalized later.
+ String receivedJvmRoute = null;
+ int index = oldsessionId.lastIndexOf(".");
+ if (index > 0)
+ {
+ receivedJvmRoute = oldsessionId.substring(index + 1, sessionId.length());
+ }
+
+ String requestedJvmRoute = null;
+ index = sessionId.lastIndexOf(".");
+ if (index > 0)
+ {
+ requestedJvmRoute = sessionId.substring(index + 1, sessionId.length());
+ }
+
+ String newId = null;
+ if (!jvmRoute.equals(requestedJvmRoute))
+ {
+ if (requestedJvmRoute == null)
+ {
+ // If this valve is turned on, we assume we have an appendix of jvmRoute.
+ // So this request is new.
+ newId = sessionId + "." + jvmRoute;
+ }
+ else
+ {
+ // We just had a failover since jvmRoute does not match.
+ // We will replace the old one with the new one.
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): We have detected a failover with different jvmRoute." +
+ " old one: " + requestedJvmRoute + " new one: " + jvmRoute + ". Will reset the session id.");
+ }
+
+ String base = sessionId.substring(0, index);
+ newId = base + "." + jvmRoute;
+ }
+
+ resetSessionId(sessionId, newId);
+
+ }
+ /* Also check the jvmRoute received (via req.getRequestedSessionId()) */
+ if (!jvmRoute.equals(receivedJvmRoute))
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): We have detected a failover with different jvmRoute." +
+ " received one: " + receivedJvmRoute + " new one: " + jvmRoute + ". Will resent the session id.");
+ }
+ String base = sessionId.substring(0, index);
+ newId = base + "." + jvmRoute;
+ }
+
+ /* Change the sessionid cookie if needed */
+ if (setCookie && newId != null)
+ manager_.setNewSessionCookie(newId, response);
+ }
+
+ private void resetSessionId(String oldId, String newId)
+ {
+ try
+ {
+ ClusteredSession session = (ClusteredSession)manager_.findSession(oldId);
+ // change session id with the new one using local jvmRoute.
+ if( session != null )
+ {
+ // Note this will trigger a session remove from the super Tomcat class.
+ session.resetIdWithRouteInfo(newId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("resetSessionId(): changed catalina session to= [" + newId + "] old one= [" + oldId + "]");
+ }
+ }
+ else if (log_.isDebugEnabled())
+ {
+ log_.debug("resetSessionId(): no session with id " + newId + " found");
+ }
+ }
+ catch (IOException e)
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("resetSessionId(): manager_.findSession() unable to find session= [" + oldId + "]", e);
+ }
+ throw new RuntimeException("JvmRouteValve.resetSessionId(): cannot find session [" + oldId + "]", e);
+ }
+ }
+
+ // Lifecycle Interface
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ support.addLifecycleListener(listener);
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ support.removeLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return support.findLifecycleListeners();
+ }
+
+ public void start() throws LifecycleException
+ {
+ support.fireLifecycleEvent(START_EVENT, this);
+ }
+
+ public void stop() throws LifecycleException
+ {
+ support.fireLifecycleEvent(STOP_EVENT, this);
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,15 @@
+clusteredSession.attributeEvent=Session attribute event listener threw exception
+clusteredSession.invalidate.ise=invalidate: Session already invalidated
+clusteredSession.isNew.ise=isNew: Session already invalidated
+clusteredSession.getAttribute.ise=getAttribute: Session already invalidated
+clusteredSession.getAttributeNames.ise=getAttributeNames: Session already invalidated
+clusteredSession.getCreationTime.ise=getCreationTime: Session already invalidated
+clusteredSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: Session already invalidated
+clusteredSession.getValueNames.ise=getAttributeNames: Session already invalidated
+clusteredSession.notSerializable=Cannot serialize session attribute {0} for session {1}
+clusteredSession.removeAttribute.ise=removeAttribute: Session already invalidated
+clusteredSession.sessionEvent=Session event listener threw exception
+clusteredSession.setAttribute.iae=setAttribute: Non-serializable attribute
+clusteredSession.setAttribute.ise=setAttribute: Session already invalidated
+clusteredSession.setAttribute.namenull=setAttribute: name parameter cannot be null
+clusteredSession.sessionCreated=Created Session id = {0}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,15 @@
+clusteredSession.attributeEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u5c5e\u6027\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+clusteredSession.invalidate.ise=invalidate: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.isNew.ise=isNew: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getAttribute.ise=getAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getAttributeNames.ise=getAttributeNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getCreationTime.ise=getCreationTime: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getValueNames.ise=getAttributeNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.notSerializable=\u30bb\u30c3\u30b7\u30e7\u30f3 {1} \u306e\u305f\u3081\u306b\u30bb\u30c3\u30b7\u30e7\u30f3\u5c5e\u6027 {0} \u3092\u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3067\u304d\u307e\u305b\u3093
+clusteredSession.removeAttribute.ise=removeAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.sessionEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+clusteredSession.setAttribute.iae=setAttribute: \u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3067\u304d\u306a\u3044\u5c5e\u6027\u3067\u3059
+clusteredSession.setAttribute.ise=setAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.setAttribute.namenull=setAttribute: name\u30d1\u30e9\u30e1\u30bf\u306fnull\u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+clusteredSession.sessionCreated=\u30bb\u30c3\u30b7\u30e7\u30f3ID = {0} \u3092\u751f\u6210\u3057\u307e\u3057\u305f
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,194 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Map;
+
+
+/**
+ * Implementation of a clustered session for the JBossCacheManager. The replication granularity
+ * level is session based; that is, we replicate per whole session object.
+ * We use JBossCache for our internal replicated data store.
+ * The internal structure in JBossCache is as follows:
+ * <pre>
+ * /JSESSION
+ * /hostname
+ * /web_app_path (path + session id is unique)
+ * /id Map(id, session)
+ * (VERSION_KEY, version) // Used for version tracking. version is an Integer.
+ * </pre>
+ * <p/>
+ * Note that the isolation level of the cache dictates the
+ * concurrency behavior.</p>
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 57206 $
+ */
+class SessionBasedClusteredSession
+ extends JBossCacheClusteredSession
+{
+ static final long serialVersionUID = 3200976125245487256L;
+ /**
+ * Descriptive information describing this Session implementation.
+ */
+ protected static final String info = "SessionBasedClusteredSession/1.0";
+
+ public SessionBasedClusteredSession(JBossCacheManager manager)
+ {
+ super(manager);
+ }
+
+ // ---------------------------------------------- Overridden Public Methods
+
+ /**
+ * Return a string representation of this object.
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("SessionBasedClusteredSession[");
+ sb.append(super.toString());
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+ public void removeMyself()
+ {
+ proxy_.removeSession(realId);
+ }
+
+ public void removeMyselfLocal()
+ {
+ proxy_.removeSessionLocal(realId);
+ }
+
+ // ----------------------------------------------HttpSession Public Methods
+
+ /**
+ * Does nothing -- all attributes are populated already
+ */
+ protected void populateAttributes()
+ {
+ // no-op
+ }
+
+ protected Object getJBossInternalAttribute(String name)
+ {
+ Object result = attributes.get(name);
+
+ // Do dirty check even if result is null, as w/ SET_AND_GET null
+ // still makes us dirty (ensures timely replication w/o using ACCESS)
+ if (isGetDirty(result))
+ {
+ sessionAttributesDirty();
+ }
+
+ return result;
+
+ }
+
+ protected Object removeJBossInternalAttribute(String name,
+ boolean localCall,
+ boolean localOnly)
+ {
+ if (localCall)
+ sessionAttributesDirty();
+ return attributes.remove(name);
+ }
+
+ protected Map getJBossInternalAttributes()
+ {
+ return attributes;
+ }
+
+ protected Object setJBossInternalAttribute(String name, Object value)
+ {
+ sessionAttributesDirty();
+ return attributes.put(name, value);
+ }
+
+ /**
+ * Overrides the superclass version by additionally reading the
+ * attributes map.
+ *
+ * <p>
+ * This method is deliberately public so it can be used to reset
+ * the internal state of a session object using serialized
+ * contents replicated from another JVM via JBossCache.
+ * </p>
+ *
+ * @see org.jboss.web.tomcat.tc5.session.ClusteredSession#readExternal(java.io.ObjectInput)
+ */
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+ {
+ synchronized (this)
+ {
+ // Let superclass read in everything but the attribute map
+ super.readExternal(in);
+
+ attributes = (Map) in.readObject();
+ }
+ }
+
+ /**
+ * Overrides the superclass version by appending the attributes map. Does
+ * not write any attributes whose names are found in
+ * {@link ClusteredSession#excludedAttributes}.
+ *
+ * @see org.jboss.web.tomcat.tc5.session.ClusteredSession#writeExternal(java.io.ObjectOutput)
+ */
+ public void writeExternal(ObjectOutput out) throws IOException
+ {
+ synchronized (this)
+ {
+ // Let superclass write out everything but the attribute map
+ super.writeExternal(out);
+
+ // Don't replicate any excluded attributes
+ Map excluded = removeExcludedAttributes(attributes);
+
+ out.writeObject(attributes);
+
+ // Restore any excluded attributes
+ if (excluded != null)
+ attributes.putAll(excluded);
+ }
+
+ }
+
+
+ /**
+ * Overrides the superclass version to return <code>true</code> if either
+ * the metadata or the attributes are dirty.
+ */
+ public boolean getReplicateSessionBody()
+ {
+ return isSessionDirty() || getExceedsMaxUnreplicatedInterval();
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,239 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Random;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import org.jboss.logging.Logger;
+
+/**
+ * Unique session id generator
+ *
+ * @author Ben Wang
+ */
+public class SessionIDGenerator
+{
+ protected final static int SESSION_ID_BYTES = 16; // We want 16 Bytes for the session-id
+ protected final static String SESSION_ID_HASH_ALGORITHM = "MD5";
+ protected final static String SESSION_ID_RANDOM_ALGORITHM = "SHA1PRNG";
+ protected final static String SESSION_ID_RANDOM_ALGORITHM_ALT = "IBMSecureRandom";
+ protected Logger log = Logger.getLogger(SessionIDGenerator.class);
+
+ protected MessageDigest digest = null;
+ protected Random random = null;
+ protected static SessionIDGenerator s_;
+
+ protected String sessionIdAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*";
+
+ public static SessionIDGenerator getInstance()
+ {
+ if (s_ == null) s_ = new SessionIDGenerator();
+ return s_;
+ }
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public void setSessionIdAlphabet(String sessionIdAlphabet)
+ {
+ if (sessionIdAlphabet.length() != 65) {
+ throw new IllegalArgumentException("SessionIdAlphabet must be exactly 65 characters long");
+ }
+
+ checkDuplicateChars(sessionIdAlphabet);
+
+ this.sessionIdAlphabet = sessionIdAlphabet;
+ }
+
+ protected void checkDuplicateChars(String sessionIdAlphabet) {
+ char[] alphabet = sessionIdAlphabet.toCharArray();
+ for (int i=0; i < alphabet.length; i++) {
+ if (!uniqueChar(alphabet[i], sessionIdAlphabet)) {
+ throw new IllegalArgumentException("All chars in SessionIdAlphabet must be unique");
+ }
+ }
+ }
+
+ // does a character appear in the String once and only once?
+ protected boolean uniqueChar(char c, String s) {
+ int firstIndex = s.indexOf(c);
+ if (firstIndex == -1) return false;
+ return s.indexOf(c, firstIndex + 1) == -1;
+ }
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public String getSessionIdAlphabet() {
+ return this.sessionIdAlphabet;
+ }
+
+ public synchronized String getSessionId()
+ {
+ String id = generateSessionId();
+ if (log.isDebugEnabled())
+ log.debug("getSessionId called: " + id);
+ return id;
+ }
+
+
+ /**
+ * Generate a session-id that is not guessable
+ *
+ * @return generated session-id
+ */
+ protected synchronized String generateSessionId()
+ {
+ if (this.digest == null)
+ {
+ this.digest = getDigest();
+ }
+
+ if (this.random == null)
+ {
+ this.random = getRandom();
+ }
+
+ byte[] bytes = new byte[SESSION_ID_BYTES];
+
+ // get random bytes
+ this.random.nextBytes(bytes);
+
+ // Hash the random bytes
+ bytes = this.digest.digest(bytes);
+
+ // Render the result as a String of hexadecimal digits
+ return encode(bytes);
+ }
+
+ /**
+ * Encode the bytes into a String with a slightly modified Base64-algorithm
+ * This code was written by Kevin Kelley <kelley at ruralnet.net>
+ * and adapted by Thomas Peuss <jboss at peuss.de>
+ *
+ * @param data The bytes you want to encode
+ * @return the encoded String
+ */
+ protected String encode(byte[] data)
+ {
+ char[] out = new char[((data.length + 2) / 3) * 4];
+ char[] alphabet = this.sessionIdAlphabet.toCharArray();
+
+ //
+ // 3 bytes encode to 4 chars. Output is always an even
+ // multiple of 4 characters.
+ //
+ for (int i = 0, index = 0; i < data.length; i += 3, index += 4)
+ {
+ boolean quad = false;
+ boolean trip = false;
+
+ int val = (0xFF & (int) data[i]);
+ val <<= 8;
+ if ((i + 1) < data.length)
+ {
+ val |= (0xFF & (int) data[i + 1]);
+ trip = true;
+ }
+ val <<= 8;
+ if ((i + 2) < data.length)
+ {
+ val |= (0xFF & (int) data[i + 2]);
+ quad = true;
+ }
+ out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
+ val >>= 6;
+ out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
+ val >>= 6;
+ out[index + 1] = alphabet[val & 0x3F];
+ val >>= 6;
+ out[index + 0] = alphabet[val & 0x3F];
+ }
+ return new String(out);
+ }
+
+ /**
+ * get a random-number generator
+ *
+ * @return a random-number generator
+ */
+ protected synchronized Random getRandom()
+ {
+ long seed;
+ Random random = null;
+
+ // Mix up the seed a bit
+ seed = System.currentTimeMillis();
+ seed ^= Runtime.getRuntime().freeMemory();
+
+ try
+ {
+ random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ try
+ {
+ random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM_ALT);
+ }
+ catch (NoSuchAlgorithmException e_alt)
+ {
+ log.error("Could not generate SecureRandom for session-id randomness", e);
+ log.error("Could not generate SecureRandom for session-id randomness", e_alt);
+ return null;
+ }
+ }
+
+ // set the generated seed for this PRNG
+ random.setSeed(seed);
+
+ return random;
+ }
+
+ /**
+ * get a MessageDigest hash-generator
+ *
+ * @return a hash generator
+ */
+ protected synchronized MessageDigest getDigest()
+ {
+ MessageDigest digest = null;
+
+ try
+ {
+ digest = MessageDigest.getInstance(SESSION_ID_HASH_ALGORITHM);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ log.error("Could not generate MessageDigest for session-id hashing", e);
+ return null;
+ }
+
+ return digest;
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,301 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+public final class SessionReplicationContext
+{
+ private static final ThreadLocal replicationContext = new ThreadLocal();
+
+ private static final SessionReplicationContext EMPTY = new SessionReplicationContext();
+
+ private int webappCount;
+ private int activityCount;
+ private SnapshotManager soleManager;
+ private ClusteredSession soleSession;
+ private Map crossCtxSessions;
+ private Map expiredSessions;
+ private Request outerRequest;
+ private Response outerResponse;
+
+ /**
+ * Associate a SessionReplicationContext with the current thread, if
+ * there isn't one already. If there isn't one, associate the
+ * given request and response with the context.
+ * <p/>
+ * <strong>NOTE:</strong> Nested calls to this method and {@link #exitWebapp()}
+ * are supported; once a context is established the number of calls to this
+ * method and <code>exitWebapp()</code> are tracked.
+ *
+ * @param request
+ * @param response
+ */
+ public static void enterWebapp(Request request, Response response, boolean startCacheActivity)
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx == null)
+ {
+ ctx = new SessionReplicationContext(request, response);
+ replicationContext.set(ctx);
+ }
+
+ ctx.webappCount++;
+ if (startCacheActivity)
+ ctx.activityCount++;
+ }
+
+ /**
+ * Signals that the webapp is finished handling the request (and
+ * therefore replication can begin.)
+ *
+ * @return a SessionReplicationContext, from which information
+ * about any sessions needing replication can be obtained.
+ * Will not return <code>null</code>.
+ */
+ public static SessionReplicationContext exitWebapp()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null)
+ {
+ ctx.webappCount--;
+ if (ctx.webappCount < 1)
+ {
+ // We've unwound any nested webapp calls, so we'll clean up and
+ // return the context to allow replication. If all cache activity
+ // is done as well, clear the ThreadLocal
+
+ ctx.outerRequest = null;
+ ctx.outerResponse = null;
+
+ if (ctx.activityCount < 1)
+ replicationContext.set(null);
+
+ return ctx;
+ }
+ }
+
+ // A nested valve called us. Just return an empty context
+ return EMPTY;
+
+ }
+
+ public static void bindSession(ClusteredSession session, SnapshotManager manager)
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null && ctx.webappCount > 0)
+ {
+ ctx.addReplicatableSession(session, manager);
+ }
+ /*else {
+ We are past the part of the request cycle where we
+ track sessions for replication
+ }*/
+ }
+
+ public static void sessionExpired(ClusteredSession session, String realId, SnapshotManager manager)
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null && ctx.webappCount > 0)
+ {
+ ctx.addExpiredSession(session, manager);
+ }
+ }
+
+ public static boolean isSessionBoundAndExpired(String realId, SnapshotManager manager)
+ {
+ boolean result = false;
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null)
+ {
+ result = ctx.isSessionExpired(realId, manager);
+ }
+ return result;
+ }
+
+ /**
+ * Marks the current thread as actively processing the given session.
+ * If the thread has already been so marked, increases a counter
+ * so a subsequent call to finishLocalActivity does not remove
+ * the association (allowing nested calls).
+ */
+ public static void startCacheActivity()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx == null)
+ {
+ ctx = new SessionReplicationContext();
+ replicationContext.set(ctx);
+ }
+
+ ctx.activityCount++;
+ }
+
+ /**
+ * Marks the completion of activity on a given session. Should be called
+ * once for each invocation of {@link #startCacheActivity()}.
+ */
+ public static void finishCacheActivity()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null)
+ {
+ ctx.activityCount--;
+ if (ctx.activityCount < 1 && ctx.webappCount < 1)
+ {
+ replicationContext.set(null);
+ }
+ }
+ }
+
+ public static boolean isLocallyActive()
+ {
+ return getCurrentContext() != null;
+ }
+
+ public static Request getOriginalRequest()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ return (ctx == null ? null : ctx.outerRequest);
+ }
+
+ public static Response getOriginalResponse()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ return (ctx == null ? null : ctx.outerResponse);
+ }
+
+ private static SessionReplicationContext getCurrentContext()
+ {
+ return (SessionReplicationContext) replicationContext.get();
+ }
+
+ private SessionReplicationContext(Request request, Response response)
+ {
+ this.outerRequest = request;
+ this.outerResponse = response;
+ }
+
+ private SessionReplicationContext() {}
+
+ /**
+ * Gets a Map<SnapshotManager, ClusteredSession> of sessions that were accessed
+ * during the course of a request. Will only be non-null if
+ * {@link #bindSession(ClusteredSession, SnapshotManager)} was called
+ * with more than one SnapshotManager (i.e the request crossed session
+ * contexts.)
+ */
+ public Map getCrossContextSessions()
+ {
+ return crossCtxSessions;
+ }
+
+ /**
+ * Gets the SnapshotManager that was passed to
+ * {@link #bindSession(ClusteredSession, SnapshotManager)} if and only
+ * if only one such SnapshotManager was passed. Returns <code>null</code>
+ * otherwise, in which case a cross-context request is a possibility,
+ * and {@link #getCrossContextSessions()} should be checked.
+ */
+ public SnapshotManager getSoleSnapshotManager()
+ {
+ return soleManager;
+ }
+
+ /**
+ * Gets the ClusteredSession that was passed to
+ * {@link #bindSession(ClusteredSession, SnapshotManager)} if and only
+ * if only one SnapshotManager was passed. Returns <code>null</code>
+ * otherwise, in which case a cross-context request is a possibility,
+ * and {@link #getCrossContextSessions()} should be checked.
+ */
+ public ClusteredSession getSoleSession()
+ {
+ return soleSession;
+ }
+
+ private void addReplicatableSession(ClusteredSession session, SnapshotManager mgr)
+ {
+ if (crossCtxSessions != null)
+ {
+ crossCtxSessions.put(session, mgr);
+ }
+ else if (soleManager == null)
+ {
+ // First one bound
+ soleManager = mgr;
+ soleSession = session;
+ }
+ else if (!mgr.equals(soleManager))
+ {
+ // We have a cross-context call; need a Map for the sessions
+ crossCtxSessions = new HashMap();
+ crossCtxSessions.put(soleSession, soleManager);
+ crossCtxSessions.put(session, mgr);
+ soleManager = null;
+ soleSession = null;
+ }
+ else
+ {
+ soleSession = session;
+ }
+ }
+
+ private void addExpiredSession(ClusteredSession session, SnapshotManager manager)
+ {
+ boolean store = manager.equals(soleManager);
+ if (store)
+ {
+ soleManager = null;
+ soleSession = null;
+ }
+ else if (crossCtxSessions != null)
+ {
+ // Only store the session if it was previously in our map
+ store = (crossCtxSessions.remove(session) != null);
+ }
+
+ if (store)
+ {
+ if (this.expiredSessions == null)
+ {
+ expiredSessions = new HashMap();
+ }
+ expiredSessions.put(manager, session.getRealId());
+ }
+ }
+
+ private boolean isSessionExpired(String realId, SnapshotManager manager)
+ {
+ boolean result = false;
+ if (expiredSessions != null)
+ {
+ result = realId.equals(expiredSessions.get(manager));
+ }
+ return result;
+ }
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,85 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.logging.Logger;
+
+/**
+ * Abstract base class for a session snapshot manager.
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @version $Revision: 57206 $
+ */
+public abstract class SnapshotManager
+{
+ // The manager the snapshot manager should use
+ protected AbstractJBossManager manager;
+
+ // The context-path
+ protected String contextPath;
+
+ private Logger log;
+
+ public SnapshotManager(AbstractJBossManager manager, String path)
+ {
+ this.manager = manager;
+ contextPath = path;
+
+ String suffix = path;
+ if (suffix == null || "".equals(suffix))
+ suffix = "ROOT";
+ log = Logger.getLogger(getClass().getName() + "." + suffix);
+ }
+
+ /**
+ * Tell the snapshot manager which session was modified and
+ * must be replicated
+ */
+ public abstract void snapshot(ClusteredSession session);
+
+ /**
+ * Start the snapshot manager
+ */
+ public abstract void start();
+
+ /**
+ * Stop the snapshot manager
+ */
+ public abstract void stop();
+
+ protected String getContextPath()
+ {
+ return contextPath;
+ }
+
+ protected Logger getLog()
+ {
+ return log;
+ }
+
+ protected AbstractJBossManager getManager()
+ {
+ return manager;
+ }
+
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,147 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.session;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.aop.Advised;
+
+/**
+ * Utility methods related to JBoss distributed sessions.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class Util
+{
+ // Types that are considered "primitive".
+ private static final Set immediates =
+ new HashSet(Arrays.asList(new Object[]{
+ String.class,
+ Boolean.class,
+ Double.class,
+ Float.class,
+ Integer.class,
+ Long.class,
+ Short.class,
+ Character.class,
+ Boolean.TYPE,
+ Double.TYPE,
+ Float.TYPE,
+ Integer.TYPE,
+ Long.TYPE,
+ Short.TYPE,
+ Character.TYPE,
+ Class.class}));
+
+ /**
+ * Returns a session id with any trailing jvmRoute removed.
+ *
+ * @param sessionId the raw session id
+ *
+ * @return <code>sessionId</code> with the final '.' and any
+ * characters thereafter removed.
+ */
+ public static String getRealId(String sessionId)
+ {
+ int index = sessionId.lastIndexOf(".");
+ if (index > 0)
+ {
+ return sessionId.substring(0, index);
+ }
+ else
+ {
+ return sessionId;
+ }
+ }
+
+ /**
+ * Checks whether the given object is usable for FIELD granularity
+ * replication.
+ *
+ * @param pojo the pojo
+ * @return <code>true</code> if the attribute type is acceptable,
+ * <code>false</code> otherwise
+ */
+ public static boolean checkPojoType(Object pojo)
+ {
+ return ( (pojo instanceof Serializable)
+ || (pojo instanceof Collection)
+ || (pojo instanceof Map)
+ || (pojo instanceof Advised)
+ || (immediates.contains(pojo.getClass())));
+ }
+
+ public static Set parseComplexFields(Class clazz)
+ {
+ Set result = new HashSet();
+
+ while (clazz != null)
+ {
+ Field[] fields = clazz.getDeclaredFields();
+ for (int i = 0; i < fields.length; i++)
+ {
+ if (!immediates.contains(fields[i].getType())
+ && isReplicatable(fields[i]))
+ {
+ result.add(fields[i].getName());
+ }
+ }
+
+ clazz = clazz.getSuperclass();
+ }
+ return result;
+ }
+
+ /**
+ * Returns false if the given field is static, transient or final.
+ *
+ * @param f the field
+ * @return
+ */
+ public static boolean isReplicatable(Field f) {
+ int mods = f.getModifiers();
+ /**
+ * The following modifiers are ignored in the cache, i.e., they will not be stored in the cache.
+ * Whenever, user trying to access these fields, it will be accessed from the in-memory version.
+ */
+ if (Modifier.isStatic(mods)
+ || Modifier.isTransient(mods)
+ || Modifier.isFinal(mods)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Prevent instantiation.
+ */
+ private Util() {}
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1313 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.sso;
+
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.session.ManagerBase;
+import org.jboss.web.tomcat.service.JBossWeb;
+import org.jboss.web.tomcat.service.session.JBossManager;
+
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * A <strong>Valve</strong> that supports a "single sign on" user experience,
+ * where the security identity of a user who successfully authenticates to one
+ * web application is propogated to other web applications in the same
+ * security domain. For successful use, the following requirements must
+ * be met:
+ * <ul>
+ * <li>This Valve must be configured on the Container that represents a
+ * virtual host (typically an implementation of <code>Host</code>).</li>
+ * <li>The <code>Realm</code> that contains the shared user and role
+ * information must be configured on the same Container (or a higher
+ * one), and not overridden at the web application level.</li>
+ * <li>The web applications themselves must use one of the standard
+ * Authenticators found in the
+ * <code>org.apache.catalina.authenticator</code> package.</li>
+ * </ul>
+ *
+ * @author Brian E. Stansberry based on the work of Craig R. McClanahan
+ * @version $Revision: 57329 $ $Date: 2006-10-02 00:35:46 +0200 (lun., 02 oct. 2006) $
+ */
+public class ClusteredSingleSignOn
+ extends org.apache.catalina.authenticator.SingleSignOn
+ implements LifecycleListener
+{
+ /** By default we process expired SSOs no more often than once per minute */
+ public static final int DEFAULT_PROCESS_EXPIRES_INTERVAL = 60;
+ /** By default we let SSOs without active sessions live for 30 mins */
+ public static final int DEFAULT_MAX_EMPTY_LIFE = 1800;
+
+ // Override the superclass value
+ static
+ {
+ info = ClusteredSingleSignOn.class.getName();
+ }
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * Fully qualified name of a class implementing
+ * {@link SSOClusterManager SSOClusterManager} that will be used
+ * to manage SSOs across a cluster.
+ */
+ private String clusterManagerClass =
+ TreeCacheSSOClusterManager.class.getName();
+
+ /**
+ * Object used to provide cross-cluster support for single sign on.
+ */
+ private SSOClusterManager ssoClusterManager = null;
+
+ /**
+ * Object name of the tree cache used by SSOClusterManager.
+ * Only relevant if the SSOClusterManager implementation is
+ * TreeCacheSSOClusterManager.
+ */
+ private String treeCacheName = JBossWeb.DEFAULT_CACHE_NAME;
+
+// DISABLED UNTIL 4.0.6.CR1
+// /**
+// * Object name of the thread pool used by SSOClusterManager.
+// * Only relevant if the SSOClusterManager implementation is
+// * TreeCacheSSOClusterManager.
+// */
+// private String threadPoolName = "jboss.system:service=ThreadPool";
+
+ /** Currently started Managers that have associated as session with an SSO */
+ private Set activeManagers = Collections.synchronizedSet(new HashSet());
+
+ /** Max number of ms an SSO with no active sessions will be usable by a request */
+ private int maxEmptyLife = DEFAULT_MAX_EMPTY_LIFE * 1000;
+
+ /**
+ * Minimum number of ms since the last processExpires() run
+ * before a new run is allowed.
+ */
+ private int processExpiresInterval = DEFAULT_PROCESS_EXPIRES_INTERVAL * 1000;
+
+ /** Timestamp of the last processExpires() run */
+ private long lastProcessExpires = System.currentTimeMillis();
+
+ /**
+ * Map<String, Long> containing the ids of SSOs with no active sessions
+ * and the time at which they entered that state
+ */
+ private Map emptySSOs = new ConcurrentHashMap();
+
+ /** Used for sync locking of processExpires runs */
+ private final Object mutex = new Object();
+
+ // ------------------------------------------------------------- Properties
+
+ /**
+ * Gets the object that provides SSO support across a cluster.
+ *
+ * @return the object provided cluster support, or <code>null</code> if
+ * no such object has been configured.
+ */
+ public SSOClusterManager getClusterManager()
+ {
+ return this.ssoClusterManager;
+ }
+
+
+ /**
+ * Sets the object that provides SSO support across a cluster.
+ *
+ * @param clusterManager the object that provides SSO support.
+ * @throws IllegalStateException if this method is invoked after this valve
+ * has been started.
+ */
+ public void setClusterManager(SSOClusterManager clusterManager)
+ {
+ if (started && (clusterManager != ssoClusterManager))
+ {
+ throw new IllegalStateException("already started -- cannot set a " +
+ "new SSOClusterManager");
+ }
+
+ this.ssoClusterManager = clusterManager;
+
+ if (clusterManager != null)
+ {
+ clusterManagerClass = clusterManager.getClass().getName();
+ }
+ }
+
+
+ /**
+ * Gets the name of the class that will be used to provide SSO support
+ * across a cluster.
+ *
+ * @return Fully qualified name of a class implementing
+ * {@link SSOClusterManager SSOClusterManager}
+ * that is being used to manage SSOs across a cluster.
+ * May return <code>null</code> (the default) if clustered
+ * SSO support is not configured.
+ */
+ public String getClusterManagerClass()
+ {
+ return clusterManagerClass;
+ }
+
+
+ /**
+ * Sets the name of the class that will be used to provide SSO support
+ * across a cluster.
+ * <p><b>NOTE: </b>
+ * If this Valve has already started, and no SSOClusterManager has been
+ * configured for it, calling this method will
+ *
+ * @param managerClass Fully qualified name of a class implementing
+ * {@link SSOClusterManager SSOClusterManager}
+ * that will be used to manage SSOs across a cluster.
+ * Class must declare a public no-arguments
+ * constructor. <code>null</code> is allowed.
+ */
+ public void setClusterManagerClass(String managerClass)
+ {
+ if (!started)
+ {
+ clusterManagerClass = managerClass;
+ }
+ else if (ssoClusterManager == null)
+ {
+ try
+ {
+ createClusterManager(managerClass);
+ }
+ catch (LifecycleException e)
+ {
+ getContainer().getLogger().error("Exception creating SSOClusterManager " +
+ managerClass, e);
+ }
+ }
+ else
+ {
+ getContainer().getLogger().error("Cannot set clusterManagerClass to " + managerClass +
+ "; already started using " + clusterManagerClass);
+ }
+ }
+
+ /**
+ * Object name of the tree cache used by SSOClusterManager.
+ * Only relevant if the SSOClusterManager implementation is
+ * TreeCacheSSOClusterManager.
+ */
+ public String getTreeCacheName()
+ {
+ return treeCacheName;
+ }
+
+ /**
+ * Sets the object name of the tree cache used by SSOClusterManager.
+ * Only relevant if the SSOClusterManager implementation is
+ * TreeCacheSSOClusterManager.
+ */
+ public void setTreeCacheName(String cacheName)
+ throws Exception
+ {
+ this.treeCacheName = cacheName;
+ if (ssoClusterManager != null
+ && ssoClusterManager instanceof TreeCacheSSOClusterManager)
+ {
+ ((TreeCacheSSOClusterManager) ssoClusterManager).setCacheName(cacheName);
+ }
+ }
+
+// DISABLED UNTIL 4.0.6.CR1
+// /**
+// * Object name of the thread pool used by SSOClusterManager.
+// * Only relevant if the SSOClusterManager implementation is
+// * TreeCacheSSOClusterManager.
+// */
+// public String getThreadPoolName()
+// {
+// return threadPoolName;
+// }
+//
+// /**
+// * Sets the object name of the thread pool used by SSOClusterManager.
+// * Only relevant if the SSOClusterManager implementation is
+// * TreeCacheSSOClusterManager.
+// */
+// public void setThreadPoolName(String poolName)
+// throws Exception
+// {
+// this.threadPoolName = poolName;
+// if (ssoClusterManager != null
+// && ssoClusterManager instanceof TreeCacheSSOClusterManager)
+// {
+// ((TreeCacheSSOClusterManager) ssoClusterManager).setThreadPoolName(poolName);
+// }
+// }
+
+ /**
+ * Gets the max number of seconds an SSO with no active sessions will be
+ * usable by a request.
+ *
+ * @return a non-negative number
+ *
+ * @see #DEFAULT_MAX_EMPTY_LIFE *
+ * @see #setMaxEmptyLife()
+ */
+ public int getMaxEmptyLife()
+ {
+ return (maxEmptyLife / 1000);
+ }
+
+
+ /**
+ * Sets the maximum number of seconds an SSO with no active sessions will be
+ * usable by a request.
+ * <p>
+ * A positive value for this property allows a user to continue to use an SSO
+ * even after all the sessions associated with it have been expired. It does not
+ * keep an SSO alive if a session associated with it has been invalidated due to
+ * an <code>HttpSession.invalidate()</code> call.
+ * </p>
+ * <p>
+ * The primary purpose of this property is to avoid the situation where a server
+ * on which all of an SSO's sessions lives is shutdown, thus expiring all the
+ * sessions and causing the invalidation of the SSO. A positive value for this
+ * property would give the user an opportunity to fail over to another server
+ * and maintain the SSO.
+ * </p>
+ *
+ * @param maxEmptyLife a non-negative number
+ *
+ * @throws IllegalArgumentException if <code>maxEmptyLife < 0</code>
+ */
+ public void setMaxEmptyLife(int maxEmptyLife)
+ {
+ if (maxEmptyLife < 0)
+ throw new IllegalArgumentException("maxEmptyLife must be >= 0");
+
+ this.maxEmptyLife = maxEmptyLife * 1000;
+ }
+
+
+ /**
+ * Gets the minimum number of seconds since the start of the last check for overaged
+ * SSO's with no active sessions before a new run is allowed.
+ *
+ * @return a positive number
+ *
+ * @see #DEFAULT_PROCESS_EXPIRES_INTERVAL
+ * @see #setMaxEmptyLife()
+ * @see #setProcessExpiresInterval(int)
+ */
+ public int getProcessExpiresInterval()
+ {
+ return processExpiresInterval / 1000;
+ }
+
+ /**
+ * Sets the minimum number of seconds since the start of the last check for overaged
+ * SSO's with no active sessions before a new run is allowed. During this check,
+ * any such overaged SSOs will be invalidated.
+ * <p>
+ * Note that setting this value does not imply that a check will be performed
+ * every <code>processExpiresInterval</code> seconds, only that it will not
+ * be performed more often than that.
+ * </p>
+ *
+ * @param processExpiresInterval a non-negative number. <code>0</code> means
+ * the overage check can be performed whenever
+ * the container wishes to.
+ *
+ * @throws IllegalArgumentException if <code>processExpiresInterval < 1</code>
+ *
+ * @see #setMaxEmptyLife()
+ */
+ public void setProcessExpiresInterval(int processExpiresInterval)
+ {
+ if (processExpiresInterval < 0)
+ throw new IllegalArgumentException("processExpiresInterval must be >= 0");
+
+ this.processExpiresInterval = processExpiresInterval * 1000;
+ }
+
+
+ /**
+ * Gets the timestamp of the start of the last check for overaged
+ * SSO's with no active sessions.
+ *
+ * @see #setProcessExpiresInterval(int)
+ */
+ public long getLastProcessExpires()
+ {
+ return lastProcessExpires;
+ }
+
+
+ // ------------------------------------------------------ Lifecycle Methods
+
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after <code>configure()</code>,
+ * and before any of the public methods of the component are utilized.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ public void start() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (started)
+ {
+ throw new LifecycleException
+ (sm.getString("authenticator.alreadyStarted"));
+ }
+
+ // Attempt to create an SSOClusterManager
+ createClusterManager(getClusterManagerClass());
+
+ lifecycle.fireLifecycleEvent(START_EVENT, null);
+ started = true;
+
+ if (ssoClusterManager != null)
+ {
+ ssoClusterManager.start();
+ }
+
+ }
+
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that needs to be reported
+ */
+ public void stop() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (!started)
+ {
+ throw new LifecycleException
+ (sm.getString("authenticator.notStarted"));
+ }
+
+ if (ssoClusterManager != null)
+ {
+ ssoClusterManager.stop();
+ }
+
+ lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+ started = false;
+
+ }
+
+
+ // ------------------------------------------------ SessionListener Methods
+
+
+ /**
+ * Updates the state of a single sign on session to reflect the destruction
+ * of a standard HTTP session.
+ * <p/>
+ * If the given event is a {@link Session#SESSION_DESTROYED_EVENT
+ * Session destroyed event}, checks whether the session was destroyed due
+ * to timeout or user action (i.e. logout). If due to timeout, disassociates
+ * the Session from the single sign on session. If due to logout, invokes
+ * the {@link #logout} method.
+ *
+ * @param event SessionEvent that has occurred
+ */
+ public void sessionEvent(SessionEvent event)
+ {
+ // We only care about session destroyed events
+ if (!Session.SESSION_DESTROYED_EVENT.equals(event.getType()))
+ return;
+
+ // Look up the single session id associated with this session (if any)
+ Session session = event.getSession();
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Process session destroyed on " + session);
+
+ String ssoId = null;
+ synchronized (reverse)
+ {
+ ssoId = (String) reverse.get(session);
+ }
+ if (ssoId == null)
+ return;
+
+ try
+ {
+ // Was the session destroyed as the result of a timeout or
+ // the undeployment of the containing webapp?
+ // If so, we'll just remove the expired session from the
+ // SSO. If the session was logged out, we'll log out
+ // of all sessions associated with the SSO.
+ if (isSessionTimedOut(session) || isManagerStopped(session))
+ {
+ removeSession(ssoId, session);
+
+ // Quite poor. We hijack the caller thread (the Tomcat background thread)
+ // to do our cleanup of expired sessions
+ processExpires();
+ }
+ else
+ {
+ // The session was logged out.
+ logout(ssoId);
+ }
+ }
+ catch (Exception e)
+ {
+ // Don't propagate back to the webapp; we don't want to disrupt
+ // the session expiration process
+ getContainer().getLogger().error("Caught exception updating SSO " + ssoId +
+ " following destruction of session " +
+ session.getIdInternal(), e);
+ }
+ }
+
+ private boolean isSessionTimedOut(Session session)
+ {
+ return (session.getMaxInactiveInterval() > 0)
+ && (System.currentTimeMillis() - session.getLastAccessedTime() >=
+ session.getMaxInactiveInterval() * 1000);
+ }
+
+ private boolean isManagerStopped(Session session)
+ {
+ boolean stopped = false;
+
+ Manager manager = session.getManager();
+
+ if (manager instanceof ManagerBase)
+ {
+ ObjectName mgrName = ((ManagerBase)manager).getObjectName();
+ stopped = (!activeManagers.contains(mgrName));
+ }
+ else if (manager instanceof JBossManager)
+ {
+ ObjectName mgrName = ((JBossManager)manager).getObjectName();
+ stopped = (!activeManagers.contains(mgrName));
+ }
+ else if (manager instanceof Lifecycle)
+ {
+ stopped = (!activeManagers.contains(manager));
+ }
+ // else we have no way to tell, so assume not
+
+ return stopped;
+ }
+
+ // ---------------------------------------------- LifecycleListener Methods
+
+
+ public void lifecycleEvent(LifecycleEvent event)
+ {
+ String type = event.getType();
+ if (Lifecycle.BEFORE_STOP_EVENT.equals(type)
+ || Lifecycle.STOP_EVENT.equals(type)
+ || Lifecycle.AFTER_STOP_EVENT.equals(type))
+ {
+ Lifecycle source = event.getLifecycle();
+ boolean removed;
+ if (source instanceof ManagerBase)
+ {
+ removed = activeManagers.remove(((ManagerBase)source).getObjectName());
+ }
+ else if (source instanceof JBossManager)
+ {
+ removed = activeManagers.remove(((JBossManager)source).getObjectName());
+ }
+ else
+ {
+ removed = activeManagers.remove(source);
+ }
+
+ if (removed)
+ {
+ source.removeLifecycleListener(this);
+
+ if (getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug("ClusteredSSO: removed " +
+ "stopped manager " + source.toString());
+ }
+ }
+
+ // TODO consider getting the sessions and removing any from our sso's
+ // Idea is to cleanup after managers that don't destroy sessions
+
+ }
+ }
+
+
+ // ---------------------------------------------------------- Valve Methods
+
+
+ /**
+ * Perform single-sign-on support processing for this request.
+ * <p/>
+ * Overrides the superclass version by handling the fact that a
+ * single sign on may have been originated on another cluster node and
+ * thus may not have a <code>Principal</code> object associated with it
+ * on this node.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ * @param context The valve context used to invoke the next valve
+ * in the current processing pipeline
+ * @throws IOException if an input/output error occurs
+ * @throws ServletException if a servlet error occurs
+ */
+ public void invoke(Request request, Response response)
+ throws IOException, ServletException
+ {
+ request.removeNote(Constants.REQ_SSOID_NOTE);
+
+ // Has a valid user already been authenticated?
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Process request for '" + request.getRequestURI() + "'");
+ if (request.getUserPrincipal() != null)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug(" Principal '" + request.getUserPrincipal().getName() +
+ "' has already been authenticated");
+ getNext().invoke(request, response);
+ return;
+ }
+
+ // Check for the single sign on cookie
+ Cookie cookie = null;
+ Cookie cookies[] = request.getCookies();
+ if (cookies == null)
+ cookies = new Cookie[0];
+ for (int i = 0; i < cookies.length; i++)
+ {
+ if (Constants.SINGLE_SIGN_ON_COOKIE.equals(cookies[i].getName()))
+ {
+ cookie = cookies[i];
+ break;
+ }
+ }
+ if (cookie == null)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug(" SSO cookie is not present");
+ getNext().invoke(request, response);
+ return;
+ }
+
+ // Look up the cached Principal associated with this cookie value
+ String ssoId = cookie.getValue();
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug(" Checking for cached principal for " + ssoId);
+ SingleSignOnEntry entry = getSingleSignOnEntry(cookie.getValue());
+ if (entry != null && isValid(ssoId, entry))
+ {
+ Principal ssoPrinc = entry.getPrincipal();
+ // have to deal with the fact that the entry may not have an
+ // associated Principal. SSO entries retrieved via a lookup from a
+ // cluster will not have a Principal, as Principal is not Serializable
+ if (getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug(" Found cached principal '" +
+ (ssoPrinc == null ? "NULL" : ssoPrinc.getName()) +
+ "' with auth type '" + entry.getAuthType() + "'");
+ }
+ request.setNote(Constants.REQ_SSOID_NOTE, cookie.getValue());
+ // Only set security elements if per-request reauthentication is
+ // not required AND the SSO entry had a Principal.
+ if (!getRequireReauthentication() && ssoPrinc != null)
+ {
+ request.setAuthType(entry.getAuthType());
+ request.setUserPrincipal(ssoPrinc);
+ }
+ }
+ else
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug(" No cached principal found, erasing SSO cookie");
+ cookie.setMaxAge(0);
+ response.addCookie(cookie);
+ }
+
+ // Invoke the next Valve in our pipeline
+ getNext().invoke(request, response);
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Associate the specified single sign on identifier with the
+ * specified Session.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of any new association of SSO and Session.
+ *
+ * @param ssoId Single sign on identifier
+ * @param session Session to be associated
+ */
+ protected void associate(String ssoId, Session session)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Associate sso id " + ssoId + " with session " + session);
+
+ SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+ boolean added = false;
+ if (sso != null)
+ added = sso.addSession(this, session);
+
+ synchronized (reverse)
+ {
+ reverse.put(session, ssoId);
+ }
+
+ // If we made a change, track the manager and notify any cluster
+ if (added)
+ {
+ Manager manager = session.getManager();
+
+ // Prefer to cache an ObjectName to avoid risk of leaking a manager,
+ // so if the manager exposes one, use it
+ Object mgrKey = null;
+ if (manager instanceof ManagerBase)
+ {
+ mgrKey = ((ManagerBase)manager).getObjectName();
+ }
+ else if (manager instanceof JBossManager)
+ {
+ mgrKey = ((JBossManager)manager).getObjectName();
+ }
+ else if (manager instanceof Lifecycle)
+ {
+ mgrKey = manager;
+ }
+ else {
+ getContainer().getLogger().warn("Manager for session " +
+ session.getIdInternal() +
+ " does not implement Lifecycle; web app shutdown may " +
+ " lead to incorrect SSO invalidations");
+ }
+
+ if (mgrKey != null)
+ {
+ synchronized (activeManagers)
+ {
+ if (!activeManagers.contains(mgrKey))
+ {
+ activeManagers.add(mgrKey);
+ ((Lifecycle) manager).addLifecycleListener(this);
+ }
+ }
+ }
+
+ if (ssoClusterManager != null)
+ ssoClusterManager.addSession(ssoId, session);
+ }
+ }
+
+
+ /**
+ * Deregister the specified session. If it is the last session,
+ * then also get rid of the single sign on identifier.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of any disassociation of SSO and Session.
+ *
+ * @param ssoId Single sign on identifier
+ * @param session Session to be deregistered
+ */
+ protected void deregister(String ssoId, Session session)
+ {
+ synchronized (reverse)
+ {
+ reverse.remove(session);
+ }
+
+ SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+ if (sso == null)
+ return;
+
+ boolean removed = sso.removeSession(session);
+ // If we changed anything, notify any cluster
+ if (removed && ssoClusterManager != null)
+ {
+ ssoClusterManager.removeSession(ssoId, session);
+ }
+
+ // see if this was the last session on this node,
+ // if remove sso entry from our local cache
+ if (sso.getSessionCount() == 0)
+ {
+ synchronized (cache)
+ {
+ sso = (SingleSignOnEntry) cache.remove(ssoId);
+ }
+ }
+ }
+
+
+ /**
+ * Deregister the specified single sign on identifier, and invalidate
+ * any associated sessions.
+ *
+ * @param ssoId Single sign on identifier to deregister
+ */
+ protected void deregister(String ssoId)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Deregistering sso id '" + ssoId + "'");
+
+
+ // It's possible we don't have the SSO locally but it's in
+ // the emptySSOs map; if so remove it
+ emptySSOs.remove(ssoId);
+
+ // Look up and remove the corresponding SingleSignOnEntry
+ SingleSignOnEntry sso = null;
+ synchronized (cache)
+ {
+ sso = (SingleSignOnEntry) cache.remove(ssoId);
+ }
+
+ if (sso == null)
+ return;
+
+ // Expire any associated sessions
+ Session sessions[] = sso.findSessions();
+ for (int i = 0; i < sessions.length; i++)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug(" Invalidating session " + sessions[i]);
+ // Remove from reverse cache first to avoid recursion
+ synchronized (reverse)
+ {
+ reverse.remove(sessions[i]);
+ }
+ // Invalidate this session
+ sessions[i].expire();
+ }
+
+ // NOTE: Clients may still possess the old single sign on cookie,
+ // but it will be removed on the next request since it is no longer
+ // in the cache
+ }
+
+
+ /**
+ * Deregister the given SSO, invalidating any associated sessions, then
+ * notify any cluster of the logout.
+ *
+ * @param ssoId the id of the SSO session
+ */
+ protected void logout(String ssoId)
+ {
+ deregister(ssoId);
+ // broadcast logout to any cluster
+ if (ssoClusterManager != null)
+ ssoClusterManager.logout(ssoId);
+ }
+
+
+ /**
+ * Look up and return the cached SingleSignOn entry associated with this
+ * sso id value, if there is one; otherwise return <code>null</code>.
+ *
+ * @param ssoId Single sign on identifier to look up
+ */
+ protected SingleSignOnEntry getSingleSignOnEntry(String ssoId)
+ {
+ SingleSignOnEntry sso = localLookup(ssoId);
+ // If we don't have one locally and there is a cluster,
+ // query the cluster for the SSO
+ if (sso == null && ssoClusterManager != null)
+ {
+ sso = ssoClusterManager.lookup(ssoId);
+ if (sso != null)
+ {
+ // Store it locally
+ synchronized (cache)
+ {
+ cache.put(ssoId, sso);
+ }
+ }
+ }
+
+ return sso;
+ }
+
+
+ /**
+ * Attempts reauthentication to the given <code>Realm</code> using
+ * the credentials associated with the single sign-on session
+ * identified by argument <code>ssoId</code>.
+ * <p/>
+ * If reauthentication is successful, the <code>Principal</code> and
+ * authorization type associated with the SSO session will be bound
+ * to the given <code>HttpRequest</code> object via calls to
+ * {@link HttpRequest#setAuthType HttpRequest.setAuthType()} and
+ * {@link HttpRequest#setUserPrincipal HttpRequest.setUserPrincipal()}
+ * </p>
+ *
+ * @param ssoId identifier of SingleSignOn session with which the
+ * caller is associated
+ * @param realm Realm implementation against which the caller is to
+ * be authenticated
+ * @param request the request that needs to be authenticated
+ * @return <code>true</code> if reauthentication was successful,
+ * <code>false</code> otherwise.
+ */
+ protected boolean reauthenticate(String ssoId, Realm realm,
+ Request request)
+ {
+ if (ssoId == null || realm == null)
+ return false;
+
+ boolean reauthenticated = false;
+
+ SingleSignOnEntry entry = getSingleSignOnEntry(ssoId);
+ if (entry != null && entry.getCanReauthenticate())
+ {
+
+ String username = entry.getUsername();
+ if (username != null)
+ {
+ Principal reauthPrincipal =
+ realm.authenticate(username, entry.getPassword());
+ if (reauthPrincipal != null)
+ {
+ reauthenticated = true;
+ // Bind the authorization credentials to the request
+ request.setAuthType(entry.getAuthType());
+ request.setUserPrincipal(reauthPrincipal);
+ // JBAS-2314 -- bind principal to the entry as well
+ entry.setPrincipal(reauthPrincipal);
+ }
+ }
+ }
+
+ return reauthenticated;
+ }
+
+
+ /**
+ * Register the specified Principal as being associated with the specified
+ * value for the single sign on identifier.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of the registration.
+ *
+ * @param ssoId Single sign on identifier to register
+ * @param principal Associated user principal that is identified
+ * @param authType Authentication type used to authenticate this
+ * user principal
+ * @param username Username used to authenticate this user
+ * @param password Password used to authenticate this user
+ */
+ protected void register(String ssoId, Principal principal, String authType,
+ String username, String password)
+ {
+ registerLocal(ssoId, principal, authType, username, password);
+
+ // broadcast change to any cluster
+ if (ssoClusterManager != null)
+ ssoClusterManager.register(ssoId, authType, username, password);
+ }
+
+
+ /**
+ * Remove a single Session from a SingleSignOn. Called when
+ * a session is timed out and no longer active.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of any disassociation of SSO and Session.
+ *
+ * @param ssoId Single sign on identifier from which to remove the session.
+ * @param session the session to be removed.
+ */
+ protected void removeSession(String ssoId, Session session)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Removing session " + session.toString() +
+ " from sso id " + ssoId);
+
+ // Get a reference to the SingleSignOn
+ SingleSignOnEntry entry = getSingleSignOnEntry(ssoId);
+ if (entry == null)
+ return;
+
+ // Remove the inactive session from SingleSignOnEntry
+ boolean removed = entry.removeSession(session);
+ // If we changed anything, notify any cluster
+ if (removed && ssoClusterManager != null)
+ {
+ ssoClusterManager.removeSession(ssoId, session);
+ }
+
+ // Remove the inactive session from the 'reverse' Map.
+ synchronized (reverse)
+ {
+ reverse.remove(session);
+ }
+
+// // If there are no sessions left in the SingleSignOnEntry,
+// // deregister the entry.
+// if (entry.getSessionCount() == 0)
+// {
+// deregister(ssoId);
+// }
+ }
+
+
+ /**
+ * Updates any <code>SingleSignOnEntry</code> found under key
+ * <code>ssoId</code> with the given authentication data.
+ * <p/>
+ * The purpose of this method is to allow an SSO entry that was
+ * established without a username/password combination (i.e. established
+ * following DIGEST or CLIENT-CERT authentication) to be updated with
+ * a username and password if one becomes available through a subsequent
+ * BASIC or FORM authentication. The SSO entry will then be usable for
+ * reauthentication.
+ * <p/>
+ * <b>NOTE:</b> Only updates the SSO entry if a call to
+ * <code>SingleSignOnEntry.getCanReauthenticate()</code> returns
+ * <code>false</code>; otherwise, it is assumed that the SSO entry already
+ * has sufficient information to allow reauthentication and that no update
+ * is needed.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of any update.
+ *
+ * @param ssoId identifier of Single sign to be updated
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ protected void update(String ssoId, Principal principal, String authType,
+ String username, String password)
+ {
+ boolean needToBroadcast = updateLocal(ssoId, principal, authType,
+ username, password);
+
+ // if there was a change, broadcast it to any cluster
+ if (needToBroadcast && ssoClusterManager != null)
+ {
+ ssoClusterManager.updateCredentials(ssoId, authType,
+ username, password);
+ }
+ }
+
+ //---------------------------------------------- Package-Protected Methods
+
+ /**
+ * Search in our local cache for an SSO entry.
+ *
+ * @param ssoId the id of the SSO session
+ * @return any SingleSignOnEntry associated with the given id, or
+ * <code>null</code> if there is none.
+ */
+ SingleSignOnEntry localLookup(String ssoId)
+ {
+ synchronized (cache)
+ {
+ return ((SingleSignOnEntry) cache.get(ssoId));
+ }
+
+ }
+
+ /**
+ * Create a SingleSignOnEntry using the passed configuration parameters and
+ * register it in the local cache, bound to the given id.
+ *
+ * @param ssoId the id of the SSO session
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ void registerLocal(String ssoId, Principal principal, String authType,
+ String username, String password)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug("Registering sso id '" + ssoId + "' for user '" +
+ principal.getName() + "' with auth type '" + authType + "'");
+ }
+
+ synchronized (cache)
+ {
+ cache.put(ssoId, new SingleSignOnEntry(principal, authType,
+ username, password));
+ }
+ }
+
+ /**
+ * Updates any <code>SingleSignOnEntry</code> found under key
+ * <code>ssoId</code> with the given authentication data.
+ *
+ * @param ssoId identifier of Single sign to be updated
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ * @return <code>true</code> if the update resulted in an actual change
+ * to the entry's authType, username or principal properties
+ */
+ boolean updateLocal(String ssoId, Principal principal, String authType,
+ String username, String password)
+ {
+ boolean shouldBroadcast = false;
+
+ SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+ // Only update if the entry is missing information
+ if (sso != null)
+ {
+ if (sso.getCanReauthenticate() == false)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Update sso id " + ssoId + " to auth type " + authType);
+
+ synchronized (sso)
+ {
+ shouldBroadcast = sso.updateCredentials(principal, authType,
+ username, password);
+ }
+ }
+ else if (sso.getPrincipal() == null && principal != null)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Update sso id " + ssoId + " with principal " +
+ principal.getName());
+
+ synchronized (sso)
+ {
+ sso.setPrincipal(principal);
+ // No need to notify cluster; Principals don't replicate
+ }
+ }
+
+ }
+
+ return shouldBroadcast;
+
+ }
+
+ void remoteUpdate(String ssoId, String authType,
+ String username, String password)
+ {
+ SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+ // Only update if the entry is missing information
+ if (sso != null && sso.getCanReauthenticate() == false)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Update sso id " + ssoId + " to auth type " + authType);
+
+ synchronized (sso)
+ {
+ // Use the existing principal
+ Principal p = sso.getPrincipal();
+ sso.updateCredentials(p, authType, username, password);
+ }
+ }
+
+ }
+
+ /**
+ * Callback from the SSOManager when it detects an SSO without
+ * any active sessions across the cluster
+ */
+ void notifySSOEmpty(String ssoId)
+ {
+ Object obj = emptySSOs.put(ssoId, new Long(System.currentTimeMillis()));
+
+ if (obj == null && getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug("Notified that SSO " + ssoId + " is empty");
+ }
+ }
+
+ /**
+ * Callback from the SSOManager when it detects an SSO that
+ * has active sessions across the cluster
+ */
+ void notifySSONotEmpty(String ssoId)
+ {
+ Object obj = emptySSOs.remove(ssoId);
+
+ if (obj != null && getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug("Notified that SSO " + ssoId +
+ " is no longer empty");
+ }
+ }
+
+
+ // ------------------------------------------------------- Private Methods
+
+
+ /**
+ * Instantiates an instance of the given class, making it this valve's
+ * SSOClusterManager.
+ * <p/>
+ * If this valve has been started and the given class implements
+ * <code>Lifecycle</code>, starts the new SSOClusterManager.
+ *
+ * @param className fully qualified class name of an implementation
+ * of {@link SSOClusterManager SSOClusterManager}.
+ * @throws LifecycleException if there is any problem instantiating or
+ * starting the object, or if the created
+ * object does not implement
+ * <code>SSOClusterManger</code>
+ */
+ private void createClusterManager(String className)
+ throws LifecycleException
+ {
+ if (ssoClusterManager != null)
+ return;
+
+ if (className != null)
+ {
+ SSOClusterManager mgr = null;
+ try
+ {
+ ClassLoader tcl =
+ Thread.currentThread().getContextClassLoader();
+ Class clazz = tcl.loadClass(className);
+ mgr = (SSOClusterManager) clazz.newInstance();
+ mgr.setSingleSignOnValve(this);
+ if (mgr instanceof TreeCacheSSOClusterManager)
+ {
+ ((TreeCacheSSOClusterManager) mgr).setCacheName(getTreeCacheName());
+// DISABLED UNTIL 4.0.6.CR1
+// ((TreeCacheSSOClusterManager) mgr).setThreadPoolName(getThreadPoolName());
+ }
+ ssoClusterManager = mgr;
+ clusterManagerClass = className;
+ }
+ catch (Throwable t)
+ {
+ throw new LifecycleException("Cannot create " +
+ "SSOClusterManager using " +
+ className, t);
+ }
+
+ if (started)
+ {
+ ssoClusterManager.start();
+ }
+ }
+ }
+
+
+ private void processExpires()
+ {
+ long now = 0L;
+ synchronized (mutex)
+ {
+ now = System.currentTimeMillis();
+
+ if (now - lastProcessExpires > processExpiresInterval)
+ {
+ lastProcessExpires = now;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ clearExpiredSSOs(now);
+ }
+
+ private synchronized void clearExpiredSSOs(long now)
+ {
+ for (Iterator iter = emptySSOs.entrySet().iterator(); iter.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ if ( (now - ((Long) entry.getValue()).longValue()) > maxEmptyLife)
+ {
+ String ssoId = (String) entry.getKey();
+ if (getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug("Invalidating expired SSO " + ssoId);
+ }
+ logout(ssoId);
+ }
+ }
+ }
+
+ private boolean isValid(String ssoId, SingleSignOnEntry entry)
+ {
+ boolean valid = true;
+ if (entry.getSessionCount() == 0)
+ {
+ Long expired = (Long) emptySSOs.get(ssoId);
+ if (expired != null
+ && (System.currentTimeMillis() - expired.longValue()) > maxEmptyLife)
+ {
+ valid = false;
+
+ if (getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug("Invalidating expired SSO " + ssoId);
+ }
+
+ logout(ssoId);
+ }
+ }
+
+ return valid;
+ }
+
+}
\ No newline at end of file
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,121 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.sso;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.Lifecycle;
+
+/**
+ * Provides communications support between a SingleSignOn valve and other
+ * such valves configured for the same hostname within a server cluster.
+ * <p/>
+ * Implementations of this interface must declare a public no-arguments
+ * constructor.
+ *
+ * @author Brian E. Stansberry
+ * @version $Revision: 57206 $ $Date: 2006-09-26 14:25:30 +0200 (mar., 26 sept. 2006) $
+ * @see ClusteredSingleSignOn
+ */
+public interface SSOClusterManager
+ extends Lifecycle
+{
+
+ /**
+ * Notify the cluster of the addition of a Session to an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param session the Session that has been added
+ */
+ void addSession(String ssoId, Session session);
+
+ /**
+ * Gets the SingleSignOn valve for which this object is handling
+ * cluster communications.
+ *
+ * @return the <code>SingleSignOn</code> valve.
+ */
+ ClusteredSingleSignOn getSingleSignOnValve();
+
+ /**
+ * Sets the SingleSignOn valve for which this object is handling
+ * cluster communications.
+ * <p><b>NOTE:</b> This method must be called before calls can be
+ * made to the other methods of this interface.
+ *
+ * @param valve a <code>SingleSignOn</code> valve.
+ */
+ void setSingleSignOnValve(ClusteredSingleSignOn valve);
+
+ /**
+ * Notifies the cluster that a single sign on session has been terminated
+ * due to a user logout.
+ *
+ * @param ssoId the id of the SSO session
+ */
+ void logout(String ssoId);
+
+ /**
+ * Queries the cluster for the existence of a SSO session with the given
+ * id, returning a <code>SingleSignOnEntry</code> if one is found.
+ *
+ * @param ssoId the id of the SSO session
+ * @return a <code>SingleSignOnEntry</code> created using information
+ * found on another cluster node, or <code>null</code> if no
+ * entry could be found.
+ */
+ SingleSignOnEntry lookup(String ssoId);
+
+ /**
+ * Notifies the cluster of the creation of a new SSO entry.
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ void register(String ssoId, String authType, String username,
+ String password);
+
+ /**
+ * Notify the cluster of the removal of a Session from an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param session the Session that has been removed
+ */
+ void removeSession(String ssoId, Session session);
+
+ /**
+ * Notifies the cluster of an update of the security credentials
+ * associated with an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ void updateCredentials(String ssoId, String authType, String username,
+ String password);
+
+
+}
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,264 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.sso;
+
+import java.security.Principal;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.authenticator.SingleSignOn;
+
+/**
+ * A class that represents entries in the cache of authenticated users.
+ *
+ * @author Brian E. Stansberry, based on work by Craig R. McClanahan
+ * @version $Revision: 41072 $ $Date: 2006-02-09 08:47:17 +0100 (jeu., 09 févr. 2006) $
+ * @see SingleSignOn
+ */
+class SingleSignOnEntry
+{
+ // ------------------------------------------------------ Instance Fields
+
+ private String authType = null;
+
+ private String password = null;
+
+ private Principal principal = null;
+
+ private Session sessions[] = new Session[0];
+
+ private String username = null;
+
+ private boolean canReauthenticate = false;
+
+ // --------------------------------------------------------- Constructors
+
+ /**
+ * Creates a new SingleSignOnEntry
+ *
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ SingleSignOnEntry(Principal principal, String authType,
+ String username, String password)
+ {
+ updateCredentials(principal, authType, username, password);
+ }
+
+ // ------------------------------------------------------- Package Methods
+
+ /**
+ * Adds a <code>Session</code> to the list of those associated with
+ * this SSO.
+ *
+ * @param sso The <code>SingleSignOn</code> valve that is managing
+ * the SSO session.
+ * @param session The <code>Session</code> being associated with the SSO.
+ * @return <code>true</code> if the given Session was a new addition (i.e.
+ * was not previously associated with this entry);
+ * <code>false</code> otherwise.
+ */
+ synchronized boolean addSession(SingleSignOn sso, Session session)
+ {
+ for (int i = 0; i < sessions.length; i++)
+ {
+ if (session == sessions[i])
+ return false;
+ }
+ Session results[] = new Session[sessions.length + 1];
+ System.arraycopy(sessions, 0, results, 0, sessions.length);
+ results[sessions.length] = session;
+ sessions = results;
+ session.addSessionListener(sso);
+ return true;
+ }
+
+ /**
+ * Removes the given <code>Session</code> from the list of those
+ * associated with this SSO.
+ *
+ * @param session the <code>Session</code> to remove.
+ * @return <code>true</code> if the given Session needed to be removed
+ * (i.e. was in fact previously associated with this entry);
+ * <code>false</code> otherwise.
+ */
+ synchronized boolean removeSession(Session session)
+ {
+ if (sessions.length == 0)
+ return false;
+
+ boolean removed = false;
+ Session[] nsessions = new Session[sessions.length - 1];
+ for (int i = 0, j = 0; i < sessions.length; i++)
+ {
+ if (session == sessions[i])
+ {
+ removed = true;
+ continue;
+ }
+ else if (!removed && i == nsessions.length)
+ {
+ // We have tested all our sessions, and have not had to
+ // remove any; break loop now so we don't cause an
+ // ArrayIndexOutOfBounds on nsessions
+ break;
+ }
+ nsessions[j++] = sessions[i];
+ }
+ sessions = nsessions;
+ // Only if we removed a session, do we replace our session list
+ if (removed)
+ sessions = nsessions;
+ return removed;
+ }
+
+ /**
+ * Returns the <code>Session</code>s associated with this SSO.
+ */
+ synchronized Session[] findSessions()
+ {
+ return (this.sessions);
+ }
+
+ /**
+ * Gets the name of the authentication type originally used to authenticate
+ * the user associated with the SSO.
+ *
+ * @return "BASIC", "CLIENT-CERT", "DIGEST", "FORM" or "NONE"
+ */
+ String getAuthType()
+ {
+ return (this.authType);
+ }
+
+ /**
+ * Gets whether the authentication type associated with the original
+ * authentication supports reauthentication.
+ *
+ * @return <code>true</code> if <code>getAuthType</code> returns
+ * "BASIC" or "FORM", <code>false</code> otherwise.
+ */
+ boolean getCanReauthenticate()
+ {
+ return (this.canReauthenticate);
+ }
+
+ /**
+ * Gets the password credential (if any) associated with the SSO.
+ *
+ * @return the password credential associated with the SSO, or
+ * <code>null</code> if the original authentication type
+ * does not involve a password.
+ */
+ String getPassword()
+ {
+ return (this.password);
+ }
+
+ /**
+ * Gets the <code>Principal</code> that has been authenticated by
+ * the SSO.
+ * <p/>
+ * <b>NOTE: </b> May return <code>null</code> if this object was
+ * retrieved via a lookup from another node in a cluster. Interface
+ * <code>Principal</code> does not extend <code>Serializable</code>,
+ * so a <code>SingleSignOnEntry</code>'s principal member cannot be
+ * serialized as part of SSO management in a cluster. A
+ * <code>Principal</code> cannot be bound to a
+ * <code>SingleSignOnEntry</code> until the SSO has been authenticated
+ * by the local node.
+ *
+ * @return The <code>Principal</code> that has been authenticated by
+ * the local SSO, or <code>null</code> if no authentication
+ * has been performed yet in this cluster node.
+ */
+ Principal getPrincipal()
+ {
+ return (this.principal);
+ }
+
+ /**
+ * Sets the <code>Principal</code> that has been authenticated by
+ * the SSO.
+ */
+ void setPrincipal(Principal principal)
+ {
+ this.principal = principal;
+ }
+
+ /**
+ * Returns the number of sessions associated with this SSO, either
+ * locally or remotely.
+ */
+ int getSessionCount()
+ {
+ return (sessions.length);
+ }
+
+ /**
+ * Gets the username provided by the user as part of the authentication
+ * process.
+ */
+ String getUsername()
+ {
+ return (this.username);
+ }
+
+
+ /**
+ * Updates the SingleSignOnEntry to reflect the latest security
+ * information associated with the caller.
+ *
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ synchronized boolean updateCredentials(Principal principal, String authType,
+ String username, String password)
+ {
+
+ boolean changed =
+ (safeEquals(this.principal, principal)
+ || safeEquals(this.authType, authType)
+ || safeEquals(this.username, username)
+ || safeEquals(this.password, password));
+
+ this.principal = principal;
+ this.authType = authType;
+ this.username = username;
+ this.password = password;
+ this.canReauthenticate =
+ (Constants.BASIC_METHOD.equals(authType)
+ || Constants.FORM_METHOD.equals(authType));
+ return changed;
+ }
+
+ // ------------------------------------------------------- Private Methods
+
+ private boolean safeEquals(Object a, Object b)
+ {
+ return ((a == b)
+ || (a != null && a.equals(b))
+ || (b != null && b.equals(a)));
+ }
+}
\ No newline at end of file
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,1547 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.sso;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.management.AttributeNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Session;
+import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.cache.AbstractTreeCacheListener;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.TreeCacheListener;
+import org.jboss.cache.config.Option;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.util.NestedRuntimeException;
+import org.jboss.util.threadpool.ThreadPool;
+import org.jboss.util.threadpool.ThreadPoolMBean;
+import org.jboss.web.tomcat.service.JBossWeb;
+import org.jgroups.Address;
+import org.jgroups.View;
+
+/**
+ * An implementation of SSOClusterManager that uses a TreeCache
+ * to share SSO information between cluster nodes.
+ *
+ * @author Brian E. Stansberry
+ * @version $Revision: 57330 $ $Date: 2006-10-02 00:36:06 +0200 (lun., 02 oct. 2006) $
+ */
+public final class TreeCacheSSOClusterManager
+ extends AbstractTreeCacheListener
+ implements SSOClusterManager
+{
+ // ------------------------------------------------------------- Constants
+
+ /**
+ * Final segment of any FQN that names a TreeCache node storing
+ * SSO credential information.
+ */
+ private static final String CREDENTIALS = "credentials";
+
+ /**
+ * First segment of any FQN that names a TreeCache node associated
+ * with an SSO
+ */
+ private static final String SSO = "SSO";
+
+ /**
+ * Final segment of any FQN that names a TreeCache node storing
+ * the set of Sessions associated with an SSO.
+ */
+ private static final String SESSIONS = "sessions";
+
+ /**
+ * Key under which data is stored to the TreeCache.
+ */
+ private static final String KEY = "key";
+
+ /**
+ * Default global value for the cacheName property
+ */
+ public static final String DEFAULT_GLOBAL_CACHE_NAME =
+ JBossWeb.DEFAULT_CACHE_NAME;
+
+ /**
+ * Parameter signature used for TreeCache.get calls over JMX
+ */
+ private static final String[] GET_SIGNATURE =
+ {Fqn.class.getName(), Object.class.getName(), Option.class.getName()};
+
+ /**
+ * Parameter signature used for TreeCache.put calls over JMX
+ */
+ private static final String[] PUT_SIGNATURE =
+ {Fqn.class.getName(), Object.class.getName(),
+ Object.class.getName(), Option.class.getName()};
+
+ /**
+ * Parameter signature used for TreeCache.remove calls over JMX
+ */
+ private static final String[] REMOVE_SIGNATURE =
+ {Fqn.class.getName(), Option.class.getName()};
+
+ private static final Option GRAVITATE_OPTION = new Option();
+ private static final Option LOCAL_OPTION = new Option();
+
+ static
+ {
+ GRAVITATE_OPTION.setForceDataGravitation(true);
+ LOCAL_OPTION.setCacheModeLocal(true);
+ }
+
+
+ // ------------------------------------------------------- Instance Fields
+
+ /**
+ * SSO id which the thread is currently storing to the cache
+ */
+ private ThreadLocal beingLocallyAdded = new ThreadLocal();
+
+ /**
+ * SSO id which a thread is currently removing from the cache
+ */
+ private ThreadLocal beingLocallyRemoved = new ThreadLocal();
+
+ /**
+ * SSO id which the thread is deregistering due to removal on another node
+ */
+ private ThreadLocal beingRemotelyRemoved = new ThreadLocal();
+
+ /**
+ * ObjectName of the TreeCache
+ */
+ private ObjectName cacheObjectName = null;
+
+// DISABLED UNTIL 4.0.6.CR1
+// private String threadPoolName = "jboss.system:service=ThreadPool";
+//
+// private ThreadPool threadPool;
+
+ /**
+ * String version of the object name to use to access the TreeCache
+ */
+ private String cacheName = null;
+
+ /**
+ * Transaction Manager
+ */
+ private TransactionManager tm = null;
+
+ /**
+ * The lifecycle event support for this component.
+ */
+ private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+ /**
+ * The Log-object for this class
+ */
+ private Logger log = Logger.getLogger(getClass().getName());;
+
+ /**
+ * Whether we are registered as a TreeCacheListener anywhere
+ */
+ private boolean registeredAsListener = false;
+
+ /**
+ * The MBean server we use to access our TreeCache
+ */
+ private MBeanServer server = null;
+
+ /**
+ * The SingleSignOn for which we are providing cluster support
+ */
+ private ClusteredSingleSignOn ssoValve = null;
+
+ /**
+ * Whether we have been started
+ */
+ private boolean started = false;
+
+ /**
+ * Whether a valid TreeCache is available for use
+ */
+ private boolean treeCacheAvailable = false;
+
+ /**
+ * Whether we have logged an error due to not having a valid cache
+ */
+ private boolean missingCacheErrorLogged = false;
+
+ /**
+ * Our node's address in the cluster.
+ */
+ private Serializable localAddress = null;
+
+// DISABLED UNTIL 4.0.6.CR1
+// /** The last view passed to viewAccepted() */
+// private View currentView;
+//
+// /** Mutex lock to ensure only one view change at a time is being processed */
+// private Object cleanupMutex = new Object();
+
+ // ---------------------------------------------------------- Constructors
+
+
+ /**
+ * Creates a new TreeCacheSSOClusterManager
+ */
+ public TreeCacheSSOClusterManager()
+ {
+ // Find our MBeanServer
+ server = MBeanServerLocator.locateJBoss();
+ if (server == null)
+ server = MBeanServerLocator.locate();
+ }
+
+
+ /**
+ * Creates a new TreeCacheSSOClusterManager that works with the given
+ * MBeanServer. This constructor is only intended for use in unit testing.
+ */
+ public TreeCacheSSOClusterManager(MBeanServer server)
+ {
+ this.server = server;
+ }
+
+
+ // ------------------------------------------------------------ Properties
+
+ public String getCacheName()
+ {
+ return cacheName;
+ }
+
+ public void setCacheName(String objectName)
+ throws Exception
+ {
+ if (objectName == null)
+ {
+ setCacheObjectName(null);
+ }
+ else if (objectName.equals(cacheName) == false)
+ {
+ setCacheObjectName(new ObjectName(objectName));
+ }
+ }
+
+ public ObjectName getCacheObjectName()
+ {
+ return cacheObjectName;
+ }
+
+ public void setCacheObjectName(ObjectName objectName)
+ throws Exception
+ {
+ // If no change, do nothing
+ if ((objectName != null && objectName.equals(cacheObjectName))
+ || (cacheObjectName != null && cacheObjectName.equals(objectName))
+ || (objectName == null && cacheObjectName == null))
+ {
+ return;
+ }
+
+ removeAsTreeCacheListener();
+ this.tm = null;
+
+ this.cacheObjectName = objectName;
+ this.cacheName = (objectName == null
+ ? null
+ : objectName.getCanonicalName());
+
+ if (false == isTreeCacheAvailable(true))
+ {
+ if (started)
+ {
+ logMissingCacheError();
+ }
+ else
+ {
+ // Just put an advice in the log
+ log.info("Cannot find TreeCache using " + cacheName + " -- tree" +
+ "CacheName must be set to point to a running TreeCache " +
+ "before ClusteredSingleSignOn can handle requests");
+ }
+ }
+ }
+
+// DISABLED UNTIL 4.0.6.CR1
+// public String getThreadPoolName()
+// {
+// return threadPoolName;
+// }
+//
+//
+// public void setThreadPoolName(String threadPoolName)
+// {
+// if (started)
+// {
+// log.info("Call to setThreadPoolName() ignored; already started");
+// }
+// else
+// {
+// this.threadPoolName = threadPoolName;
+// }
+// }
+
+ // ----------------------------------------------------- SSOClusterManager
+
+
+ /**
+ * Notify the cluster of the addition of a Session to an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param session the Session that has been added
+ */
+ public void addSession(String ssoId, Session session)
+ {
+ if (ssoId == null || session == null)
+ {
+ return;
+ }
+
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("addSession(): adding Session " + session.getId() +
+ " to cached session set for SSO " + ssoId);
+ }
+
+ Fqn fqn = getSessionsFqn(ssoId);
+ boolean doTx = false;
+ try
+ {
+ // Confirm we have a transaction manager; if not get it from TreeCache
+ // failure to find will throw an IllegalStateException
+ if (tm == null)
+ configureFromCache();
+
+ // Don't do anything if there is already a transaction
+ // context associated with this thread.
+ if(tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ Set sessions = getSessionSet(fqn, true, false);
+ sessions.add(new SessionAddress(session.getId(), localAddress));
+ putInTreeCache(fqn, sessions, false);
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if(doTx)
+ tm.setRollbackOnly();
+ }
+ catch (Exception ignored)
+ {
+ }
+ String sessId = (session == null ? "NULL" : session.getId());
+ log.error("caught exception adding session " + sessId +
+ " to SSO id " + ssoId, e);
+ }
+ finally
+ {
+ if (doTx)
+ endTransaction();
+ }
+ }
+
+
+ /**
+ * Gets the SingleSignOn valve for which this object is handling
+ * cluster communications.
+ *
+ * @return the <code>SingleSignOn</code> valve.
+ */
+ public ClusteredSingleSignOn getSingleSignOnValve()
+ {
+ return ssoValve;
+ }
+
+
+ /**
+ * Sets the SingleSignOn valve for which this object is handling
+ * cluster communications.
+ * <p><b>NOTE:</b> This method must be called before calls can be
+ * made to the other methods of this interface.
+ *
+ * @param valve a <code>SingleSignOn</code> valve.
+ */
+ public void setSingleSignOnValve(ClusteredSingleSignOn valve)
+ {
+ ssoValve = valve;
+ }
+
+
+ /**
+ * Notifies the cluster that a single sign on session has been terminated
+ * due to a user logout.
+ *
+ * @param ssoId
+ */
+ public void logout(String ssoId)
+ {
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ // Check whether we are already handling this removal
+ if (ssoId.equals(beingLocallyRemoved.get()))
+ {
+ return;
+ }
+
+ // Add this SSO to our list of in-process local removals so
+ // this.nodeRemoved() will ignore the removal
+ beingLocallyRemoved.set(ssoId);
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("Registering logout of SSO " + ssoId +
+ " in clustered cache");
+ }
+
+ Fqn fqn = getSingleSignOnFqn(ssoId);
+
+ try
+ {
+ removeFromTreeCache(fqn);
+ }
+ catch (Exception e)
+ {
+ log.error("Exception attempting to remove node " +
+ fqn.toString() + " from TreeCache", e);
+ }
+ finally
+ {
+ beingLocallyRemoved.set(null);
+ }
+ }
+
+
+ /**
+ * Queries the cluster for the existence of an SSO session with the given
+ * id, returning a <code>SingleSignOnEntry</code> if one is found.
+ *
+ * @param ssoId the id of the SSO session
+ * @return a <code>SingleSignOnEntry</code> created using information
+ * found on another cluster node, or <code>null</code> if no
+ * entry could be found.
+ */
+ public SingleSignOnEntry lookup(String ssoId)
+ {
+ if (!checkTreeCacheAvailable())
+ {
+ return null;
+ }
+
+ SingleSignOnEntry entry = null;
+ // Find the latest credential info from the cluster
+ Fqn fqn = getCredentialsFqn(ssoId);
+ try
+ {
+ SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn, false);
+ if (data != null)
+ {
+ entry = new SingleSignOnEntry(null,
+ data.getAuthType(),
+ data.getUsername(),
+ data.getPassword());
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("caught exception looking up SSOCredentials for SSO id " +
+ ssoId, e);
+ }
+ return entry;
+ }
+
+
+ /**
+ * Notifies the cluster of the creation of a new SSO entry.
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ public void register(String ssoId, String authType,
+ String username, String password)
+ {
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("Registering SSO " + ssoId + " in clustered cache");
+ }
+
+ storeSSOData(ssoId, authType, username, password);
+ }
+
+
+ /**
+ * Notify the cluster of the removal of a Session from an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param session the Session that has been removed
+ */
+ public void removeSession(String ssoId, Session session)
+ {
+ if (ssoId == null || session == null)
+ {
+ return;
+ }
+
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ // Check that this session removal is not due to our own deregistration
+ // of an SSO following receipt of a nodeRemoved() call
+ if (ssoId.equals(beingRemotelyRemoved.get()))
+ {
+ return;
+ }
+
+ Fqn fqn = getSessionsFqn(ssoId);
+ boolean doTx = false;
+// boolean removing = false;
+ try
+ {
+ // Confirm we have a transaction manager; if not get it from TreeCache
+ // failure to find will throw an IllegalStateException
+ if (tm == null)
+ configureFromCache();
+
+ // Don't do anything if there is already a transaction
+ // context associated with this thread.
+ if(tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ Set sessions = getSessionSet(fqn, false, false);
+ if (sessions != null)
+ {
+ sessions.remove(new SessionAddress(session.getId(), localAddress));
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("removed session " + session.getId() +
+ " from cached session set for SSO " + ssoId + " -- " +
+ sessions.size() + " sessions remain");
+ }
+
+// if (sessions.size() == 0)
+// {
+// // No sessions left; remove node
+//
+// // Add this SSO to our list of in-process local removals so
+// // this.nodeRemoved() will ignore the removal
+// removing = true;
+// beingLocallyRemoved.set(ssoId);
+// removeFromTreeCache(getSingleSignOnFqn(ssoId));
+// }
+// else
+// {
+ putInTreeCache(fqn, sessions, false);
+// }
+ }
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if(doTx)
+ tm.setRollbackOnly();
+ }
+ catch (Exception x)
+ {
+ }
+
+ String sessId = (session == null ? "NULL" : session.getId());
+ log.error("caught exception removing session " + sessId +
+ " from SSO id " + ssoId, e);
+ }
+ finally
+ {
+// try
+// {
+// if (removing)
+// {
+// beingLocallyRemoved.set(null);
+// }
+// }
+// finally
+// {
+ if (doTx)
+ endTransaction();
+// }
+ }
+ }
+
+
+ /**
+ * Notifies the cluster of an update of the security credentials
+ * associated with an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ public void updateCredentials(String ssoId, String authType,
+ String username, String password)
+ {
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("Updating credentials for SSO " + ssoId +
+ " in clustered cache");
+ }
+
+ storeSSOData(ssoId, authType, username, password);
+ }
+
+
+ // ------------------------------------------------------ TreeCacheListener
+
+ /**
+ * Extracts an SSO session id from the Fqn and uses it in an invocation of
+ * {@link ClusteredSingleSignOn#deregister(String) ClusteredSingleSignOn.deregister(String)}.
+ * <p/>
+ * Ignores invocations resulting from TreeCache changes originated by
+ * this object.
+ *
+ * @param fqn the fully-qualified name of the node that was removed
+ */
+ public void nodeRemoved(Fqn fqn)
+ {
+ String ssoId = getIdFromFqn(fqn);
+
+ if (ssoId == null)
+ return;
+
+ // Ignore messages generated by our own activity
+ if (ssoId.equals(beingLocallyRemoved.get()))
+ {
+ return;
+ }
+
+ beingRemotelyRemoved.set(ssoId);
+
+ try
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("received a node removed message for SSO " + ssoId);
+ }
+
+ ssoValve.deregister(ssoId);
+ }
+ finally
+ {
+ beingRemotelyRemoved.set(null);
+ }
+
+ }
+
+
+ /**
+ * Extracts an SSO session id from the Fqn and uses it in an invocation of
+ * {@link ClusteredSingleSignOn#update ClusteredSingleSignOn.update()}.
+ * <p/>
+ * Only responds to modifications of nodes whose FQN's final segment is
+ * "credentials".
+ * <p/>
+ * Ignores invocations resulting from TreeCache changes originated by
+ * this object.
+ * <p/>
+ * Ignores invocations for SSO session id's that are not registered
+ * with the local SingleSignOn valve.
+ *
+ * @param fqn the fully-qualified name of the node that was modified
+ */
+ public void nodeModified(Fqn fqn)
+ {
+ String type = getTypeFromFqn(fqn);
+ if (CREDENTIALS.equals(type))
+ {
+ handleCredentialUpdate(fqn);
+ }
+ else if (SESSIONS.equals(type))
+ {
+ handleSessionSetChange(fqn);
+ }
+ }
+
+
+ /**
+ *
+ * @param fqn an Fqn that points to the CREDENTIALS node of an SSO
+ */
+ private void handleCredentialUpdate(Fqn fqn)
+ {
+ String ssoId = getIdFromFqn(fqn); // won't be null per the API contract
+
+ // Ignore invocations that come as a result of our additions
+ if (ssoId.equals(beingLocallyAdded.get()))
+ {
+ return;
+ }
+
+ SingleSignOnEntry sso = ssoValve.localLookup(ssoId);
+ if (sso == null || sso.getCanReauthenticate())
+ {
+ // No reason to update
+ return;
+ }
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("received a credentials modified message for SSO " + ssoId);
+ }
+
+ // Put this SSO in the queue of those to be updated
+// credentialUpdater.enqueue(sso, ssoId);
+ try
+ {
+ SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn, false);
+ if (data != null)
+ {
+ // We want to release our read lock quickly, so get the needed
+ // data from the cache, commit the tx, and then use the data
+ String authType = data.getAuthType();
+ String username = data.getUsername();
+ String password = data.getPassword();
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("CredentialUpdater: Updating credentials for SSO " + sso);
+ }
+
+ synchronized (sso)
+ {
+ // Use the existing principal
+ Principal p = sso.getPrincipal();
+ sso.updateCredentials(p, authType, username, password);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("failed to update credentials for SSO " + ssoId, e);
+ }
+ }
+
+ /**
+ *
+ * @param fqn an Fqn that points to the SESSIONS node of an SSO
+ */
+ private void handleSessionSetChange(Fqn fqn)
+ {
+ // Here we *want* to handle local activity as well as remote,
+ // as the distributed cache set of sessions is the only
+ // complete representation
+
+ String ssoId = getIdFromFqn(fqn);
+ try
+ {
+ Set sessions = (Set) getFromTreeCache(fqn, false);
+ if (sessions == null)
+ {
+ // don't think we'd get this event;
+ // in any case we don't deal with this
+ }
+ else if (sessions.size() == 0)
+ {
+ ssoValve.notifySSOEmpty(ssoId);
+ }
+ else
+ {
+ ssoValve.notifySSONotEmpty(ssoId);
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("failed in check for empty SSO " + ssoId, e);
+ }
+ }
+
+// DISABLE UNTIL 4.0.6.CR1
+// /**
+// * If any nodes have been removed from the view, asynchronously scans
+// * all SSOs looking for and removing sessions owned by the removed node.
+// * Notifies the SSO valve if as a result any SSOs no longer have active
+// * sessions. If the removed node is the one associated with this object,
+// * does nothing.
+// */
+// public void viewChange(View new_view)
+// {
+// if (currentView == null)
+// {
+// currentView = new_view;
+// return;
+// }
+//
+// Vector oldMembers = currentView.getMembers();
+//
+// currentView = new_view;
+//
+// // If we're not in the view, just exit
+// if (localAddress == null || !currentView.containsMember((Address) localAddress))
+// return;
+//
+// boolean hasDeadMembers = false;
+// for (Iterator iter = oldMembers.iterator(); iter.hasNext(); )
+// {
+// Address member = (Address) iter.next();
+// if (!new_view.containsMember(member))
+// {
+// hasDeadMembers = true;
+// break;
+// }
+// }
+//
+// if (hasDeadMembers)
+// {
+// DeadMemberCleaner cleaner = new DeadMemberCleaner();
+// if (threadPool != null)
+// {
+// threadPool.run(cleaner);
+// }
+// else
+// {
+// Thread t = new Thread(cleaner, "ClusteredSSOCleaner");
+// t.setDaemon(true);
+// t.start();
+// }
+// }
+//
+// }
+
+ // ------------------------------------------------------------- Lifecycle
+
+
+ /**
+ * Add a lifecycle event listener to this component.
+ *
+ * @param listener The listener to add
+ */
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.addLifecycleListener(listener);
+ }
+
+
+ /**
+ * Get the lifecycle listeners associated with this lifecycle. If this
+ * Lifecycle has no listeners registered, a zero-length array is returned.
+ */
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return lifecycle.findLifecycleListeners();
+ }
+
+
+ /**
+ * Remove a lifecycle event listener from this component.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.removeLifecycleListener(listener);
+ }
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called before any of the public
+ * methods of this component are utilized. It should also send a
+ * LifecycleEvent of type START_EVENT to any registered listeners.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ public void start() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (started)
+ {
+ throw new LifecycleException
+ ("TreeCacheSSOClusterManager already Started");
+ }
+
+ try
+ {
+ if (isTreeCacheAvailable(true))
+ {
+ integrateWithCache();
+ }
+ }
+ catch (Exception e)
+ {
+ throw new LifecycleException("Caught exception looking up " +
+ "TransactionManager from TreeCache", e);
+ }
+
+// DISABLED UNTIL 4.0.6.CR1
+// initThreadPool();
+
+ started = true;
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(START_EVENT, null);
+ }
+
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component. It should also send a LifecycleEvent
+ * of type STOP_EVENT to any registered listeners.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that needs to be reported
+ */
+ public void stop() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (!started)
+ {
+ throw new LifecycleException
+ ("TreeCacheSSOClusterManager not Started");
+ }
+
+ started = false;
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+ }
+
+
+ // ------------------------------------------------------- Private Methods
+
+ private Object getFromTreeCache(Fqn fqn, boolean localOnly) throws Exception
+ {
+ Object[] args = new Object[]{fqn, KEY, localOnly ? LOCAL_OPTION : GRAVITATE_OPTION};
+ return server.invoke(getCacheObjectName(), "get", args, GET_SIGNATURE);
+ }
+
+// DISABLED UNTIL 4.0.6.CR1
+// private Set getSSOIds() throws Exception
+// {
+// Fqn ssoRoot = new Fqn(new Object[] {SSO});
+// return (Set) server.invoke(getCacheObjectName(), "getChildrenNames",
+// new Object[]{ ssoRoot },
+// new String[]{ Fqn.class.getName() });
+// }
+
+ private Fqn getCredentialsFqn(String ssoid)
+ {
+ Object[] objs = new Object[]{SSO, ssoid, CREDENTIALS};
+ return new Fqn(objs);
+ }
+
+ private Fqn getSessionsFqn(String ssoid)
+ {
+ Object[] objs = new Object[]{SSO, ssoid, SESSIONS};
+ return new Fqn(objs);
+ }
+
+ private Fqn getSingleSignOnFqn(String ssoid)
+ {
+ Object[] objs = new Object[]{SSO, ssoid};
+ return new Fqn(objs);
+ }
+
+ /**
+ * Extracts an SSO session id from a fully qualified name object.
+ *
+ * @param fqn the Fully Qualified Name used by TreeCache
+ * @return the second element in the Fqn -- the SSO session id
+ */
+ private String getIdFromFqn(Fqn fqn)
+ {
+ String id = null;
+ if (fqn.size() > 1 && SSO.equals(fqn.get(0)))
+ {
+ id = (String) fqn.get(1);
+ }
+ return id;
+ }
+
+ private Set getSessionSet(Fqn fqn, boolean create, boolean localOnly)
+ throws Exception
+ {
+ Set sessions = (Set) getFromTreeCache(fqn, localOnly);
+ if (create && sessions == null)
+ {
+ sessions = new HashSet();
+ }
+ return sessions;
+ }
+
+ /**
+ * Extracts the SSO tree cache node type from a fully qualified name
+ * object.
+ *
+ * @param fqn the Fully Qualified Name used by TreeCache
+ * @return the 3rd in the Fqn -- either
+ * {@link #CREDENTIALS CREDENTIALS} or {@link #SESSIONS SESSIONS},
+ * or <code>null</code> if <code>fqn</code> is not for an SSO.
+ */
+ private String getTypeFromFqn(Fqn fqn)
+ {
+ String type = null;
+ if (fqn.size() > 2 && SSO.equals(fqn.get(0)))
+ type = (String) fqn.get(2);
+ return type;
+ }
+
+ /**
+ * Obtains needed configuration information from the tree cache.
+ * Invokes "getTransactionManager" on the tree cache, caching the
+ * result or throwing an IllegalStateException if one is not found.
+ * Also get our cluster-wide unique local address from the cache.
+ *
+ * @throws Exception
+ */
+ private void configureFromCache() throws Exception
+ {
+ tm = (TransactionManager) server.getAttribute(getCacheObjectName(),
+ "TransactionManager");
+
+ if (tm == null)
+ {
+ throw new IllegalStateException("TreeCache does not have a " +
+ "transaction manager; please " +
+ "configure a valid " +
+ "TransactionManagerLookupClass");
+ }
+
+ // Find out our address
+ Object address = server.getAttribute(cacheObjectName, "LocalAddress");
+ // In reality this is a JGroups IpAddress, but the API says
+ // "Object" so we have to be sure its Serializable
+ if (address instanceof Serializable)
+ localAddress = (Serializable) address;
+ else
+ localAddress = address.toString();
+ }
+
+
+ private void endTransaction()
+ {
+ try
+ {
+ if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
+ {
+ tm.commit();
+ }
+ else
+ {
+ tm.rollback();
+ }
+ }
+ catch (Exception e)
+ {
+ log.error(e);
+ throw new NestedRuntimeException("TreeCacheSSOClusterManager.endTransaction(): ", e);
+ }
+ }
+
+ /**
+ * Checks whether an MBean is registered under the value of property
+ * "cacheObjectName".
+ *
+ * @param forceCheck check for availability whether or not it has already
+ * been positively established
+ * @return <code>true</code> if property <code>cacheName</code> has been
+ * set and points to a registered MBean.
+ */
+ private synchronized boolean isTreeCacheAvailable(boolean forceCheck)
+ {
+ if (forceCheck || treeCacheAvailable == false)
+ {
+ boolean available = (cacheObjectName != null);
+ if (available)
+ {
+ Set s = server.queryMBeans(cacheObjectName, null);
+ available = s.size() > 0;
+ if (available)
+ {
+ try
+ {
+ // If Tomcat5 overrides the default cache name, it will do so
+ // after we are started. So we need to configure ourself here
+ // and throw an exception if there is a problem. Having this
+ // here also allows us to recover if our cache is started
+ // after we are
+ if (started)
+ integrateWithCache();
+ setMissingCacheErrorLogged(false);
+ }
+ catch (Exception e)
+ {
+ log.error("Caught exception configuring from cache " +
+ cacheObjectName, e);
+ available = false;
+ }
+ }
+ }
+ treeCacheAvailable = available;
+ }
+ return treeCacheAvailable;
+ }
+
+ private boolean checkTreeCacheAvailable()
+ {
+ boolean avail = isTreeCacheAvailable(false);
+ if (!avail)
+ logMissingCacheError();
+ return avail;
+ }
+
+ private void putInTreeCache(Fqn fqn, Object data, boolean localOnly) throws Exception
+ {
+ Object[] args = new Object[]{fqn, KEY, data, localOnly ? LOCAL_OPTION : GRAVITATE_OPTION};
+ server.invoke(getCacheObjectName(), "put", args, PUT_SIGNATURE);
+ }
+
+ private void integrateWithCache() throws Exception
+ {
+ // Ensure we have a transaction manager and a cluster-wide unique address
+ configureFromCache();
+
+ // If the SSO region is inactive, activate it
+ activateCacheRegion();
+
+ registerAsTreeCacheListener();
+
+ log.debug("Successfully integrated with cache service " + cacheObjectName);
+ }
+
+
+ /**
+ * If we are sharing a cache with HttpSession replication, the SSO
+ * region may not be active, so here we ensure it is.
+ *
+ * @throws Exception
+ */
+ private void activateCacheRegion() throws Exception
+ {
+ // NOTE: to be compatible with 1.2.3, no direct use of the 1.2.4
+ // region activation API can be used here even if the rest of this
+ // class is converted to using MBean proxies
+ try
+ {
+ Boolean inactive = (Boolean) server.getAttribute(cacheObjectName, "InactiveOnStartup");
+ if (inactive.booleanValue())
+ {
+ Boolean useMarshalling = (Boolean) server.getAttribute(cacheObjectName, "UseMarshalling");
+ if (useMarshalling.booleanValue())
+ {
+ // TODO replace this try/catch with a call to an isRegionActive API
+ try
+ {
+ server.invoke(cacheObjectName, "activateRegion",
+ new Object[]{ "/" + SSO },
+ new String[]{ "java.lang.String"});
+ }
+ catch (MBeanException e)
+ {
+ Exception cause = e.getTargetException();
+ if (cause != null
+ && "org.jboss.cache.RegionNotEmptyException".equals(cause.getClass().getName()))
+ {
+ log.debug(SSO + " region already active", cause);
+ }
+ else
+ throw e;
+ }
+ }
+ }
+ }
+ catch (AttributeNotFoundException ignore)
+ {
+ log.debug("Attribute InactiveOnStartup not available; " +
+ "must be using JBossCache 1.2.3 or earlier");
+ }
+ }
+
+ /**
+ * Invokes an operation on the JMX server to register ourself as a
+ * listener on the TreeCache service.
+ *
+ * @throws Exception
+ */
+ private void registerAsTreeCacheListener() throws Exception
+ {
+ server.invoke(cacheObjectName, "addTreeCacheListener",
+ new Object[]{this},
+ new String[]{TreeCacheListener.class.getName()});
+ registeredAsListener = true;
+ }
+
+
+ /**
+ * Invokes an operation on the JMX server to register ourself as a
+ * listener on the TreeCache service.
+ *
+ * @throws Exception
+ */
+ private void removeAsTreeCacheListener() throws Exception
+ {
+ if (registeredAsListener && cacheObjectName != null)
+ {
+ server.invoke(cacheObjectName, "removeTreeCacheListener",
+ new Object[]{this},
+ new String[]{TreeCacheListener.class.getName()});
+ }
+ }
+
+ private void removeFromTreeCache(Fqn fqn) throws Exception
+ {
+ server.invoke(getCacheObjectName(), "remove",
+ new Object[]{fqn, GRAVITATE_OPTION},
+ REMOVE_SIGNATURE);
+ }
+
+// DISABLED UNTIL 4.0.6.CR1
+// private void initThreadPool()
+// {
+// if (threadPoolName != null)
+// {
+// try
+// {
+// ObjectName on = new ObjectName(threadPoolName);
+// threadPool = (ThreadPool) MBeanProxyExt.create(ThreadPoolMBean.class, on);
+// }
+// catch (Exception e)
+// {
+// log.info("Unable to access ThreadPool at " + threadPoolName +
+// " -- will use individual threads for cleanup work");
+// }
+// }
+// }
+
+ /**
+ * Stores the given data to the clustered cache in a tree branch whose FQN
+ * is the given SSO id. Stores the given credential data in a child node
+ * named "credentials". If parameter <code>storeSessions</code> is
+ * <code>true</code>, also stores an empty HashSet in a sibling node
+ * named "sessions". This HashSet will later be used to hold session ids
+ * associated with the SSO.
+ * <p/>
+ * Any items stored are stored under the key "key".
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ private void storeSSOData(String ssoId, String authType, String username,
+ String password)
+ {
+ SSOCredentials data = new SSOCredentials(authType, username, password);
+
+ // Add this SSO to our list of in-process local adds so
+ // this.nodeModified() will ignore the addition
+ beingLocallyAdded.set(ssoId);
+
+ try
+ {
+ putInTreeCache(getCredentialsFqn(ssoId), data, false);
+ }
+ catch (Exception e)
+ {
+ log.error("Exception attempting to add TreeCache nodes for SSO " +
+ ssoId, e);
+ }
+ finally
+ {
+ beingLocallyAdded.set(null);
+ }
+ }
+
+ private boolean isMissingCacheErrorLogged()
+ {
+ return missingCacheErrorLogged;
+ }
+
+ private void setMissingCacheErrorLogged(boolean missingCacheErrorLogged)
+ {
+ this.missingCacheErrorLogged = missingCacheErrorLogged;
+ }
+
+ private void logMissingCacheError()
+ {
+ StringBuffer msg = new StringBuffer("Cannot find TreeCache using ");
+ msg.append(getCacheName());
+ msg.append(" -- TreeCache must be started before ClusteredSingleSignOn ");
+ msg.append("can handle requests");
+
+ if (isMissingCacheErrorLogged())
+ {
+ // Just log it as a warning
+ log.warn(msg);
+ }
+ else
+ {
+ log.error(msg);
+ // Set a flag so we don't relog this error over and over
+ setMissingCacheErrorLogged(true);
+ }
+ }
+
+ // --------------------------------------------------------- Outer Classes
+
+ /**
+ * Private class used to store authentication credentials in the TreeCache.
+ * <p/>
+ * For security, password accessor is private.
+ */
+ public static class SSOCredentials
+ implements Serializable
+ {
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 5704877226920571663L;
+
+ private String authType = null;
+ private String password = null;
+ private String username = null;
+
+ /**
+ * Creates a new SSOCredentials.
+ *
+ * @param authType The authorization method used to authorize the
+ * SSO (BASIC, CLIENT-CERT, DIGEST, FORM or NONE).
+ * @param username The username of the user associated with the SSO
+ * @param password The password of the user associated with the SSO
+ */
+ private SSOCredentials(String authType, String username, String password)
+ {
+ this.authType = authType;
+ this.username = username;
+ this.password = password;
+ }
+
+ /**
+ * Gets the username of the user associated with the SSO.
+ *
+ * @return the username
+ */
+ public String getUsername()
+ {
+ return username;
+ }
+
+ /**
+ * Gets the authorization method used to authorize the SSO.
+ *
+ * @return "BASIC", "CLIENT-CERT", "DIGEST" or "FORM"
+ */
+ public String getAuthType()
+ {
+ return authType;
+ }
+
+ /**
+ * Gets the password of the user associated with the SSO.
+ *
+ * @return the password, or <code>null</code> if the authorization
+ * type was DIGEST or CLIENT-CERT.
+ */
+ private String getPassword()
+ {
+ return password;
+ }
+
+ } // end SSOCredentials
+
+ static class SessionAddress implements Serializable
+ {
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -3702932999380140004L;
+
+ Serializable address;
+ String sessionId;
+
+ SessionAddress(String sessionId, Serializable address)
+ {
+ this.sessionId = sessionId;
+ this.address = address;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+
+ if (!(obj instanceof SessionAddress))
+ return false;
+
+ SessionAddress other = (SessionAddress) obj;
+
+ return (sessionId.equals(other.sessionId)
+ && address.equals(other.address));
+ }
+
+ public int hashCode()
+ {
+ int total = (19 * 43) + sessionId.hashCode();
+ return ((total * 43) + address.hashCode());
+ }
+
+
+ }
+
+// DISABLED UNTIL 4.0.6.CR1
+// /**
+// * Runnable that's run when the removal of a node from the cluster has been detected.
+// * Removes any SessionAddress objects associated with dead members from the
+// * session set of each SSO. Operates locally only so each node can independently clean
+// * its SSOs without concern about replication lock conflicts.
+// */
+// private class DeadMemberCleaner implements Runnable
+// {
+// public void run()
+// {
+// synchronized (cleanupMutex)
+// {
+// try
+// {
+// // Ensure we have a TransactionManager
+// if (tm == null)
+// configureFromCache();
+//
+// Set ids = getSSOIds();
+//
+// for (Iterator iter = ids.iterator(); iter.hasNext();)
+// {
+// cleanDeadMembersFromSSO((String) iter.next());
+//
+// }
+// }
+// catch (Exception e)
+// {
+// log.error("Caught exception cleaning sessions from dead cluster members from SSOs ", e);
+// }
+// }
+// }
+//
+// private void cleanDeadMembersFromSSO(String ssoId)
+// {
+// Fqn fqn = getSessionsFqn(ssoId);
+// boolean doTx = false;
+// try
+// {
+// // Don't start tx if there is already one associated with this thread.
+// if(tm.getTransaction() == null)
+// doTx = true;
+//
+// if(doTx)
+// tm.begin();
+//
+// Set sessions = getSessionSet(fqn, false, true);
+// if (sessions != null && sessions.size() > 0)
+// {
+// boolean changed = false;
+// for (Iterator iter = sessions.iterator(); iter.hasNext();)
+// {
+// SessionAddress session = (SessionAddress) iter.next();
+// if (!currentView.containsMember((Address) session.address))
+// {
+// iter.remove();
+// changed = true;
+// }
+// }
+//
+// if (changed)
+// {
+// if (sessions.size() == 0)
+// {
+// ssoValve.notifySSOEmpty(ssoId);
+// }
+//
+// putInTreeCache(fqn, sessions, true);
+// }
+// }
+// }
+// catch (Exception e)
+// {
+// try
+// {
+// if(doTx)
+// tm.setRollbackOnly();
+// }
+// catch (Exception ignored)
+// {
+// }
+// log.error("caught exception cleaning dead members from SSO " + ssoId, e);
+// }
+// finally
+// {
+// if (doTx)
+// endTransaction();
+// }
+// }
+// }
+
+} // end TreeCacheSSOClusterManager
+
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml (rev 0)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+ <mbean name="ClusteredSingleSignOn"
+ description="A Valve that supports a 'single signon' user experience"
+ domain="Catalina"
+ group="Valve"
+ type="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn">
+
+ <attribute name="className"
+ description="Fully qualified class name of the managed object"
+ type="java.lang.String"
+ writeable="false"/>
+
+ <attribute name="cookieDomain"
+ description="Domain to which sso cookies should be scoped"
+ type="java.lang.String"/>
+
+ <attribute name="requireReauthentication"
+ description="Should we attempt to reauthenticate each request against the security Realm?"
+ type="boolean"/>
+
+ <attribute name="maxEmptyLife"
+ description="The maximum number of seconds an SSO with no active sessions will be usable by a request"
+ type="int"/>
+
+ <attribute name="processExpiresInterval"
+ description="The maximum number of seconds an SSO with no active sessions will be usable by a request"
+ type="int"/>
+
+ <attribute name="lastProcessExpires"
+ writeable="false"
+ description="The timestamp of the start of the last check for overaged SSO's with no active sessions."
+ type="long"/>
+
+ <attribute name="clusterManager"
+ description="SSOClusterManager to use for cluster support"
+ type="org.jboss.web.tomcat.service.sso.SSOClusterManager"/>
+
+ <attribute name="clusterManagerClass"
+ description="Fully qualified class name of the SSOClusterManager to use for cluster support"
+ type="java.lang.String"/>
+
+ <attribute name="treeCacheName"
+ description="The ObjectName of the TreeCache that this Valve should use for cluster communications.
+ Only relevant if clusterManagerClass is org.jboss.web.tomcat.tc5.sso.TreeCacheSSOClusterManager"
+ type="java.lang.String"/>
+
+ </mbean>
+
+</mbeans-descriptors>
Property changes on: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: branches/Branch_4_2/tomcat/src/resources/META-INF/jboss-aop.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/META-INF/jboss-aop.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/META-INF/jboss-aop.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -3,19 +3,19 @@
<!-- With this declaration, FieldBasedClusteredSession will implement the
Observable interface to receive the event.
- Note that this file is bundled in tc5-cluster.aop. The aop package
+ Note that this file is bundled in jboss-web-cluster.aop. The aop package
is needed such that a user doesn't depend on JBossCache library.
-->
<!-- If a POJO has AopMarker annotation, it will be aspectized. -->
- <prepare expr="field(* @org.jboss.web.tomcat.tc5.session.AopMarker->*)" />
+ <prepare expr="field(* @org.jboss.web.tomcat.service.session.AopMarker->*)" />
<!-- Make any POJO with a AopMarker annotation as a Subject/Observerable -->
- <bind pointcut="set(* @org.jboss.web.tomcat.tc5.session.AopMarker->*)">
+ <bind pointcut="set(* @org.jboss.web.tomcat.service.session.AopMarker->*)">
<interceptor class="org.jboss.aspects.patterns.observable.SubjectInterceptor"/>
</bind>
- <introduction class="@org.jboss.web.tomcat.tc5.session.AopMarker">
+ <introduction class="@org.jboss.web.tomcat.service.session.AopMarker">
<mixin>
<interfaces>org.jboss.aspects.patterns.observable.Subject</interfaces>
<class>org.jboss.aspects.patterns.observable.SubjectImpl</class>
@@ -24,14 +24,14 @@
</introduction>
<!-- If a POJO has InstanceOfAopMarker annotation, it will be aspectized. -->
- <prepare expr="field(* $instanceof{@org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker}->*)" />
+ <prepare expr="field(* $instanceof{@org.jboss.web.tomcat.service.session.InstanceOfAopMarker}->*)" />
<!-- Make any POJO with a AopMarker annotation as a Subject/Observerable -->
- <bind pointcut="set(* @org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker->*)">
+ <bind pointcut="set(* @org.jboss.web.tomcat.service.session.InstanceOfAopMarker->*)">
<interceptor class="org.jboss.aspects.patterns.observable.SubjectInterceptor"/>
</bind>
- <introduction class="@org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker">
+ <introduction class="@org.jboss.web.tomcat.service.session.InstanceOfAopMarker">
<mixin>
<interfaces>org.jboss.aspects.patterns.observable.Subject</interfaces>
<class>org.jboss.aspects.patterns.observable.SubjectImpl</class>
Modified: branches/Branch_4_2/tomcat/src/resources/jboss-service-all.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/jboss-service-all.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/jboss-service-all.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -2,8 +2,8 @@
<!-- $Id$ -->
<server>
- <!-- Tomcat 5 Service-->
- <mbean code="org.jboss.web.tomcat.tc5.Tomcat5"
+ <!-- JBoss Web Service-->
+ <mbean code="org.jboss.web.tomcat.service.JBossWeb"
name="jboss.web:service=WebServer" xmbean-dd="META-INF/webserver-xmbean.xml">
<!-- You can configure a set of authenticators keyed by http-auth method used. This
@@ -71,9 +71,9 @@
<!--
Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
- - org.jboss.web.tomcat.tc5.session.JBossCacheManager
+ - org.jboss.web.tomcat.service.session.JBossCacheManager
-->
- <attribute name="ManagerClass">org.jboss.web.tomcat.tc5.session.JBossCacheManager</attribute>
+ <attribute name="ManagerClass">org.jboss.web.tomcat.service.session.JBossCacheManager</attribute>
<!-- The name of the request attribute under with the authenticated JAAS
Subject is stored on successful authentication. If null or empty then
@@ -161,7 +161,7 @@
<depends>jboss:service=TransactionManager</depends>
- <!-- Only needed if the org.jboss.web.tomcat.tc5.jca.CachedConnectionValve
+ <!-- Only needed if the org.jboss.web.tomcat.service.jca.CachedConnectionValve
is enabled in the tomcat server.xml file.
-->
<depends>jboss.jca:service=CachedConnectionManager</depends>
Modified: branches/Branch_4_2/tomcat/src/resources/jboss-service.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/jboss-service.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/jboss-service.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -3,7 +3,7 @@
<server>
<!-- Tomcat 5 Service-->
- <mbean code="org.jboss.web.tomcat.tc5.Tomcat5"
+ <mbean code="org.jboss.web.tomcat.service.JBossWeb"
name="jboss.web:service=WebServer" xmbean-dd="META-INF/webserver-xmbean.xml">
<!-- You can configure a set of authenticators keyed by http-auth method used. This
@@ -73,9 +73,9 @@
<!--
Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
- - org.jboss.web.tomcat.tc5.session.JBossCacheManager
+ - org.jboss.web.tomcat.service.session.JBossCacheManager
-->
- <attribute name="ManagerClass">org.jboss.web.tomcat.tc5.session.JBossCacheManager</attribute>
+ <attribute name="ManagerClass">org.jboss.web.tomcat.service.session.JBossCacheManager</attribute>
<!-- The name of the request attribute under with the authenticated JAAS
Subject is stored on successful authentication. If null or empty then
@@ -165,7 +165,7 @@
<depends>jboss:service=TransactionManager</depends>
- <!-- Only needed if the org.jboss.web.tomcat.tc5.jca.CachedConnectionValve
+ <!-- Only needed if the org.jboss.web.tomcat.service.jca.CachedConnectionValve
is enabled in the tomcat server.xml file.
-->
<depends>jboss.jca:service=CachedConnectionManager</depends>
Added: branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml (rev 0)
+++ branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ===================================================================== -->
+<!-- -->
+<!-- Customized TreeCache Service Configuration for Tomcat 5 Clustering -->
+<!-- -->
+<!-- ===================================================================== -->
+
+<server>
+
+ <!-- ==================================================================== -->
+ <!-- Defines TreeCache configuration -->
+ <!-- ==================================================================== -->
+
+ <!-- Note we are using TreeCacheAop -->
+ <mbean code="org.jboss.cache.aop.TreeCacheAop"
+ name="jboss.cache:service=TomcatClusteringCache">
+
+ <depends>jboss:service=Naming</depends>
+ <depends>jboss:service=TransactionManager</depends>
+ <!-- We need the AspectDeployer to deploy our FIELD granularity aspects -->
+ <depends>jboss.aop:service=AspectDeployer</depends>
+
+ <!-- Name of cluster. Needs to be the same for all nodes in the
+ cluster, in order to find each other
+ -->
+ <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
+
+ <!--
+ Isolation level : SERIALIZABLE
+ REPEATABLE_READ (default)
+ READ_COMMITTED
+ READ_UNCOMMITTED
+ NONE
+ -->
+ <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+ <!-- Valid modes are LOCAL, REPL_ASYNC and REPL_SYNC
+
+ If you use REPL_SYNC and a UDP-based ClusterConfig
+ we recommend you comment out the FC (flow control)
+ protocol in the ClusterConfig section below.
+ -->
+ <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+ <!--
+ Indicates whether to the cache should unmarshall objects replicated
+ from other cluster nodes, or store them internally as a byte[]
+ until a web app requests them. Must be "true" if session replication
+ granularity "FIELD" is used in any webapp, otherwise "false" is
+ recommended.
+ -->
+ <attribute name="UseRegionBasedMarshalling">false</attribute>
+
+ <!-- Whether or not the entire tree is inactive upon startup, only
+ responding to replication messages after activateRegion() is
+ called to activate one or more parts of the tree when a webapp is
+ deployed. Must have the same value as "UseRegionBasedMarshalling".
+ -->
+ <attribute name="InactiveOnStartup">false</attribute>
+
+ <!-- Make sure to specify BatchModeTransactionManager only! -->
+ <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</attribute>
+
+ <!-- Configures binary format of messages sent between cluster nodes.
+ Changing this allows a later version of JBoss Cache to interoperate
+ with an earlier version. You might, for example, change this
+ if you are integrating a 4.0.4 server into a cluster with
+ servers running an earlier AS version.
+
+ Possible values:
+
+ 1.2.3 JBC 1.2.3 or earlier; bundled with AS 4.0.3.SP1 and earlier
+ 1.2.4 JBC 1.2.4
+ 1.2.4.SP1 JBC 1.2.4.SP1
+ 1.2.4.SP2 JBC 1.2.4.SP2; bundled with AS 4.0.4
+
+ For version 1.3.0.GA and later, use the version name.
+
+ If left blank or commented out, JBoss Cache will use the default
+ for that release (e.g. 1.4.0 for releases in the 1.4.0 series.
+
+ The binary format of replication version 1.4.0 is much more efficient
+ than earlier releases, so there is a significant performance penalty
+ to trying to interoperate 1.4.0 with earlier releases vs. a pure
+ 1.4.0 cluster.
+
+ <attribute name="ReplicationVersion">1.4.0.GA</attribute>
+ -->
+
+ <!-- JGroups protocol stack properties. Can also be a URL,
+ e.g. file:/home/bela/default.xml
+ <attribute name="ClusterProperties"></attribute>
+ -->
+
+ <attribute name="ClusterConfig">
+ <!--
+ The default UDP stack:
+ - If you have a multihomed machine, set the UDP protocol's bind_addr attribute to the
+ appropriate NIC IP address, e.g bind_addr="192.168.0.2".
+ - On Windows machines, because of the media sense feature being broken with multicast
+ (even after disabling media sense) set the UDP protocol's loopback attribute to true
+
+ - If your CacheMode is set to REPL_SYNC we recommend you comment
+ out the FC (flow control) protocol
+ -->
+ <config>
+ <UDP mcast_addr="${jboss.partition.udpGroup:230.1.2.7}"
+ mcast_port="45577"
+ ucast_recv_buf_size="20000000"
+ ucast_send_buf_size="640000"
+ mcast_recv_buf_size="25000000"
+ mcast_send_buf_size="640000"
+ loopback="false"
+ max_bundle_size="64000"
+ max_bundle_timeout="30"
+ use_incoming_packet_handler="true"
+ use_outgoing_packet_handler="true"
+ ip_ttl="${jgroups.mcast.ip_ttl:2}"
+ down_thread="false" up_thread="false"
+ enable_bundling="true"/>
+ <PING timeout="2000"
+ down_thread="false" up_thread="false" num_initial_members="3"/>
+ <MERGE2 max_interval="100000"
+ down_thread="false" up_thread="false" min_interval="20000"/>
+ <FD_SOCK down_thread="false" up_thread="false"/>
+ <FD shun="true" up_thread="false" down_thread="false"
+ timeout="20000" max_tries="5"/>
+ <VERIFY_SUSPECT timeout="1500"
+ up_thread="false" down_thread="false"/>
+ <pbcast.NAKACK max_xmit_size="60000"
+ use_mcast_xmit="false" gc_lag="50"
+ retransmit_timeout="300,600,1200,2400,4800"
+ down_thread="false" up_thread="false"
+ discard_delivered_msgs="true"/>
+ <UNICAST timeout="300,600,1200,2400,3600"
+ down_thread="false" up_thread="false"/>
+ <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
+ down_thread="false" up_thread="false"
+ max_bytes="400000"/>
+ <pbcast.GMS print_local_addr="true" join_timeout="3000"
+ down_thread="false" up_thread="false"
+ join_retry_timeout="2000" shun="true"/>
+ <FC max_credits="2000000" down_thread="false" up_thread="false"
+ min_threshold="0.10"/>
+ <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
+ <pbcast.STATE_TRANSFER down_thread="false" up_thread="false"/>
+ </config>
+
+ <!-- Alternate TCP stack: customize it for your environment, change bind_addr and initial_hosts -->
+ <!--
+ <config>
+ <TCP bind_addr="thishost" start_port="7810" loopback="true"
+ tcp_nodelay="false" down_thread="false" up_thread="false"/>
+ <TCPPING initial_hosts="thishost[7810],otherhost[7810]" port_range="3" timeout="3500"
+ num_initial_members="3" up_thread="false" down_thread="false"/>
+ <MERGE2 min_interval="5000" max_interval="10000"
+ up_thread="false" down_thread="false"/>
+ <FD_SOCK down_thread="false" up_thread="false"/>
+ <FD shun="true" up_thread="false" down_thread="false"
+ timeout="10000" max_tries="5"/>
+ <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />
+ <pbcast.NAKACK down_thread="false" up_thread="false" gc_lag="100"
+ retransmit_timeout="3000"/>
+ <pbcast.STABLE desired_avg_gossip="20000" down_thread="false" up_thread="false" />
+ <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true"
+ print_local_addr="true" down_thread="false" up_thread="false"/>
+ <FC max_credits="2000000" down_thread="false" up_thread="false"
+ min_threshold="0.10"/>
+ <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
+ <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
+ </config>
+ -->
+
+ </attribute>
+
+ <!--
+ Number of milliseconds to wait until all responses for a
+ synchronous call have been received.
+ -->
+ <attribute name="SyncReplTimeout">20000</attribute>
+
+ <!-- Max number of milliseconds to wait for a lock acquisition -->
+ <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+ <!-- Buddy Replication config.
+
+ See http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossCacheBuddyReplicationDesign
+ and the JBoss Cache docs for more on buddy replication.
+
+ By default, buddy replication is disabled.
+
+ Following are the configuration elements likely to be changed:
+
+ buddyReplicationEnabled true if you want buddy replication; false if data
+ should be replicated to all nodes in the cluster
+
+ numBuddies to how many backup nodes should each node replicate
+ its state
+
+ buddyPoolName allows logical subgrouping of nodes within the cluster;
+ if possible, buddies will be chosen from nodes in the
+ same buddy pool
+
+ Do not change the data gravitation related options.
+ -->
+ <attribute name="BuddyReplicationConfig">
+ <config>
+ <buddyReplicationEnabled>false</buddyReplicationEnabled>
+ <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
+ <buddyLocatorProperties>
+ numBuddies = 1
+ ignoreColocatedBuddies = true
+ </buddyLocatorProperties>
+
+ <buddyPoolName>default</buddyPoolName>
+ <buddyCommunicationTimeout>2000</buddyCommunicationTimeout>
+
+ <autoDataGravitation>false</autoDataGravitation>
+ <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
+ <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
+
+ </config>
+ </attribute>
+
+ </mbean>
+
+</server>
Property changes on: branches/Branch_4_2/tomcat/src/resources/jboss-web-cluster-service.xml
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: branches/Branch_4_2/tomcat/src/resources/server.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/server.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/server.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -1,38 +1,41 @@
<Server>
+ <!--APR library loader. Documentation at /docs/apr.html -->
+ <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
+ <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
+ <Listener className="org.apache.catalina.core.JasperListener" />
+
<!-- Use a custom version of StandardService that allows the
connectors to be started independent of the normal lifecycle
start to allow web apps to be deployed before starting the
connectors.
-->
<Service name="jboss.web"
- className="org.jboss.web.tomcat.tc5.StandardService">
+ className="org.jboss.web.tomcat.service.StandardService">
- <!-- A HTTP/1.1 Connector on port 8080 -->
- <Connector port="8080" address="${jboss.bind.address}"
- maxThreads="250" strategy="ms" maxHttpHeaderSize="8192"
- emptySessionPath="true"
- enableLookups="false" redirectPort="8443" acceptCount="100"
- connectionTimeout="20000" disableUploadTimeout="true"/>
+ <!-- A "Connector" represents an endpoint by which requests are received
+ and responses are returned. Documentation at :
+ Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
+ Java AJP Connector: /docs/config/ajp.html
+ APR (HTTP/AJP) Connector: /docs/apr.html
+ Define a non-SSL HTTP/1.1 Connector on port 8080
+ -->
+ <Connector port="8080" protocol="HTTP/1.1"
+ maxThreads="150" connectionTimeout="20000"
+ redirectPort="8443" />
- <!-- Add this option to the connector to avoid problems with
- .NET clients that don't implement HTTP/1.1 correctly
- restrictedUserAgents="^.*MS Web Services Client Protocol 1.1.4322.*$"
- -->
-
- <!-- A AJP 1.3 Connector on port 8009 -->
- <Connector port="8009" address="${jboss.bind.address}"
- emptySessionPath="true" enableLookups="false" redirectPort="8443"
- protocol="AJP/1.3"/>
+ <!-- Define a SSL HTTP/1.1 Connector on port 8443
+ This connector uses the JSSE configuration, when using APR, the
+ connector should be using the OpenSSL style configuration
+ described in the APR documentation -->
+ <!--
+ <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
+ maxThreads="150" scheme="https" secure="true"
+ clientAuth="false" sslProtocol="TLS" />
+ -->
- <!-- SSL/TLS Connector configuration using the admin devl guide keystore
- <Connector port="8443" address="${jboss.bind.address}"
- maxThreads="100" strategy="ms" maxHttpHeaderSize="8192"
- emptySessionPath="true"
- scheme="https" secure="true" clientAuth="false"
- keystoreFile="${jboss.server.home.dir}/conf/chap8.keystore"
- keystorePass="rmi+ssl" sslProtocol = "TLS" />
- -->
+ <!-- Define an AJP 1.3 Connector on port 8009 -->
+ <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="jboss.web" defaultHost="localhost">
@@ -86,7 +89,7 @@
<!-- Access logger -->
<!--
- <Valve className="org.apache.catalina.valves.FastCommonAccessLogValve"
+ <Valve className="org.apache.catalina.valves.AccessLogValve"
prefix="localhost_access_log." suffix=".log"
pattern="common" directory="${jboss.server.home.dir}/log"
resolveHosts="false" />
@@ -123,7 +126,7 @@
caching and replication across the cluster. The JBossCache
instance must be configured separately. By default, the valve
shares a JBossCache with the service that supports HttpSession
- replication. See the "tc5-cluster-service.xml" file in the
+ replication. See the "jboss-web-cluster-service.xml" file in the
server/all/deploy directory for cache configuration details.
Besides the attributes supported by the standard Tomcat
@@ -140,7 +143,7 @@
to support session replication.
-->
<!--
- <Valve className="org.jboss.web.tomcat.tc5.sso.ClusteredSingleSignOn" />
+ <Valve className="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn" />
-->
<!-- Check for unclosed connections and transaction terminated checks
@@ -149,7 +152,7 @@
Important: The dependency on the CachedConnectionManager
in META-INF/jboss-service.xml must be uncommented, too
-->
- <Valve className="org.jboss.web.tomcat.tc5.jca.CachedConnectionValve"
+ <Valve className="org.jboss.web.tomcat.service.jca.CachedConnectionValve"
cachedConnectionManagerObjectName="jboss.jca:service=CachedConnectionManager"
transactionManagerObjectName="jboss:service=TransactionManager" />
Modified: branches/Branch_4_2/tomcat/src/resources/standalone-server.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/standalone-server.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/standalone-server.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -1,147 +1,88 @@
-<!-- Example Server Configuration File -->
-<!-- Note that component elements are nested corresponding to their
- parent-child relationships with each other -->
-
-<!-- A "Server" is a singleton element that represents the entire JVM,
- which may contain one or more "Service" instances. The Server
- listens for a shutdown command on the indicated port.
-
- Note: A "Server" is not itself a "Container", so you may not
- define subcomponents such as "Valves" or "Loggers" at this level.
+<!-- Note: A "Server" is not itself a "Container", so you may not
+ define subcomponents such as "Valves" at this level.
+ Documentation at /docs/config/server.html
-->
-
<Server port="8005" shutdown="SHUTDOWN">
- <!-- Comment these entries out to disable JMX MBeans support used for the
- administration web application -->
+ <!--APR library loader. Documentation at /docs/apr.html -->
+ <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
+ <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
+ <Listener className="org.apache.catalina.core.JasperListener" />
+ <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html -->
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
- <Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>
- <!-- Global JNDI resources -->
+ <!-- Global JNDI resources
+ Documentation at /docs/jndi-resources-howto.html
+ -->
<GlobalNamingResources>
-
- <!-- Test entry for demonstration purposes -->
- <Environment name="simpleValue" type="java.lang.Integer" value="30"/>
-
<!-- Editable user database that can also be used by
- UserDatabaseRealm to authenticate users -->
+ UserDatabaseRealm to authenticate users
+ -->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
- description="User database that can be updated and saved"
- factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
- pathname="conf/tomcat-users.xml" />
-
+ description="User database that can be updated and saved"
+ factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
+ pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share
- a single "Container" (and therefore the web applications visible
- within that Container). Normally, that Container is an "Engine",
- but this is not required.
-
- Note: A "Service" is not itself a "Container", so you may not
- define subcomponents such as "Valves" or "Loggers" at this level.
+ a single "Container" Note: A "Service" is not itself a "Container",
+ so you may not define subcomponents such as "Valves" at this level.
+ Documentation at /docs/config/service.html
-->
-
- <!-- Define the Tomcat Stand-Alone Service -->
<Service name="Catalina">
<!-- A "Connector" represents an endpoint by which requests are received
- and responses are returned. Each Connector passes requests on to the
- associated "Container" (normally an Engine) for processing.
-
- By default, a non-SSL HTTP/1.1 Connector is established on port 8080.
- You can also enable an SSL HTTP/1.1 Connector on port 8443 by
- following the instructions below and uncommenting the second Connector
- entry. SSL support requires the following steps (see the SSL Config
- HOWTO in the Tomcat 5 documentation bundle for more detailed
- instructions):
- * If your JDK version 1.3 or prior, download and install JSSE 1.0.2 or
- later, and put the JAR files into "$JAVA_HOME/jre/lib/ext".
- * Execute:
- %JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA (Windows)
- $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA (Unix)
- with a password value of "changeit" for both the certificate and
- the keystore itself.
-
- By default, DNS lookups are enabled when a web application calls
- request.getRemoteHost(). This can have an adverse impact on
- performance, so you can disable it by setting the
- "enableLookups" attribute to "false". When DNS lookups are disabled,
- request.getRemoteHost() will return the String version of the
- IP address of the remote client.
+ and responses are returned. Documentation at :
+ Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
+ Java AJP Connector: /docs/config/ajp.html
+ APR (HTTP/AJP) Connector: /docs/apr.html
+ Define a non-SSL HTTP/1.1 Connector on port 8080
-->
+ <Connector port="8080" protocol="HTTP/1.1"
+ maxThreads="150" connectionTimeout="20000"
+ redirectPort="8443" />
- <!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
- <Connector port="8080" maxHttpHeaderSize="8192"
- maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
- enableLookups="false" redirectPort="8443" acceptCount="100"
- connectionTimeout="20000" disableUploadTimeout="true" />
- <!-- Note : To disable connection timeouts, set connectionTimeout value
- to 0 -->
-
- <!-- Note : To use gzip compression you could set the following properties :
-
- compression="on"
- compressionMinSize="2048"
- noCompressionUserAgents="gozilla, traviata"
- compressableMimeType="text/html,text/xml"
- -->
-
- <!-- Define a SSL HTTP/1.1 Connector on port 8443 -->
+ <!-- Define a SSL HTTP/1.1 Connector on port 8443
+ This connector uses the JSSE configuration, when using APR, the
+ connector should be using the OpenSSL style configuration
+ described in the APR documentation -->
<!--
- <Connector port="8443" maxHttpHeaderSize="8192"
- maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
- enableLookups="false" disableUploadTimeout="true"
- acceptCount="100" scheme="https" secure="true"
+ <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
+ maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
- <Connector port="8009"
- enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />
+ <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
- <!-- Define a Proxied HTTP/1.1 Connector on port 8082 -->
- <!-- See proxy documentation for more information about using this. -->
- <!--
- <Connector port="8082"
- maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
- enableLookups="false" acceptCount="100" connectionTimeout="20000"
- proxyPort="80" disableUploadTimeout="true" />
- -->
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
- on to the appropriate Host (virtual host). -->
+ on to the appropriate Host (virtual host).
+ Documentation at /docs/config/engine.html -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Standalone" defaultHost="localhost" jvmRoute="jvm1">
-->
-
- <!-- Define the top level container in our container hierarchy -->
<Engine name="Catalina" defaultHost="localhost">
- <!-- The request dumper valve dumps useful debugging information about
- the request headers and cookies that were received, and the response
- headers and cookies that were sent, for all requests received by
- this instance of Tomcat. If you care only about requests to a
- particular virtual host, or a particular application, nest this
- element inside the corresponding <Host> or <Context> entry instead.
+ <!--For clustering, please take a look at documentation at:
+ /docs/cluster-howto.html (simple how to)
+ /docs/config/cluster.html (reference documentation) -->
+ <!--
+ <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
+ -->
- For a similar mechanism that is portable to all Servlet 2.4
- containers, check out the "RequestDumperFilter" Filter in the
- example application (the source for this filter may be found in
- "$CATALINA_HOME/webapps/examples/WEB-INF/classes/filters").
-
- Request dumping is disabled by default. Uncomment the following
- element to enable it. -->
+ <!-- The request dumper valve dumps useful debugging information about
+ the request and response data received and sent by Tomcat.
+ Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.valves.RequestDumperValve"/>
-->
- <!-- Because this Realm is here, an instance will be shared globally -->
-
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
@@ -149,163 +90,27 @@
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
- <!-- Comment out the old realm but leave here for now in case we
- need to go back quickly -->
- <!--
- <Realm className="org.apache.catalina.realm.MemoryRealm" />
- -->
-
- <!-- Replace the above Realm with one of the following to get a Realm
- stored in a database and accessed via JDBC -->
-
- <!--
- <Realm className="org.apache.catalina.realm.JDBCRealm"
- driverName="org.gjt.mm.mysql.Driver"
- connectionURL="jdbc:mysql://localhost/authority"
- connectionName="test" connectionPassword="test"
- userTable="users" userNameCol="user_name" userCredCol="user_pass"
- userRoleTable="user_roles" roleNameCol="role_name" />
- -->
-
- <!--
- <Realm className="org.apache.catalina.realm.JDBCRealm"
- driverName="oracle.jdbc.driver.OracleDriver"
- connectionURL="jdbc:oracle:thin:@ntserver:1521:ORCL"
- connectionName="scott" connectionPassword="tiger"
- userTable="users" userNameCol="user_name" userCredCol="user_pass"
- userRoleTable="user_roles" roleNameCol="role_name" />
- -->
-
- <!--
- <Realm className="org.apache.catalina.realm.JDBCRealm"
- driverName="sun.jdbc.odbc.JdbcOdbcDriver"
- connectionURL="jdbc:odbc:CATALINA"
- userTable="users" userNameCol="user_name" userCredCol="user_pass"
- userRoleTable="user_roles" roleNameCol="role_name" />
- -->
- <!--
- <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
- -->
<!-- Define the default virtual host
Note: XML Schema validation will not work with Xerces 2.2.
-->
-
- <Host name="localhost" appBase="webapps"
- unpackWARs="true" autoDeploy="true"
- xmlValidation="false" xmlNamespaceAware="false">
-
- <!-- Use JBossCache for HttpSession replication across a cluster -->
- <Cluster className="org.jboss.web.tomcat.tc5.session.JBossCacheCluster"
- cacheObjectName="Catalina:service=TomcatClusteringCache"
- cacheConfigPath="conf/cluster-cache.xml"
- snapshotMode="instant"
- snapshotInterval="2000"
- useJK="false"
- defaultReplicationTrigger="SET_AND_NON_PRIMITIVE_GET"
- defaultReplicationGranularity="session"/>
+ <Host name="localhost" appBase="webapps"
+ unpackWARs="true" autoDeploy="true"
+ xmlValidation="false" xmlNamespaceAware="false">
- <!-- Normally, users must authenticate themselves to each web app
- individually. Uncomment the following entry if you would like
- a user to be authenticated the first time they encounter a
- resource protected by a security constraint, and then have that
- user identity maintained across *all* web applications contained
- in this virtual host.
-
- Does not provide SSO across a cluster.
-
- If this valve is used, do not use the ClusteredSingleSignOn
- valve shown below.
-
- A new configuration attribute is available beginning with
- release 5.5.13:
-
- cookieDomain configures the domain to which the SSO cookie
- will be scoped (i.e. the set of hosts to
- which the cookie will be presented). By default
- the cookie is scoped to "/", meaning the host
- that presented it. Set cookieDomain to a
- wider domain (e.g. "xyz.com") to allow an SSO
- to span more than one hostname.
- -->
+ <!-- SingleSignOn valve, share authentication between web applications
+ Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
-
- <!-- Uncomment to enable single sign-on across web apps
- deployed to this host AND to all other hosts in the cluster
- with the same virtual hostname.
-
- If this valve is used, do not use the standard Tomcat SingleSignOn
- valve shown above.
-
- This valve uses uses JBossCache to replicate SSO credentials across
- the cluster. To use it, be sure the JBossCache-based Cluster
- element above is uncommented.
-
- Besides the attributes supported by the standard Tomcat
- SingleSignOn valve (see the Tomcat docs), this version also supports
- the following attributes:
-
- cookieDomain see above
-
- treeCacheName the JMX ObjectName of the JBossCache MBean created by
- the Cluster element above. This should be set to
- match the "cacheObjectName" attribute of the Cluster
- element. If this attribute is not configured, a
- default of "jboss.web:service=TomcatClusteringCache"
- will be used, which matches the default value of the
- Cluster element's "cacheObjectName".
- -->
- <!--
- <Valve className="org.jboss.web.tomcat.tc5.sso.ClusteredSingleSignOn" />
- -->
- <!-- Access log processes all requests for this virtual host. By
- default, log files are created in the "logs" directory relative to
- $CATALINA_HOME. If you wish, you can specify a different
- directory with the "directory" attribute. Specify either a relative
- (to $CATALINA_HOME) or absolute path to the desired directory.
- -->
+ <!-- Access log processes all example.
+ Documentation at: /docs/config/valve.html -->
<!--
- <Valve className="org.apache.catalina.valves.AccessLogValve"
- directory="logs" prefix="localhost_access_log." suffix=".txt"
- pattern="common" resolveHosts="false"/>
+ <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
+ prefix="localhost_access_log." suffix=".txt" pattern="common" resolveHosts="false"/>
-->
- <!-- Access log processes all requests for this virtual host. By
- default, log files are created in the "logs" directory relative to
- $CATALINA_HOME. If you wish, you can specify a different
- directory with the "directory" attribute. Specify either a relative
- (to $CATALINA_HOME) or absolute path to the desired directory.
- This access log implementation is optimized for maximum performance,
- but is hardcoded to support only the "common" and "combined" patterns.
- -->
- <!--
- <Valve className="org.apache.catalina.valves.FastCommonAccessLogValve"
- directory="logs" prefix="localhost_access_log." suffix=".txt"
- pattern="common" resolveHosts="false"/>
- -->
- <!-- Access log processes all requests for this virtual host. By
- default, log files are created in the "logs" directory relative to
- $CATALINA_HOME. If you wish, you can specify a different
- directory with the "directory" attribute. Specify either a relative
- (to $CATALINA_HOME) or absolute path to the desired directory.
- This access log implementation is optimized for maximum performance,
- but is hardcoded to support only the "common" and "combined" patterns.
-
- This valve use NIO direct Byte Buffer to asynchornously store the
- log.
- -->
- <!--
- <Valve className="org.apache.catalina.valves.ByteBufferAccessLogValve"
- directory="logs" prefix="localhost_access_log." suffix=".txt"
- pattern="common" resolveHosts="false"/>
- -->
-
</Host>
-
</Engine>
-
</Service>
-
</Server>
Deleted: branches/Branch_4_2/tomcat/src/resources/tc5-cluster-service.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/tc5-cluster-service.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/tc5-cluster-service.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -1,228 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ===================================================================== -->
-<!-- -->
-<!-- Customized TreeCache Service Configuration for Tomcat 5 Clustering -->
-<!-- -->
-<!-- ===================================================================== -->
-
-<server>
-
- <!-- ==================================================================== -->
- <!-- Defines TreeCache configuration -->
- <!-- ==================================================================== -->
-
- <!-- Note we are using TreeCacheAop -->
- <mbean code="org.jboss.cache.aop.TreeCacheAop"
- name="jboss.cache:service=TomcatClusteringCache">
-
- <depends>jboss:service=Naming</depends>
- <depends>jboss:service=TransactionManager</depends>
- <!-- We need the AspectDeployer to deploy our FIELD granularity aspects -->
- <depends>jboss.aop:service=AspectDeployer</depends>
-
- <!-- Name of cluster. Needs to be the same for all nodes in the
- cluster, in order to find each other
- -->
- <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
-
- <!--
- Isolation level : SERIALIZABLE
- REPEATABLE_READ (default)
- READ_COMMITTED
- READ_UNCOMMITTED
- NONE
- -->
- <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
-
- <!-- Valid modes are LOCAL, REPL_ASYNC and REPL_SYNC
-
- If you use REPL_SYNC and a UDP-based ClusterConfig
- we recommend you comment out the FC (flow control)
- protocol in the ClusterConfig section below.
- -->
- <attribute name="CacheMode">REPL_ASYNC</attribute>
-
- <!--
- Indicates whether to the cache should unmarshall objects replicated
- from other cluster nodes, or store them internally as a byte[]
- until a web app requests them. Must be "true" if session replication
- granularity "FIELD" is used in any webapp, otherwise "false" is
- recommended.
- -->
- <attribute name="UseRegionBasedMarshalling">false</attribute>
-
- <!-- Whether or not the entire tree is inactive upon startup, only
- responding to replication messages after activateRegion() is
- called to activate one or more parts of the tree when a webapp is
- deployed. Must have the same value as "UseRegionBasedMarshalling".
- -->
- <attribute name="InactiveOnStartup">false</attribute>
-
- <!-- Make sure to specify BatchModeTransactionManager only! -->
- <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</attribute>
-
- <!-- Configures binary format of messages sent between cluster nodes.
- Changing this allows a later version of JBoss Cache to interoperate
- with an earlier version. You might, for example, change this
- if you are integrating a 4.0.4 server into a cluster with
- servers running an earlier AS version.
-
- Possible values:
-
- 1.2.3 JBC 1.2.3 or earlier; bundled with AS 4.0.3.SP1 and earlier
- 1.2.4 JBC 1.2.4
- 1.2.4.SP1 JBC 1.2.4.SP1
- 1.2.4.SP2 JBC 1.2.4.SP2; bundled with AS 4.0.4
-
- For version 1.3.0.GA and later, use the version name.
-
- If left blank or commented out, JBoss Cache will use the default
- for that release (e.g. 1.4.0 for releases in the 1.4.0 series.
-
- The binary format of replication version 1.4.0 is much more efficient
- than earlier releases, so there is a significant performance penalty
- to trying to interoperate 1.4.0 with earlier releases vs. a pure
- 1.4.0 cluster.
-
- <attribute name="ReplicationVersion">1.4.0.GA</attribute>
- -->
-
- <!-- JGroups protocol stack properties. Can also be a URL,
- e.g. file:/home/bela/default.xml
- <attribute name="ClusterProperties"></attribute>
- -->
-
- <attribute name="ClusterConfig">
- <!--
- The default UDP stack:
- - If you have a multihomed machine, set the UDP protocol's bind_addr attribute to the
- appropriate NIC IP address, e.g bind_addr="192.168.0.2".
- - On Windows machines, because of the media sense feature being broken with multicast
- (even after disabling media sense) set the UDP protocol's loopback attribute to true
-
- - If your CacheMode is set to REPL_SYNC we recommend you comment
- out the FC (flow control) protocol
- -->
- <config>
- <UDP mcast_addr="${jboss.partition.udpGroup:230.1.2.7}"
- mcast_port="45577"
- ucast_recv_buf_size="20000000"
- ucast_send_buf_size="640000"
- mcast_recv_buf_size="25000000"
- mcast_send_buf_size="640000"
- loopback="false"
- max_bundle_size="64000"
- max_bundle_timeout="30"
- use_incoming_packet_handler="true"
- use_outgoing_packet_handler="true"
- ip_ttl="${jgroups.mcast.ip_ttl:2}"
- down_thread="false" up_thread="false"
- enable_bundling="true"/>
- <PING timeout="2000"
- down_thread="false" up_thread="false" num_initial_members="3"/>
- <MERGE2 max_interval="100000"
- down_thread="false" up_thread="false" min_interval="20000"/>
- <FD_SOCK down_thread="false" up_thread="false"/>
- <FD shun="true" up_thread="false" down_thread="false"
- timeout="20000" max_tries="5"/>
- <VERIFY_SUSPECT timeout="1500"
- up_thread="false" down_thread="false"/>
- <pbcast.NAKACK max_xmit_size="60000"
- use_mcast_xmit="false" gc_lag="50"
- retransmit_timeout="300,600,1200,2400,4800"
- down_thread="false" up_thread="false"
- discard_delivered_msgs="true"/>
- <UNICAST timeout="300,600,1200,2400,3600"
- down_thread="false" up_thread="false"/>
- <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
- down_thread="false" up_thread="false"
- max_bytes="400000"/>
- <pbcast.GMS print_local_addr="true" join_timeout="3000"
- down_thread="false" up_thread="false"
- join_retry_timeout="2000" shun="true"/>
- <FC max_credits="2000000" down_thread="false" up_thread="false"
- min_threshold="0.10"/>
- <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
- <pbcast.STATE_TRANSFER down_thread="false" up_thread="false"/>
- </config>
-
- <!-- Alternate TCP stack: customize it for your environment, change bind_addr and initial_hosts -->
- <!--
- <config>
- <TCP bind_addr="thishost" start_port="7810" loopback="true"
- tcp_nodelay="false" down_thread="false" up_thread="false"/>
- <TCPPING initial_hosts="thishost[7810],otherhost[7810]" port_range="3" timeout="3500"
- num_initial_members="3" up_thread="false" down_thread="false"/>
- <MERGE2 min_interval="5000" max_interval="10000"
- up_thread="false" down_thread="false"/>
- <FD_SOCK down_thread="false" up_thread="false"/>
- <FD shun="true" up_thread="false" down_thread="false"
- timeout="10000" max_tries="5"/>
- <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />
- <pbcast.NAKACK down_thread="false" up_thread="false" gc_lag="100"
- retransmit_timeout="3000"/>
- <pbcast.STABLE desired_avg_gossip="20000" down_thread="false" up_thread="false" />
- <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true"
- print_local_addr="true" down_thread="false" up_thread="false"/>
- <FC max_credits="2000000" down_thread="false" up_thread="false"
- min_threshold="0.10"/>
- <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
- <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
- </config>
- -->
-
- </attribute>
-
- <!--
- Number of milliseconds to wait until all responses for a
- synchronous call have been received.
- -->
- <attribute name="SyncReplTimeout">20000</attribute>
-
- <!-- Max number of milliseconds to wait for a lock acquisition -->
- <attribute name="LockAcquisitionTimeout">15000</attribute>
-
- <!-- Buddy Replication config.
-
- See http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossCacheBuddyReplicationDesign
- and the JBoss Cache docs for more on buddy replication.
-
- By default, buddy replication is disabled.
-
- Following are the configuration elements likely to be changed:
-
- buddyReplicationEnabled true if you want buddy replication; false if data
- should be replicated to all nodes in the cluster
-
- numBuddies to how many backup nodes should each node replicate
- its state
-
- buddyPoolName allows logical subgrouping of nodes within the cluster;
- if possible, buddies will be chosen from nodes in the
- same buddy pool
-
- Do not change the data gravitation related options.
- -->
- <attribute name="BuddyReplicationConfig">
- <config>
- <buddyReplicationEnabled>false</buddyReplicationEnabled>
- <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
- <buddyLocatorProperties>
- numBuddies = 1
- ignoreColocatedBuddies = true
- </buddyLocatorProperties>
-
- <buddyPoolName>default</buddyPoolName>
- <buddyCommunicationTimeout>2000</buddyCommunicationTimeout>
-
- <autoDataGravitation>false</autoDataGravitation>
- <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
- <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
-
- </config>
- </attribute>
-
- </mbean>
-
-</server>
Modified: branches/Branch_4_2/tomcat/src/resources/web.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/web.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/web.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
- version="2.4">
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+ version="2.4">
- <!-- ======================== Introduction ============================== -->
- <!-- This document defines default values for *all* web applications -->
- <!-- loaded into this instance of Tomcat. As each application is -->
- <!-- deployed, this file is processed, followed by the -->
- <!-- "/WEB-INF/web.xml" deployment descriptor from your own -->
- <!-- applications. -->
- <!-- -->
- <!-- WARNING: Do not configure application-specific resources here! -->
- <!-- They should go in the "/WEB-INF/web.xml" file in your application. -->
+ <!-- ======================== Introduction ============================== -->
+ <!-- This document defines default values for *all* web applications -->
+ <!-- loaded into this instance of Tomcat. As each application is -->
+ <!-- deployed, this file is processed, followed by the -->
+ <!-- "/WEB-INF/web.xml" deployment descriptor from your own -->
+ <!-- applications. -->
+ <!-- -->
+ <!-- WARNING: Do not configure application-specific resources here! -->
+ <!-- They should go in the "/WEB-INF/web.xml" file in your application. -->
<!-- ================== Common filter Configuration ==================== -->
<filter>
@@ -34,962 +34,1150 @@
<listener-class>org.jboss.web.tomcat.security.SecurityFlushSessionListener</listener-class>
</listener>
- <!-- ================== Built In Servlet Definitions ==================== -->
+ <!-- ================== Built In Servlet Definitions ==================== -->
- <!-- The default servlet for all web applications, that serves static -->
- <!-- resources. It processes all requests that are not mapped to other -->
- <!-- servlets with servlet mappings (defined either here or in your own -->
- <!-- web.xml file. This servlet supports the following initialization -->
- <!-- parameters (default values are in square brackets): -->
- <!-- -->
- <!-- debug Debugging detail level for messages logged -->
- <!-- by this servlet. [0] -->
- <!-- -->
- <!-- input Input buffer size (in bytes) when reading -->
- <!-- resources to be served. [2048] -->
- <!-- -->
- <!-- listings Should directory listings be produced if there -->
- <!-- is no welcome file in this directory? [true] -->
- <!-- -->
- <!-- output Output buffer size (in bytes) when writing -->
- <!-- resources to be served. [2048] -->
- <!-- -->
- <!-- readonly Is this context "read only", so HTTP -->
- <!-- commands like PUT and DELETE are -->
- <!-- rejected? [true] -->
- <!-- -->
- <!-- readmeFile File name to display with the directory -->
- <!-- contents. [null] -->
- <!-- -->
- <!-- For directory listing customization. Checks localXsltFile, then -->
- <!-- globalXsltFile, then defaults to original behavior. -->
- <!-- -->
- <!-- localXsltFile Make directory listings an XML doc and -->
- <!-- pass the result to this style sheet residing -->
- <!-- in that directory. This overrides -->
- <!-- globalXsltFile[null] -->
- <!-- -->
- <!-- globalXsltFile Site wide configuration version of -->
- <!-- localXsltFile This argument is expected -->
- <!-- to be a physical file. [null] -->
- <!-- -->
- <!-- -->
+ <!-- The default servlet for all web applications, that serves static -->
+ <!-- resources. It processes all requests that are not mapped to other -->
+ <!-- servlets with servlet mappings (defined either here or in your own -->
+ <!-- web.xml file. This servlet supports the following initialization -->
+ <!-- parameters (default values are in square brackets): -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
+ <!-- -->
+ <!-- fileEncoding Encoding to be used to read static resources -->
+ <!-- [platform default] -->
+ <!-- -->
+ <!-- input Input buffer size (in bytes) when reading -->
+ <!-- resources to be served. [2048] -->
+ <!-- -->
+ <!-- listings Should directory listings be produced if there -->
+ <!-- is no welcome file in this directory? [false] -->
+ <!-- WARNING: Listings for directories with many -->
+ <!-- entries can be slow and may consume -->
+ <!-- significant proportions of server resources. -->
+ <!-- -->
+ <!-- output Output buffer size (in bytes) when writing -->
+ <!-- resources to be served. [2048] -->
+ <!-- -->
+ <!-- readonly Is this context "read only", so HTTP -->
+ <!-- commands like PUT and DELETE are -->
+ <!-- rejected? [true] -->
+ <!-- -->
+ <!-- readmeFile File name to display with the directory -->
+ <!-- contents. [null] -->
+ <!-- -->
+ <!-- sendfileSize If the connector used supports sendfile, this -->
+ <!-- represents the minimal file size in KB for -->
+ <!-- which sendfile will be used. Use a negative -->
+ <!-- value to always disable sendfile. [48] -->
+ <!-- -->
+ <!-- For directory listing customization. Checks localXsltFile, then -->
+ <!-- globalXsltFile, then defaults to original behavior. -->
+ <!-- -->
+ <!-- localXsltFile Make directory listings an XML doc and -->
+ <!-- pass the result to this style sheet residing -->
+ <!-- in that directory. This overrides -->
+ <!-- globalXsltFile[null] -->
+ <!-- -->
+ <!-- globalXsltFile Site wide configuration version of -->
+ <!-- localXsltFile This argument is expected -->
+ <!-- to be a physical file. [null] -->
+ <!-- -->
+ <!-- -->
- <servlet>
- <servlet-name>default</servlet-name>
- <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
- <init-param>
- <param-name>debug</param-name>
- <param-value>0</param-value>
- </init-param>
- <init-param>
- <param-name>listings</param-name>
- <param-value>true</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
+ <servlet>
+ <servlet-name>default</servlet-name>
+ <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <init-param>
+ <param-name>listings</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
- <!-- The "invoker" servlet, which executes anonymous servlet classes -->
- <!-- that have not been defined in a web.xml file. Traditionally, this -->
- <!-- servlet is mapped to the URL pattern "/servlet/*", but you can map -->
- <!-- it to other patterns as well. The extra path info portion of such a -->
- <!-- request must be the fully qualified class name of a Java class that -->
- <!-- implements Servlet (or extends HttpServlet), or the servlet name -->
- <!-- of an existing servlet definition. This servlet supports the -->
- <!-- following initialization parameters (default values are in square -->
- <!-- brackets): -->
- <!-- -->
- <!-- debug Debugging detail level for messages logged -->
- <!-- by this servlet. [0] -->
+ <!-- The "invoker" servlet, which executes anonymous servlet classes -->
+ <!-- that have not been defined in a web.xml file. Traditionally, this -->
+ <!-- servlet is mapped to the URL pattern "/servlet/*", but you can map -->
+ <!-- it to other patterns as well. The extra path info portion of such a -->
+ <!-- request must be the fully qualified class name of a Java class that -->
+ <!-- implements Servlet (or extends HttpServlet), or the servlet name -->
+ <!-- of an existing servlet definition. This servlet supports the -->
+ <!-- following initialization parameters (default values are in square -->
+ <!-- brackets): -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
- <!--
- <servlet>
- <servlet-name>invoker</servlet-name>
- <servlet-class>
- org.apache.catalina.servlets.InvokerServlet
- </servlet-class>
- <init-param>
- <param-name>debug</param-name>
- <param-value>0</param-value>
- </init-param>
- <load-on-startup>2</load-on-startup>
- </servlet>
- -->
+<!--
+ <servlet>
+ <servlet-name>invoker</servlet-name>
+ <servlet-class>
+ org.apache.catalina.servlets.InvokerServlet
+ </servlet-class>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <load-on-startup>2</load-on-startup>
+ </servlet>
+-->
- <!-- The JSP page compiler and execution servlet, which is the mechanism -->
- <!-- used by Tomcat to support JSP pages. Traditionally, this servlet -->
- <!-- is mapped to the URL pattern "*.jsp". This servlet supports the -->
- <!-- following initialization parameters (default values are in square -->
- <!-- brackets): -->
- <!-- -->
- <!-- checkInterval If development is false and checkInterval is -->
- <!-- greater than zero, background compilations are -->
- <!-- enabled. checkInterval is the time in seconds -->
- <!-- between checks to see if a JSP page needs to -->
- <!-- be recompiled. [0] -->
- <!-- -->
- <!-- modificationTestInterval -->
- <!-- Causes a JSP (and its dependent files) to not -->
- <!-- be checked for modification during the -->
- <!-- specified time interval (in seconds) from the -->
- <!-- last time the JSP was checked for -->
- <!-- modification. A value of 0 will cause the JSP -->
- <!-- to be checked on every access. -->
- <!-- Used in development mode only. [4] -->
- <!-- -->
- <!-- compiler Which compiler Ant should use to compile JSP -->
- <!-- pages. See the Ant documentation for more -->
- <!-- information. [javac] -->
- <!-- -->
- <!-- classdebuginfo Should the class file be compiled with -->
- <!-- debugging information? [true] -->
- <!-- -->
- <!-- classpath What class path should I use while compiling -->
- <!-- generated servlets? [Created dynamically -->
- <!-- based on the current web application] -->
- <!-- -->
- <!-- development Is Jasper used in development mode? If true, -->
- <!-- the frequency at which JSPs are checked for -->
- <!-- modification may be specified via the -->
- <!-- modificationTestInterval parameter. [true] -->
- <!-- -->
- <!-- enablePooling Determines whether tag handler pooling is -->
- <!-- enabled [true] -->
- <!-- -->
- <!-- fork Tell Ant to fork compiles of JSP pages so that -->
- <!-- a separate JVM is used for JSP page compiles -->
- <!-- from the one Tomcat is running in. [true] -->
- <!-- -->
- <!-- ieClassId The class-id value to be sent to Internet -->
- <!-- Explorer when using <jsp:plugin> tags. -->
- <!-- [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93] -->
- <!-- -->
- <!-- javaEncoding Java file encoding to use for generating java -->
- <!-- source files. [UTF8] -->
- <!-- -->
- <!-- keepgenerated Should we keep the generated Java source code -->
- <!-- for each page instead of deleting it? [true] -->
- <!-- -->
- <!-- mappedfile Should we generate static content with one -->
- <!-- print statement per input line, to ease -->
- <!-- debugging? [true] -->
- <!-- -->
- <!-- trimSpaces Should white spaces in template text between -->
- <!-- actions or directives be trimmed? [false] -->
- <!-- -->
- <!-- suppressSmap Should the generation of SMAP info for JSR45 -->
- <!-- debugging be suppressed? [false] -->
- <!-- -->
- <!-- dumpSmap Should the SMAP info for JSR45 debugging be -->
- <!-- dumped to a file? [false] -->
- <!-- False if suppressSmap is true -->
- <!-- -->
- <!-- genStrAsCharArray Should text strings be generated as char -->
- <!-- arrays, to improve performance in some cases? -->
- <!-- [false] -->
- <!-- -->
- <!-- errorOnUseBeanInvalidClassAttribute -->
- <!-- Should Jasper issue an error when the value of -->
- <!-- the class attribute in an useBean action is -->
- <!-- not a valid bean class? [true] -->
- <!-- -->
- <!-- scratchdir What scratch directory should we use when -->
- <!-- compiling JSP pages? [default work directory -->
- <!-- for the current web application] -->
- <!-- -->
- <!-- xpoweredBy Determines whether X-Powered-By response -->
- <!-- header is added by generated servlet [false] -->
- <!-- -->
- <!-- If you wish to use Jikes to compile JSP pages: -->
- <!-- Set the init parameter "compiler" to "jikes". Define -->
- <!-- the property "-Dbuild.compiler.emacs=true" when starting Tomcat -->
- <!-- by adding the above to your CATALINA_OPTS environment variable. -->
- <!-- If you get an error reporting that jikes can't use UTF8 encoding, -->
- <!-- try setting the init parameter "javaEncoding" to "ISO-8859-1". -->
+ <!-- The JSP page compiler and execution servlet, which is the mechanism -->
+ <!-- used by Tomcat to support JSP pages. Traditionally, this servlet -->
+ <!-- is mapped to the URL pattern "*.jsp". This servlet supports the -->
+ <!-- following initialization parameters (default values are in square -->
+ <!-- brackets): -->
+ <!-- -->
+ <!-- checkInterval If development is false and checkInterval is -->
+ <!-- greater than zero, background compilations are -->
+ <!-- enabled. checkInterval is the time in seconds -->
+ <!-- between checks to see if a JSP page needs to -->
+ <!-- be recompiled. [0] -->
+ <!-- -->
+ <!-- modificationTestInterval -->
+ <!-- Causes a JSP (and its dependent files) to not -->
+ <!-- be checked for modification during the -->
+ <!-- specified time interval (in seconds) from the -->
+ <!-- last time the JSP was checked for -->
+ <!-- modification. A value of 0 will cause the JSP -->
+ <!-- to be checked on every access. -->
+ <!-- Used in development mode only. [4] -->
+ <!-- -->
+ <!-- compiler Which compiler Ant should use to compile JSP -->
+ <!-- pages. See the Ant documentation for more -->
+ <!-- information. [javac] -->
+ <!-- -->
+ <!-- classdebuginfo Should the class file be compiled with -->
+ <!-- debugging information? [true] -->
+ <!-- -->
+ <!-- classpath What class path should I use while compiling -->
+ <!-- generated servlets? [Created dynamically -->
+ <!-- based on the current web application] -->
+ <!-- -->
+ <!-- development Is Jasper used in development mode? If true, -->
+ <!-- the frequency at which JSPs are checked for -->
+ <!-- modification may be specified via the -->
+ <!-- modificationTestInterval parameter. [true] -->
+ <!-- -->
+ <!-- enablePooling Determines whether tag handler pooling is -->
+ <!-- enabled [true] -->
+ <!-- -->
+ <!-- fork Tell Ant to fork compiles of JSP pages so that -->
+ <!-- a separate JVM is used for JSP page compiles -->
+ <!-- from the one Tomcat is running in. [true] -->
+ <!-- -->
+ <!-- ieClassId The class-id value to be sent to Internet -->
+ <!-- Explorer when using <jsp:plugin> tags. -->
+ <!-- [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93] -->
+ <!-- -->
+ <!-- javaEncoding Java file encoding to use for generating java -->
+ <!-- source files. [UTF8] -->
+ <!-- -->
+ <!-- keepgenerated Should we keep the generated Java source code -->
+ <!-- for each page instead of deleting it? [true] -->
+ <!-- -->
+ <!-- mappedfile Should we generate static content with one -->
+ <!-- print statement per input line, to ease -->
+ <!-- debugging? [true] -->
+ <!-- -->
+ <!-- trimSpaces Should white spaces in template text between -->
+ <!-- actions or directives be trimmed? [false] -->
+ <!-- -->
+ <!-- suppressSmap Should the generation of SMAP info for JSR45 -->
+ <!-- debugging be suppressed? [false] -->
+ <!-- -->
+ <!-- dumpSmap Should the SMAP info for JSR45 debugging be -->
+ <!-- dumped to a file? [false] -->
+ <!-- False if suppressSmap is true -->
+ <!-- -->
+ <!-- genStrAsCharArray Should text strings be generated as char -->
+ <!-- arrays, to improve performance in some cases? -->
+ <!-- [false] -->
+ <!-- -->
+ <!-- errorOnUseBeanInvalidClassAttribute -->
+ <!-- Should Jasper issue an error when the value of -->
+ <!-- the class attribute in an useBean action is -->
+ <!-- not a valid bean class? [true] -->
+ <!-- -->
+ <!-- scratchdir What scratch directory should we use when -->
+ <!-- compiling JSP pages? [default work directory -->
+ <!-- for the current web application] -->
+ <!-- -->
+ <!-- xpoweredBy Determines whether X-Powered-By response -->
+ <!-- header is added by generated servlet [false] -->
+ <!-- -->
+ <!-- compilerTargetVM Compiler target VM -->
+ <!-- default is System.properties -->
+ <!-- java.specification.version > 1.4 -->
+ <!-- [1.5] else [1.4] -->
+ <!-- -->
+ <!-- compilerSourceVM Compiler source VM -->
+ <!-- default is System.properties -->
+ <!-- java.specification.version > 1.4 -->
+ <!-- [1.5] else [1.4] -->
+ <!-- -->
+ <!-- If you wish to use Jikes to compile JSP pages: -->
+ <!-- Please see the "Using Jikes" section of the Jasper-HowTo -->
+ <!-- page in the Tomcat documentation. -->
- <servlet>
- <servlet-name>jsp</servlet-name>
- <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
- <init-param>
- <param-name>fork</param-name>
- <param-value>false</param-value>
- </init-param>
- <init-param>
- <param-name>xpoweredBy</param-name>
- <param-value>false</param-value>
- </init-param>
- <!-- Uncomment to use jdk1.5 features in jsp pages
- <init-param>
- <param-name>compilerSourceVM</param-name>
- <param-value>1.5</param-value>
- </init-param>
- -->
- <!-- Use a custom options class to allow the shared tag lib descriptors
- to be loaded from jars in the tomcat sar conf/tlds directory. The
- standard options implementation can only find taglibs based on the
- class loader classpath.
- -->
- <init-param>
- <param-name>engineOptionsClass</param-name>
- <param-value>org.jboss.web.tomcat.tc5.jasper.JspServletOptions</param-value>
- </init-param>
- <!-- Specify the jars relative to the jbossweb-tomcat55.sar that should
- be scanned for common tag lib descriptors to include in every war
- deployment.
- -->
- <init-param>
- <description>MyFaces tlds</description>
- <param-name>tagLibJar0</param-name>
- <param-value>jsf-libs/myfaces-impl.jar</param-value>
- </init-param>
+ <servlet>
+ <servlet-name>jsp</servlet-name>
+ <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+ <init-param>
+ <param-name>fork</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>xpoweredBy</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <load-on-startup>3</load-on-startup>
+ </servlet>
- <load-on-startup>3</load-on-startup>
- </servlet>
+ <!-- NOTE: An SSI Filter is also available as an alternative SSI -->
+ <!-- implementation. Use either the Servlet or the Filter but NOT both. -->
+ <!-- -->
+ <!-- Server Side Includes processing servlet, which processes SSI -->
+ <!-- directives in HTML pages consistent with similar support in web -->
+ <!-- servers like Apache. Traditionally, this servlet is mapped to the -->
+ <!-- URL pattern "*.shtml". This servlet supports the following -->
+ <!-- initialization parameters (default values are in square brackets): -->
+ <!-- -->
+ <!-- buffered Should output from this servlet be buffered? -->
+ <!-- (0=false, 1=true) [0] -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
+ <!-- -->
+ <!-- expires The number of seconds before a page with SSI -->
+ <!-- directives will expire. [No default] -->
+ <!-- -->
+ <!-- isVirtualWebappRelative -->
+ <!-- Should "virtual" paths be interpreted as -->
+ <!-- relative to the context root, instead of -->
+ <!-- the server root? (0=false, 1=true) [0] -->
+ <!-- -->
+ <!-- inputEncoding The encoding to assume for SSI resources if -->
+ <!-- one is not available from the resource. -->
+ <!-- [Platform default] -->
+ <!-- -->
+ <!-- outputEncoding The encoding to use for the page that results -->
+ <!-- from the SSI processing. [UTF-8] -->
+ <!-- -->
+ <!-- -->
+ <!-- IMPORTANT: To use the SSI servlet, you also need to rename the -->
+ <!-- $CATALINA_HOME/server/lib/servlets-ssi.renametojar file -->
+ <!-- to $CATALINA_HOME/server/lib/servlets-ssi.jar -->
- <!-- Server Side Includes processing servlet, which processes SSI -->
- <!-- directives in HTML pages consistent with similar support in web -->
- <!-- servers like Apache. Traditionally, this servlet is mapped to the -->
- <!-- URL pattern "*.shtml". This servlet supports the following -->
- <!-- initialization parameters (default values are in square brackets): -->
- <!-- -->
- <!-- buffered Should output from this servlet be buffered? -->
- <!-- (0=false, 1=true) [0] -->
- <!-- -->
- <!-- debug Debugging detail level for messages logged -->
- <!-- by this servlet. [0] -->
- <!-- -->
- <!-- expires The number of seconds before a page with SSI -->
- <!-- directives will expire. [No default] -->
- <!-- -->
- <!-- isVirtualWebappRelative -->
- <!-- Should "virtual" paths be interpreted as -->
- <!-- relative to the context root, instead of -->
- <!-- the server root? (0=false, 1=true) [0] -->
- <!-- -->
- <!-- -->
- <!-- IMPORTANT: To use the SSI servlet, you also need to rename the -->
- <!-- $CATALINA_HOME/server/lib/servlets-ssi.renametojar file -->
- <!-- to $CATALINA_HOME/server/lib/servlets-ssi.jar -->
+<!--
+ <servlet>
+ <servlet-name>ssi</servlet-name>
+ <servlet-class>
+ org.apache.catalina.ssi.SSIServlet
+ </servlet-class>
+ <init-param>
+ <param-name>buffered</param-name>
+ <param-value>1</param-value>
+ </init-param>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <init-param>
+ <param-name>expires</param-name>
+ <param-value>666</param-value>
+ </init-param>
+ <init-param>
+ <param-name>isVirtualWebappRelative</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <load-on-startup>4</load-on-startup>
+ </servlet>
+-->
- <!--
- <servlet>
- <servlet-name>ssi</servlet-name>
- <servlet-class>
- org.apache.catalina.ssi.SSIServlet
- </servlet-class>
- <init-param>
- <param-name>buffered</param-name>
- <param-value>1</param-value>
- </init-param>
- <init-param>
- <param-name>debug</param-name>
- <param-value>0</param-value>
- </init-param>
- <init-param>
- <param-name>expires</param-name>
- <param-value>666</param-value>
- </init-param>
- <init-param>
- <param-name>isVirtualWebappRelative</param-name>
- <param-value>0</param-value>
- </init-param>
- <load-on-startup>4</load-on-startup>
- </servlet>
- -->
+ <!-- Common Gateway Includes (CGI) processing servlet, which supports -->
+ <!-- execution of external applications that conform to the CGI spec -->
+ <!-- requirements. Typically, this servlet is mapped to the URL pattern -->
+ <!-- "/cgi-bin/*", which means that any CGI applications that are -->
+ <!-- executed must be present within the web application. This servlet -->
+ <!-- supports the following initialization parameters (default values -->
+ <!-- are in square brackets): -->
+ <!-- -->
+ <!-- cgiPathPrefix The CGI search path will start at -->
+ <!-- webAppRootDir + File.separator + this prefix. -->
+ <!-- [WEB-INF/cgi] -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
+ <!-- -->
+ <!-- executable Name of the exectuable used to run the -->
+ <!-- script. [perl] -->
+ <!-- -->
+ <!-- parameterEncoding Name of parameter encoding to be used with -->
+ <!-- CGI servlet. -->
+ <!-- [System.getProperty("file.encoding","UTF-8")] -->
+ <!-- -->
+ <!-- passShellEnvironment Should the shell environment variables (if -->
+ <!-- any) be passed to the CGI script? [false] -->
+ <!-- -->
+ <!-- IMPORTANT: To use the CGI servlet, you also need to rename the -->
+ <!-- $CATALINA_HOME/server/lib/servlets-cgi.renametojar file -->
+ <!-- to $CATALINA_HOME/server/lib/servlets-cgi.jar -->
- <!-- Common Gateway Includes (CGI) processing servlet, which supports -->
- <!-- execution of external applications that conform to the CGI spec -->
- <!-- requirements. Typically, this servlet is mapped to the URL pattern -->
- <!-- "/cgi-bin/*", which means that any CGI applications that are -->
- <!-- executed must be present within the web application. This servlet -->
- <!-- supports the following initialization parameters (default values -->
- <!-- are in square brackets): -->
- <!-- -->
- <!-- cgiPathPrefix The CGI search path will start at -->
- <!-- webAppRootDir + File.separator + this prefix. -->
- <!-- [WEB-INF/cgi] -->
- <!-- -->
- <!-- debug Debugging detail level for messages logged -->
- <!-- by this servlet. [0] -->
- <!-- -->
- <!-- executable Name of the exectuable used to run the -->
- <!-- script. [perl] -->
- <!-- -->
- <!-- parameterEncoding Name of parameter encoding to be used with -->
- <!-- CGI servlet. -->
- <!-- [System.getProperty("file.encoding","UTF-8")] -->
- <!-- -->
- <!-- passShellEnvironment Should the shell environment variables (if -->
- <!-- any) be passed to the CGI script? [false] -->
- <!-- -->
- <!-- IMPORTANT: To use the CGI servlet, you also need to rename the -->
- <!-- $CATALINA_HOME/server/lib/servlets-cgi.renametojar file -->
- <!-- to $CATALINA_HOME/server/lib/servlets-cgi.jar -->
+<!--
+ <servlet>
+ <servlet-name>cgi</servlet-name>
+ <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <init-param>
+ <param-name>cgiPathPrefix</param-name>
+ <param-value>WEB-INF/cgi</param-value>
+ </init-param>
+ <load-on-startup>5</load-on-startup>
+ </servlet>
+-->
- <!--
- <servlet>
- <servlet-name>cgi</servlet-name>
- <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
- <init-param>
- <param-name>debug</param-name>
- <param-value>6</param-value>
- </init-param>
- <init-param>
- <param-name>cgiPathPrefix</param-name>
- <param-value>WEB-INF/cgi</param-value>
- </init-param>
- <load-on-startup>5</load-on-startup>
- </servlet>
- -->
+ <!-- ================ Built In Servlet Mappings ========================= -->
- <!-- ================ Built In Servlet Mappings ========================= -->
+ <!-- The servlet mappings for the built in servlets defined above. Note -->
+ <!-- that, by default, the CGI and SSI servlets are *not* mapped. You -->
+ <!-- must uncomment these mappings (or add them to your application's own -->
+ <!-- web.xml deployment descriptor) to enable these services -->
- <!-- The servlet mappings for the built in servlets defined above. Note -->
- <!-- that, by default, the CGI and SSI servlets are *not* mapped. You -->
- <!-- must uncomment these mappings (or add them to your application's own -->
- <!-- web.xml deployment descriptor) to enable these services -->
+ <!-- The mapping for the default servlet -->
+ <servlet-mapping>
+ <servlet-name>default</servlet-name>
+ <url-pattern>/</url-pattern>
+ </servlet-mapping>
- <!-- The mapping for the default servlet -->
- <servlet-mapping>
- <servlet-name>default</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
+ <!-- The mapping for the invoker servlet -->
+<!--
+ <servlet-mapping>
+ <servlet-name>invoker</servlet-name>
+ <url-pattern>/servlet/*</url-pattern>
+ </servlet-mapping>
+-->
- <!-- The mapping for the invoker servlet -->
- <!--
- <servlet-mapping>
- <servlet-name>invoker</servlet-name>
- <url-pattern>/servlet/*</url-pattern>
- </servlet-mapping>
- -->
+ <!-- The mapping for the JSP servlet -->
+ <servlet-mapping>
+ <servlet-name>jsp</servlet-name>
+ <url-pattern>*.jsp</url-pattern>
+ </servlet-mapping>
- <!-- The mapping for the JSP servlet -->
- <servlet-mapping>
- <servlet-name>jsp</servlet-name>
- <url-pattern>*.jsp</url-pattern>
- </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>jsp</servlet-name>
+ <url-pattern>*.jspx</url-pattern>
+ </servlet-mapping>
- <servlet-mapping>
- <servlet-name>jsp</servlet-name>
- <url-pattern>*.jspx</url-pattern>
- </servlet-mapping>
+ <!-- The mapping for the SSI servlet -->
+<!--
+ <servlet-mapping>
+ <servlet-name>ssi</servlet-name>
+ <url-pattern>*.shtml</url-pattern>
+ </servlet-mapping>
+-->
- <!-- The mapping for the SSI servlet -->
- <!--
- <servlet-mapping>
- <servlet-name>ssi</servlet-name>
- <url-pattern>*.shtml</url-pattern>
- </servlet-mapping>
- -->
+ <!-- The mapping for the CGI Gateway servlet -->
- <!-- The mapping for the CGI Gateway servlet -->
+<!--
+ <servlet-mapping>
+ <servlet-name>cgi</servlet-name>
+ <url-pattern>/cgi-bin/*</url-pattern>
+ </servlet-mapping>
+-->
- <!--
- <servlet-mapping>
- <servlet-name>cgi</servlet-name>
- <url-pattern>/cgi-bin/*</url-pattern>
- </servlet-mapping>
- -->
+ <!-- ================== Built In Filter Definitions ===================== -->
- <!-- ==================== Default Session Configuration ================= -->
- <!-- You can set the default session timeout (in minutes) for all newly -->
- <!-- created sessions by modifying the value below. -->
+ <!-- NOTE: An SSI Servlet is also available as an alternative SSI -->
+ <!-- implementation. Use either the Servlet or the Filter but NOT both. -->
+ <!-- -->
+ <!-- Server Side Includes processing filter, which processes SSI -->
+ <!-- directives in HTML pages consistent with similar support in web -->
+ <!-- servers like Apache. Traditionally, this filter is mapped to the -->
+ <!-- URL pattern "*.shtml", though it can be mapped to "*" as it will -->
+ <!-- selectively enable/disable SSI processing based on mime types. For -->
+ <!-- this to work you will need to uncomment the .shtml mime type -->
+ <!-- definition towards the bottom of this file. -->
+ <!-- The contentType init param allows you to apply SSI processing to JSP -->
+ <!-- pages, javascript, or any other content you wish. This filter -->
+ <!-- supports the following initialization parameters (default values are -->
+ <!-- in square brackets): -->
+ <!-- -->
+ <!-- contentType A regex pattern that must be matched before -->
+ <!-- SSI processing is applied. -->
+ <!-- [text/x-server-parsed-html(;.*)?] -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
+ <!-- -->
+ <!-- expires The number of seconds before a page with SSI -->
+ <!-- directives will expire. [No default] -->
+ <!-- -->
+ <!-- isVirtualWebappRelative -->
+ <!-- Should "virtual" paths be interpreted as -->
+ <!-- relative to the context root, instead of -->
+ <!-- the server root? (0=false, 1=true) [0] -->
+ <!-- -->
+ <!-- -->
+ <!-- IMPORTANT: To use the SSI filter, you also need to rename the -->
+ <!-- $CATALINA_HOME/server/lib/servlets-ssi.renametojar file -->
+ <!-- to $CATALINA_HOME/server/lib/servlets-ssi.jar -->
- <session-config>
- <session-timeout>30</session-timeout>
- </session-config>
+<!--
+ <filter>
+ <filter-name>ssi</filter-name>
+ <filter-class>
+ org.apache.catalina.ssi.SSIFilter
+ </filter-class>
+ <init-param>
+ <param-name>contentType</param-name>
+ <param-value>text/x-server-parsed-html(;.*)?</param-value>
+ </init-param>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <init-param>
+ <param-name>expires</param-name>
+ <param-value>666</param-value>
+ </init-param>
+ <init-param>
+ <param-name>isVirtualWebappRelative</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ </filter>
+-->
- <!-- ===================== Default MIME Type Mappings =================== -->
- <!-- When serving static resources, Tomcat will automatically generate -->
- <!-- a "Content-Type" header based on the resource's filename extension, -->
- <!-- based on these mappings. Additional mappings can be added here (to -->
- <!-- apply to all web applications), or in your own application's web.xml -->
- <!-- deployment descriptor. -->
+ <!-- ==================== Built In Filter Mappings ====================== -->
- <mime-mapping>
- <extension>abs</extension>
- <mime-type>audio/x-mpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>ai</extension>
- <mime-type>application/postscript</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>aif</extension>
- <mime-type>audio/x-aiff</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>aifc</extension>
- <mime-type>audio/x-aiff</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>aiff</extension>
- <mime-type>audio/x-aiff</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>aim</extension>
- <mime-type>application/x-aim</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>art</extension>
- <mime-type>image/x-jg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>asf</extension>
- <mime-type>video/x-ms-asf</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>asx</extension>
- <mime-type>video/x-ms-asf</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>au</extension>
- <mime-type>audio/basic</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>avi</extension>
- <mime-type>video/x-msvideo</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>avx</extension>
- <mime-type>video/x-rad-screenplay</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>bcpio</extension>
- <mime-type>application/x-bcpio</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>bin</extension>
- <mime-type>application/octet-stream</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>bmp</extension>
- <mime-type>image/bmp</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>body</extension>
- <mime-type>text/html</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>cdf</extension>
- <mime-type>application/x-cdf</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>cer</extension>
- <mime-type>application/x-x509-ca-cert</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>class</extension>
- <mime-type>application/java</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>cpio</extension>
- <mime-type>application/x-cpio</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>csh</extension>
- <mime-type>application/x-csh</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>css</extension>
- <mime-type>text/css</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>dib</extension>
- <mime-type>image/bmp</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>doc</extension>
- <mime-type>application/msword</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>dtd</extension>
- <mime-type>text/plain</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>dv</extension>
- <mime-type>video/x-dv</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>dvi</extension>
- <mime-type>application/x-dvi</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>eps</extension>
- <mime-type>application/postscript</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>etx</extension>
- <mime-type>text/x-setext</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>exe</extension>
- <mime-type>application/octet-stream</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>gif</extension>
- <mime-type>image/gif</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>gtar</extension>
- <mime-type>application/x-gtar</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>gz</extension>
- <mime-type>application/x-gzip</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>hdf</extension>
- <mime-type>application/x-hdf</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>hqx</extension>
- <mime-type>application/mac-binhex40</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>htc</extension>
- <mime-type>text/x-component</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>htm</extension>
- <mime-type>text/html</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>html</extension>
- <mime-type>text/html</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>hqx</extension>
- <mime-type>application/mac-binhex40</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>ief</extension>
- <mime-type>image/ief</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>jad</extension>
- <mime-type>text/vnd.sun.j2me.app-descriptor</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>jar</extension>
- <mime-type>application/java-archive</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>java</extension>
- <mime-type>text/plain</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>jnlp</extension>
- <mime-type>application/x-java-jnlp-file</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>jpe</extension>
- <mime-type>image/jpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>jpeg</extension>
- <mime-type>image/jpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>jpg</extension>
- <mime-type>image/jpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>js</extension>
- <mime-type>text/javascript</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>jsf</extension>
- <mime-type>text/plain</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>jspf</extension>
- <mime-type>text/plain</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>kar</extension>
- <mime-type>audio/x-midi</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>latex</extension>
- <mime-type>application/x-latex</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>m3u</extension>
- <mime-type>audio/x-mpegurl</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mac</extension>
- <mime-type>image/x-macpaint</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>man</extension>
- <mime-type>application/x-troff-man</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>me</extension>
- <mime-type>application/x-troff-me</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mid</extension>
- <mime-type>audio/x-midi</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>midi</extension>
- <mime-type>audio/x-midi</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mif</extension>
- <mime-type>application/x-mif</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mov</extension>
- <mime-type>video/quicktime</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>movie</extension>
- <mime-type>video/x-sgi-movie</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mp1</extension>
- <mime-type>audio/x-mpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mp2</extension>
- <mime-type>audio/x-mpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mp3</extension>
- <mime-type>audio/x-mpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mpa</extension>
- <mime-type>audio/x-mpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mpe</extension>
- <mime-type>video/mpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mpeg</extension>
- <mime-type>video/mpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mpega</extension>
- <mime-type>audio/x-mpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mpg</extension>
- <mime-type>video/mpeg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>mpv2</extension>
- <mime-type>video/mpeg2</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>ms</extension>
- <mime-type>application/x-wais-source</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>nc</extension>
- <mime-type>application/x-netcdf</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>oda</extension>
- <mime-type>application/oda</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>pbm</extension>
- <mime-type>image/x-portable-bitmap</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>pct</extension>
- <mime-type>image/pict</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>pdf</extension>
- <mime-type>application/pdf</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>pgm</extension>
- <mime-type>image/x-portable-graymap</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>pic</extension>
- <mime-type>image/pict</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>pict</extension>
- <mime-type>image/pict</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>pls</extension>
- <mime-type>audio/x-scpls</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>png</extension>
- <mime-type>image/png</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>pnm</extension>
- <mime-type>image/x-portable-anymap</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>pnt</extension>
- <mime-type>image/x-macpaint</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>ppm</extension>
- <mime-type>image/x-portable-pixmap</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>ppt</extension>
- <mime-type>application/powerpoint</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>ps</extension>
- <mime-type>application/postscript</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>psd</extension>
- <mime-type>image/x-photoshop</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>qt</extension>
- <mime-type>video/quicktime</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>qti</extension>
- <mime-type>image/x-quicktime</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>qtif</extension>
- <mime-type>image/x-quicktime</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>ras</extension>
- <mime-type>image/x-cmu-raster</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>rgb</extension>
- <mime-type>image/x-rgb</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>rm</extension>
- <mime-type>application/vnd.rn-realmedia</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>roff</extension>
- <mime-type>application/x-troff</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>rtf</extension>
- <mime-type>application/rtf</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>rtx</extension>
- <mime-type>text/richtext</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>sh</extension>
- <mime-type>application/x-sh</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>shar</extension>
- <mime-type>application/x-shar</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>smf</extension>
- <mime-type>audio/x-midi</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>sit</extension>
- <mime-type>application/x-stuffit</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>snd</extension>
- <mime-type>audio/basic</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>src</extension>
- <mime-type>application/x-wais-source</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>sv4cpio</extension>
- <mime-type>application/x-sv4cpio</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>sv4crc</extension>
- <mime-type>application/x-sv4crc</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>swf</extension>
- <mime-type>application/x-shockwave-flash</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>t</extension>
- <mime-type>application/x-troff</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>tar</extension>
- <mime-type>application/x-tar</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>tcl</extension>
- <mime-type>application/x-tcl</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>tex</extension>
- <mime-type>application/x-tex</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>texi</extension>
- <mime-type>application/x-texinfo</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>texinfo</extension>
- <mime-type>application/x-texinfo</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>tif</extension>
- <mime-type>image/tiff</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>tiff</extension>
- <mime-type>image/tiff</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>tr</extension>
- <mime-type>application/x-troff</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>tsv</extension>
- <mime-type>text/tab-separated-values</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>txt</extension>
- <mime-type>text/plain</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>ulw</extension>
- <mime-type>audio/basic</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>ustar</extension>
- <mime-type>application/x-ustar</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>xbm</extension>
- <mime-type>image/x-xbitmap</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>xht</extension>
- <mime-type>application/xhtml</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>xhtml</extension>
- <mime-type>application/xhtml</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>xml</extension>
- <mime-type>text/xml</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>xpm</extension>
- <mime-type>image/x-xpixmap</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>xsl</extension>
- <mime-type>text/xml</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>xwd</extension>
- <mime-type>image/x-xwindowdump</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>wav</extension>
- <mime-type>audio/x-wav</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>svg</extension>
- <mime-type>image/svg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>svgz</extension>
- <mime-type>image/svg</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>vsd</extension>
- <mime-type>application/x-visio</mime-type>
- </mime-mapping>
- <mime-mapping>
- <!-- Wireless Bitmap -->
- <extension>wbmp</extension>
- <mime-type>image/vnd.wap.wbmp</mime-type>
- </mime-mapping>
- <mime-mapping>
- <!-- WML Source -->
- <extension>wml</extension>
- <mime-type>text/vnd.wap.wml</mime-type>
- </mime-mapping>
- <mime-mapping>
- <!-- Compiled WML -->
- <extension>wmlc</extension>
- <mime-type>application/vnd.wap.wmlc</mime-type>
- </mime-mapping>
- <mime-mapping>
- <!-- WML Script Source -->
- <extension>wmls</extension>
- <mime-type>text/vnd.wap.wmlscript</mime-type>
- </mime-mapping>
- <mime-mapping>
- <!-- Compiled WML Script -->
- <extension>wmlscriptc</extension>
- <mime-type>application/vnd.wap.wmlscriptc</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>wrl</extension>
- <mime-type>x-world/x-vrml</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>Z</extension>
- <mime-type>application/x-compress</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>z</extension>
- <mime-type>application/x-compress</mime-type>
- </mime-mapping>
- <mime-mapping>
- <extension>zip</extension>
- <mime-type>application/zip</mime-type>
- </mime-mapping>
+ <!-- The mapping for the SSI Filter -->
+<!--
+ <filter-mapping>
+ <filter-name>ssi</filter-name>
+ <url-pattern>*.shtml</url-pattern>
+ </filter-mapping>
+-->
- <!-- ==================== Default Welcome File List ===================== -->
- <!-- When a request URI refers to a directory, the default servlet looks -->
- <!-- for a "welcome file" within that directory and, if present, -->
- <!-- to the corresponding resource URI for display. If no welcome file -->
- <!-- is present, the default servlet either serves a directory listing, -->
- <!-- or returns a 404 status, depending on how it is configured. -->
- <!-- -->
- <!-- If you define welcome files in your own application's web.xml -->
- <!-- deployment descriptor, that list *replaces* the list configured -->
- <!-- here, so be sure that you include any of the default values that -->
- <!-- you wish to include. -->
+ <!-- ==================== Default Session Configuration ================= -->
+ <!-- You can set the default session timeout (in minutes) for all newly -->
+ <!-- created sessions by modifying the value below. -->
- <welcome-file-list>
- <welcome-file>index.html</welcome-file>
- <welcome-file>index.htm</welcome-file>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
+ <session-config>
+ <session-timeout>30</session-timeout>
+ </session-config>
+
+ <!-- ===================== Default MIME Type Mappings =================== -->
+ <!-- When serving static resources, Tomcat will automatically generate -->
+ <!-- a "Content-Type" header based on the resource's filename extension, -->
+ <!-- based on these mappings. Additional mappings can be added here (to -->
+ <!-- apply to all web applications), or in your own application's web.xml -->
+ <!-- deployment descriptor. -->
+
+ <mime-mapping>
+ <extension>abs</extension>
+ <mime-type>audio/x-mpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ai</extension>
+ <mime-type>application/postscript</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>aif</extension>
+ <mime-type>audio/x-aiff</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>aifc</extension>
+ <mime-type>audio/x-aiff</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>aiff</extension>
+ <mime-type>audio/x-aiff</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>aim</extension>
+ <mime-type>application/x-aim</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>art</extension>
+ <mime-type>image/x-jg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>asf</extension>
+ <mime-type>video/x-ms-asf</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>asx</extension>
+ <mime-type>video/x-ms-asf</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>au</extension>
+ <mime-type>audio/basic</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>avi</extension>
+ <mime-type>video/x-msvideo</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>avx</extension>
+ <mime-type>video/x-rad-screenplay</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>bcpio</extension>
+ <mime-type>application/x-bcpio</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>bin</extension>
+ <mime-type>application/octet-stream</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>bmp</extension>
+ <mime-type>image/bmp</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>body</extension>
+ <mime-type>text/html</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>cdf</extension>
+ <mime-type>application/x-cdf</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>cer</extension>
+ <mime-type>application/x-x509-ca-cert</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>class</extension>
+ <mime-type>application/java</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>cpio</extension>
+ <mime-type>application/x-cpio</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>csh</extension>
+ <mime-type>application/x-csh</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>css</extension>
+ <mime-type>text/css</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>dib</extension>
+ <mime-type>image/bmp</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>doc</extension>
+ <mime-type>application/msword</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>dtd</extension>
+ <mime-type>application/xml-dtd</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>dv</extension>
+ <mime-type>video/x-dv</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>dvi</extension>
+ <mime-type>application/x-dvi</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>eps</extension>
+ <mime-type>application/postscript</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>etx</extension>
+ <mime-type>text/x-setext</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>exe</extension>
+ <mime-type>application/octet-stream</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>gif</extension>
+ <mime-type>image/gif</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>gtar</extension>
+ <mime-type>application/x-gtar</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>gz</extension>
+ <mime-type>application/x-gzip</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>hdf</extension>
+ <mime-type>application/x-hdf</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>hqx</extension>
+ <mime-type>application/mac-binhex40</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>htc</extension>
+ <mime-type>text/x-component</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>htm</extension>
+ <mime-type>text/html</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>html</extension>
+ <mime-type>text/html</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>hqx</extension>
+ <mime-type>application/mac-binhex40</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ief</extension>
+ <mime-type>image/ief</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>jad</extension>
+ <mime-type>text/vnd.sun.j2me.app-descriptor</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>jar</extension>
+ <mime-type>application/java-archive</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>java</extension>
+ <mime-type>text/plain</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>jnlp</extension>
+ <mime-type>application/x-java-jnlp-file</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>jpe</extension>
+ <mime-type>image/jpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>jpeg</extension>
+ <mime-type>image/jpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>jpg</extension>
+ <mime-type>image/jpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>js</extension>
+ <mime-type>text/javascript</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>jsf</extension>
+ <mime-type>text/plain</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>jspf</extension>
+ <mime-type>text/plain</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>kar</extension>
+ <mime-type>audio/x-midi</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>latex</extension>
+ <mime-type>application/x-latex</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>m3u</extension>
+ <mime-type>audio/x-mpegurl</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mac</extension>
+ <mime-type>image/x-macpaint</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>man</extension>
+ <mime-type>application/x-troff-man</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mathml</extension>
+ <mime-type>application/mathml+xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>me</extension>
+ <mime-type>application/x-troff-me</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mid</extension>
+ <mime-type>audio/x-midi</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>midi</extension>
+ <mime-type>audio/x-midi</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mif</extension>
+ <mime-type>application/x-mif</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mov</extension>
+ <mime-type>video/quicktime</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>movie</extension>
+ <mime-type>video/x-sgi-movie</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mp1</extension>
+ <mime-type>audio/x-mpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mp2</extension>
+ <mime-type>audio/x-mpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mp3</extension>
+ <mime-type>audio/x-mpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mpa</extension>
+ <mime-type>audio/x-mpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mpe</extension>
+ <mime-type>video/mpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mpeg</extension>
+ <mime-type>video/mpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mpega</extension>
+ <mime-type>audio/x-mpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mpg</extension>
+ <mime-type>video/mpeg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>mpv2</extension>
+ <mime-type>video/mpeg2</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ms</extension>
+ <mime-type>application/x-wais-source</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>nc</extension>
+ <mime-type>application/x-netcdf</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>oda</extension>
+ <mime-type>application/oda</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Database -->
+ <extension>odb</extension>
+ <mime-type>application/vnd.oasis.opendocument.database</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Chart -->
+ <extension>odc</extension>
+ <mime-type>application/vnd.oasis.opendocument.chart</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Formula -->
+ <extension>odf</extension>
+ <mime-type>application/vnd.oasis.opendocument.formula</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Drawing -->
+ <extension>odg</extension>
+ <mime-type>application/vnd.oasis.opendocument.graphics</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Image -->
+ <extension>odi</extension>
+ <mime-type>application/vnd.oasis.opendocument.image</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Master Document -->
+ <extension>odm</extension>
+ <mime-type>application/vnd.oasis.opendocument.text-master</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Presentation -->
+ <extension>odp</extension>
+ <mime-type>application/vnd.oasis.opendocument.presentation</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Spreadsheet -->
+ <extension>ods</extension>
+ <mime-type>application/vnd.oasis.opendocument.spreadsheet</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Text -->
+ <extension>odt</extension>
+ <mime-type>application/vnd.oasis.opendocument.text</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ogg</extension>
+ <mime-type>application/ogg</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Drawing Template -->
+ <extension>otg </extension>
+ <mime-type>application/vnd.oasis.opendocument.graphics-template</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- HTML Document Template -->
+ <extension>oth</extension>
+ <mime-type>application/vnd.oasis.opendocument.text-web</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Presentation Template -->
+ <extension>otp</extension>
+ <mime-type>application/vnd.oasis.opendocument.presentation-template</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Spreadsheet Template -->
+ <extension>ots</extension>
+ <mime-type>application/vnd.oasis.opendocument.spreadsheet-template </mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- OpenDocument Text Template -->
+ <extension>ott</extension>
+ <mime-type>application/vnd.oasis.opendocument.text-template</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>pbm</extension>
+ <mime-type>image/x-portable-bitmap</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>pct</extension>
+ <mime-type>image/pict</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>pdf</extension>
+ <mime-type>application/pdf</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>pgm</extension>
+ <mime-type>image/x-portable-graymap</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>pic</extension>
+ <mime-type>image/pict</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>pict</extension>
+ <mime-type>image/pict</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>pls</extension>
+ <mime-type>audio/x-scpls</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>png</extension>
+ <mime-type>image/png</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>pnm</extension>
+ <mime-type>image/x-portable-anymap</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>pnt</extension>
+ <mime-type>image/x-macpaint</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ppm</extension>
+ <mime-type>image/x-portable-pixmap</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ppt</extension>
+ <mime-type>application/powerpoint</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ps</extension>
+ <mime-type>application/postscript</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>psd</extension>
+ <mime-type>image/x-photoshop</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>qt</extension>
+ <mime-type>video/quicktime</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>qti</extension>
+ <mime-type>image/x-quicktime</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>qtif</extension>
+ <mime-type>image/x-quicktime</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ras</extension>
+ <mime-type>image/x-cmu-raster</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>rdf</extension>
+ <mime-type>application/rdf+xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>rgb</extension>
+ <mime-type>image/x-rgb</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>rm</extension>
+ <mime-type>application/vnd.rn-realmedia</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>roff</extension>
+ <mime-type>application/x-troff</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>rtf</extension>
+ <mime-type>application/rtf</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>rtx</extension>
+ <mime-type>text/richtext</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>sh</extension>
+ <mime-type>application/x-sh</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>shar</extension>
+ <mime-type>application/x-shar</mime-type>
+ </mime-mapping>
+<!--
+ <mime-mapping>
+ <extension>shtml</extension>
+ <mime-type>text/x-server-parsed-html</mime-type>
+ </mime-mapping>
+-->
+ <mime-mapping>
+ <extension>smf</extension>
+ <mime-type>audio/x-midi</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>sit</extension>
+ <mime-type>application/x-stuffit</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>snd</extension>
+ <mime-type>audio/basic</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>src</extension>
+ <mime-type>application/x-wais-source</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>sv4cpio</extension>
+ <mime-type>application/x-sv4cpio</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>sv4crc</extension>
+ <mime-type>application/x-sv4crc</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>swf</extension>
+ <mime-type>application/x-shockwave-flash</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>t</extension>
+ <mime-type>application/x-troff</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>tar</extension>
+ <mime-type>application/x-tar</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>tcl</extension>
+ <mime-type>application/x-tcl</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>tex</extension>
+ <mime-type>application/x-tex</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>texi</extension>
+ <mime-type>application/x-texinfo</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>texinfo</extension>
+ <mime-type>application/x-texinfo</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>tif</extension>
+ <mime-type>image/tiff</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>tiff</extension>
+ <mime-type>image/tiff</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>tr</extension>
+ <mime-type>application/x-troff</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>tsv</extension>
+ <mime-type>text/tab-separated-values</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>txt</extension>
+ <mime-type>text/plain</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ulw</extension>
+ <mime-type>audio/basic</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ustar</extension>
+ <mime-type>application/x-ustar</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>vxml</extension>
+ <mime-type>application/voicexml+xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xbm</extension>
+ <mime-type>image/x-xbitmap</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xht</extension>
+ <mime-type>application/xhtml+xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xhtml</extension>
+ <mime-type>application/xhtml+xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xml</extension>
+ <mime-type>application/xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xpm</extension>
+ <mime-type>image/x-xpixmap</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xsl</extension>
+ <mime-type>application/xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xslt</extension>
+ <mime-type>application/xslt+xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xul</extension>
+ <mime-type>application/vnd.mozilla.xul+xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xwd</extension>
+ <mime-type>image/x-xwindowdump</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>wav</extension>
+ <mime-type>audio/x-wav</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>svg</extension>
+ <mime-type>image/svg+xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>svgz</extension>
+ <mime-type>image/svg+xml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>vsd</extension>
+ <mime-type>application/x-visio</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- Wireless Bitmap -->
+ <extension>wbmp</extension>
+ <mime-type>image/vnd.wap.wbmp</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- WML Source -->
+ <extension>wml</extension>
+ <mime-type>text/vnd.wap.wml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- Compiled WML -->
+ <extension>wmlc</extension>
+ <mime-type>application/vnd.wap.wmlc</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- WML Script Source -->
+ <extension>wmls</extension>
+ <mime-type>text/vnd.wap.wmlscript</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <!-- Compiled WML Script -->
+ <extension>wmlscriptc</extension>
+ <mime-type>application/vnd.wap.wmlscriptc</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>wrl</extension>
+ <mime-type>x-world/x-vrml</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>Z</extension>
+ <mime-type>application/x-compress</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>z</extension>
+ <mime-type>application/x-compress</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>zip</extension>
+ <mime-type>application/zip</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>xls</extension>
+ <mime-type>application/vnd.ms-excel</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>doc</extension>
+ <mime-type>application/vnd.ms-word</mime-type>
+ </mime-mapping>
+ <mime-mapping>
+ <extension>ppt</extension>
+ <mime-type>application/vnd.ms-powerpoint</mime-type>
+ </mime-mapping>
+
+ <!-- ==================== Default Welcome File List ===================== -->
+ <!-- When a request URI refers to a directory, the default servlet looks -->
+ <!-- for a "welcome file" within that directory and, if present, -->
+ <!-- to the corresponding resource URI for display. If no welcome file -->
+ <!-- is present, the default servlet either serves a directory listing, -->
+ <!-- or returns a 404 status, depending on how it is configured. -->
+ <!-- -->
+ <!-- If you define welcome files in your own application's web.xml -->
+ <!-- deployment descriptor, that list *replaces* the list configured -->
+ <!-- here, so be sure that you include any of the default values that -->
+ <!-- you wish to include. -->
+
+ <welcome-file-list>
+ <welcome-file>index.html</welcome-file>
+ <welcome-file>index.htm</welcome-file>
+ <welcome-file>index.jsp</welcome-file>
+ </welcome-file-list>
+
</web-app>
Modified: branches/Branch_4_2/tomcat/src/resources/webserver-xmbean.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/resources/webserver-xmbean.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/resources/webserver-xmbean.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -3,21 +3,21 @@
"-//JBoss//DTD JBOSS XMBEAN 1.2//EN"
"http://www.jboss.org/j2ee/dtd/jboss_xmbean_1_2.dtd">
<!--
- Tomcat XMBean descriptor
+ Jboss Web XMBean descriptor
$Id$
-->
<mbean>
- <description>The Tomcat Deployer responsible for war deployment</description>
+ <description>The JBoss Web Deployer responsible for war deployment</description>
<descriptors>
<interceptors>
<!-- comment out DynamicInterceptor to disable dynamic addition of interceptors -->
<interceptor code="org.jboss.mx.interceptor.DynamicInterceptor"/>
</interceptors>
</descriptors>
- <class>org.jboss.web.tomcat.tc5.Tomcat5</class>
+ <class>org.jboss.web.tomcat.service.JBossWeb</class>
- <!-- Tomcat5MBean attributes -->
+ <!-- JBossWebMBean attributes -->
<attribute access="read-write" getMethod="getAuthenticators" setMethod="setAuthenticators">
<name>Authenticators</name>
@@ -162,7 +162,7 @@
&deployerAttributes;
&defaultAttributes;
- <!-- Tomcat5MBean operations -->
+ <!-- JBossWebMBean operations -->
<operation>
<description>See if a war is deployed</description>
<name>isDeployed</name>
Modified: branches/Branch_4_2/tomcat/src/webapps/ROOT.war/WEB-INF/web.xml
===================================================================
--- branches/Branch_4_2/tomcat/src/webapps/ROOT.war/WEB-INF/web.xml 2007-01-18 02:15:25 UTC (rev 59744)
+++ branches/Branch_4_2/tomcat/src/webapps/ROOT.war/WEB-INF/web.xml 2007-01-18 02:36:30 UTC (rev 59745)
@@ -11,7 +11,7 @@
</description>
<servlet>
<servlet-name>Status Servlet</servlet-name>
- <servlet-class>org.jboss.web.tomcat.tc5.StatusServlet</servlet-class>
+ <servlet-class>org.jboss.web.tomcat.service.StatusServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Status Servlet</servlet-name>
More information about the jboss-cvs-commits
mailing list