[jboss-cvs] JBossAS SVN: r61355 - in trunk/tomcat: src/main/org/jboss/web/tomcat and 8 other directories.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Thu Mar 15 22:23:54 EDT 2007
Author: remy.maucherat at jboss.com
Date: 2007-03-15 22:23:53 -0400 (Thu, 15 Mar 2007)
New Revision: 61355
Added:
trunk/tomcat/src/main/org/jboss/web/tomcat/service/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/TomcatInjectionContainer.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/WarEjbResolver.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/DeployerConfig.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployer.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InvalidateSessionPolicy.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ReplicationGranularity.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
trunk/tomcat/src/resources/jboss-web-cluster-beans.xml
trunk/tomcat/src/resources/jboss-web-cluster-service.xml
Removed:
trunk/tomcat/src/main/org/jboss/web/tomcat/tc5/
trunk/tomcat/src/main/org/jboss/web/tomcat/tc6/
trunk/tomcat/src/resources/tc5-cluster-service.xml
trunk/tomcat/src/resources/tc6-cluster-beans.xml
trunk/tomcat/src/resources/tc6-cluster-service.xml
Modified:
trunk/tomcat/build.xml
trunk/tomcat/src/resources/META-INF/jboss-aop.xml
trunk/tomcat/src/resources/server.xml
trunk/tomcat/src/resources/war-deployers-all-beans.xml
trunk/tomcat/src/resources/war-deployers-beans.xml
trunk/tomcat/src/resources/web.xml
trunk/tomcat/src/resources/webserver-xmbean.xml
Log:
- Port package renaming from JBoss 4.2.
- Switch deployment2 with deployment (the deployment2 class is now the old deployment class).
- Move the AOP marker to the new package (incompatible change).
Modified: trunk/tomcat/build.xml
===================================================================
--- trunk/tomcat/build.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/build.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -218,10 +218,10 @@
<call target="compile"/>
<mkdir dir="${build.lib}"/>
- <mkdir dir="${build.deploy}/jbossweb.deployer/META-INF"/>
+ <mkdir dir="${build.deploy}/jboss-web.deployer/META-INF"/>
- <!-- Build the jbossweb.deployer -->
- <copy todir="${build.deploy}/jbossweb.deployer/META-INF">
+ <!-- Build the jboss-web.deployer -->
+ <copy todir="${build.deploy}/jboss-web.deployer/META-INF">
<fileset dir="${build.resources}">
<include name="jboss-structure.xml" />
<include name="war-deployers-beans.xml" />
@@ -229,47 +229,47 @@
</fileset>
</copy>
<copy file="${build.resources}/context.xml"
- todir="${build.deploy}/jbossweb.deployer"/>
+ todir="${build.deploy}/jboss-web.deployer"/>
<copy file="${build.resources}/web.xml"
- tofile="${build.deploy}/jbossweb.deployer/conf/web.xml"/>
+ tofile="${build.deploy}/jboss-web.deployer/conf/web.xml"/>
<copy file="${build.resources}/server.xml"
- tofile="${build.deploy}/jbossweb.deployer/server.xml"/>
- <copy todir="${build.deploy}/jbossweb.deployer">
+ tofile="${build.deploy}/jboss-web.deployer/server.xml"/>
+ <copy todir="${build.deploy}/jboss-web.deployer">
<fileset dir="${jboss.web.lib}">
<include name="*.jar"/>
<exclude name="annotations-api.jar"/>
</fileset>
</copy>
- <copy todir="${build.deploy}/jbossweb.deployer"
+ <copy todir="${build.deploy}/jboss-web.deployer"
file="${sun.jstl.lib}/jstl.jar" />
- <mkdir dir="${build.deploy}/jbossweb.deployer/jsf-libs"/>
+ <mkdir dir="${build.deploy}/jboss-web.deployer/jsf-libs"/>
- <copy todir="${build.deploy}/jbossweb.deployer/jsf-libs">
+ <copy todir="${build.deploy}/jboss-web.deployer/jsf-libs">
<fileset dir="${sun.jsf.lib}">
<include name="*.jar" />
</fileset>
</copy>
<!-- jar for jsf integration classes -->
- <jar jarfile="${build.deploy}/jbossweb.deployer/jsf-libs/jboss-faces.jar">
+ <jar jarfile="${build.deploy}/jboss-web.deployer/jsf-libs/jboss-faces.jar">
<fileset dir="${build.classes}">
<include name="org/jboss/web/jsf/integration/**"/>
</fileset>
</jar>
- <jar jarfile="${build.deploy}/jbossweb.deployer/jbossweb-service.jar"
+ <jar jarfile="${build.deploy}/jboss-web.deployer/jboss-web-service.jar"
manifest="${build.etc}/default.mf">
<!-- The service classes -->
<fileset dir="${build.classes}">
<exclude name="org/jboss/**/*.properties"/>
- <exclude name="org/jboss/web/tomcat/tc5/session/AopMarker*"/>
- <exclude name="org/jboss/web/tomcat/tc5/session/InstanceOfAopMarker*"/>
+ <exclude name="org/jboss/web/tomcat/service/session/AopMarker*"/>
+ <exclude name="org/jboss/web/tomcat/service/session/InstanceOfAopMarker*"/>
<include name="org/jboss/net/**"/>
<include name="org/jboss/web/tomcat/filters/**"/>
<include name="org/jboss/web/tomcat/servlets/**"/>
- <include name="org/jboss/web/tomcat/tc6/**"/>
+ <include name="org/jboss/web/tomcat/service/**"/>
<include name="org/jboss/web/tomcat/security/**"/>
<include name="org/jboss/web/tomcat/statistics/**"/>
</fileset>
@@ -281,10 +281,10 @@
<!-- jar for fine grained aop -->
<!-- package in .aop. This is used for fine-grained replication -->
- <jar destfile="${build.lib}/tc6-cluster.aop">
+ <jar destfile="${build.lib}/jboss-web-cluster.aop">
<fileset dir="${build.classes}">
- <include name="org/jboss/web/tomcat/tc5/session/AopMarker.class"/>
- <include name="org/jboss/web/tomcat/tc5/session/InstanceOfAopMarker.class"/>
+ <include name="org/jboss/web/tomcat/service/session/AopMarker.class"/>
+ <include name="org/jboss/web/tomcat/service/session/InstanceOfAopMarker.class"/>
</fileset>
<fileset dir="${build.resources}">
<include name="META-INF/jboss-aop.xml"/>
@@ -292,18 +292,18 @@
</jar>
<!-- jboss-web-metamodel.jar -->
- <jar jarfile="${build.deploy}/jbossweb.deployer/jboss-web-metamodel.jar">
+ <jar jarfile="${build.deploy}/jboss-web.deployer/jboss-web-metamodel.jar">
<fileset dir="${build.classes}">
<include name="org/jboss/web/metamodel/**"/>
</fileset>
</jar>
<!-- Cluster service sar and aop descriptor used for fine-grained replication -->
- <mkdir dir="${build.deploy}/tc6-cluster"/>
- <copy todir="${build.deploy}/tc6-cluster"
- file="${build.lib}/tc6-cluster.aop"/>
- <copy tofile="${build.deploy}/tc6-cluster/tc6-cluster-beans.xml"
- file="${build.resources}/tc6-cluster-beans.xml"/>
+ <mkdir dir="${build.deploy}/jboss-web-cluster"/>
+ <copy todir="${build.deploy}/jboss-web-cluster"
+ file="${build.lib}/jboss-web-cluster.aop"/>
+ <copy tofile="${build.deploy}/jboss-web-cluster/jboss-web-cluster-beans.xml"
+ file="${build.resources}/jboss-web-cluster-beans.xml"/>
<!-- Update the build marker to allow bypassing -->
<touch file="${build-bypass.marker}"/>
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,352 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service;
+
+import javax.management.ObjectName;
+
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+
+/**
+ * The tomcat war deployer configuration passed in from the web container.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 56125 $
+ */
+public class DeployerConfig
+{
+ /**
+ * The tomcat sar class loader
+ */
+ private ClassLoader serviceClassLoader;
+ /**
+ * The domain used for the tomcat mbeans
+ */
+ private String catalinaDomain = "Catalina";
+
+ /**
+ * The fully qualified name of the class that will be used for session
+ * management if <tt>distributable</tt> is set to true.
+ */
+ private String managerClass = "org.jboss.web.tomcat.service.session.JBossManager";
+
+ /**
+ * JMX Object name of the TreeCache MBean. Used by JBossCacheManager
+ */
+ private String cacheName = "jboss.cache:service=TreeCache";
+
+ /**
+ * The web context class to create
+ */
+ private String contextClassName;
+ /**
+ * The parent class loader first model flag
+ */
+ private boolean java2ClassLoadingCompliance = false;
+ /**
+ * A flag indicating if war archives should be unpacked
+ */
+ private boolean unpackWars = true;
+ /**
+ * If true, ejb-links that don't resolve don't cause an error
+ * (fallback to jndi-name)
+ */
+ private boolean lenientEjbLink = false;
+ /**
+ * The tomcat service JMX object name
+ */
+ private ObjectName serviceName;
+ /**
+ * The catalina debug level
+ */
+ private int debugLevel;
+ /**
+ * A flag indicating if the JBoss UCL should be used
+ */
+ private boolean useJBossWebLoader = true;
+ /**
+ * A flag indicating if the working dir for a war deployment should be
+ * delete when the war is undeployed.
+ */
+ private boolean deleteWorkDirs = true;
+ /**
+ * Which snapshot mode should be used in clustered environment?
+ * Default: instant
+ */
+ private String snapshotMode = "instant"; // instant or interval
+ /**
+ * With IntervalSnapshotManager use this interval (in ms) for snapshotting
+ */
+ private int snapshotInterval = 1000;
+
+ /**
+ * Should the clustering code use caching or not?
+ */
+ private boolean useLocalCache;
+
+ /**
+ * Whether to use MOD_JK(2) for sticky session combined with JvmRoute. If set to true,
+ * it will insert a JvmRouteFilter to intercept every request and replace the JvmRoute
+ * if it detects a failover.
+ */
+ private boolean useJK = false;
+
+ /**
+ * Get the request attribute name under which the JAAS Subject is store
+ */
+ private String subjectAttributeName = null;
+ /**
+ * The default security-domain name to use
+ */
+ private String defaultSecurityDomain;
+ /** Package names that should be ignored for class loading */
+ private String[] filteredPackages;
+
+ /**
+ * Flag indicating whether web-app specific context xmls may set the privileged flag.
+ */
+ private boolean allowSelfPrivilegedWebApps = false;
+ /** The service used to flush authentication cache on session invalidation. */
+ private JaasSecurityManagerServiceMBean secMgrService;
+
+ public ClassLoader getServiceClassLoader()
+ {
+ return serviceClassLoader;
+ }
+
+ public void setServiceClassLoader(ClassLoader serviceClassLoader)
+ {
+ this.serviceClassLoader = serviceClassLoader;
+ }
+
+ public String getManagerClass()
+ {
+ return managerClass;
+ }
+
+ public void setManagerClass(String managerClass)
+ {
+ this.managerClass = managerClass;
+ }
+
+ public String getCacheName()
+ {
+ return cacheName;
+ }
+
+ public void setCacheName(String cacheName)
+ {
+ this.cacheName = cacheName;
+ }
+
+ public String getCatalinaDomain()
+ {
+ return catalinaDomain;
+ }
+
+ public void setCatalinaDomain(String catalinaDomain)
+ {
+ this.catalinaDomain = catalinaDomain;
+ }
+
+ public String getContextClassName()
+ {
+ return contextClassName;
+ }
+
+ public void setContextClassName(String contextClassName)
+ {
+ this.contextClassName = contextClassName;
+ }
+
+ public boolean isJava2ClassLoadingCompliance()
+ {
+ return java2ClassLoadingCompliance;
+ }
+
+ public void setJava2ClassLoadingCompliance(boolean java2ClassLoadingCompliance)
+ {
+ this.java2ClassLoadingCompliance = java2ClassLoadingCompliance;
+ }
+
+ public boolean isUnpackWars()
+ {
+ return unpackWars;
+ }
+
+ public void setUnpackWars(boolean unpackWars)
+ {
+ this.unpackWars = unpackWars;
+ }
+
+ public boolean isLenientEjbLink()
+ {
+ return lenientEjbLink;
+ }
+
+ public void setLenientEjbLink(boolean lenientEjbLink)
+ {
+ this.lenientEjbLink = lenientEjbLink;
+ }
+
+ public ObjectName getServiceName()
+ {
+ return serviceName;
+ }
+
+ public void setServiceName(ObjectName serviceName)
+ {
+ this.serviceName = serviceName;
+ }
+
+ public int getDebugLevel()
+ {
+ return debugLevel;
+ }
+
+ public void setDebugLevel(int debugLevel)
+ {
+ this.debugLevel = debugLevel;
+ }
+
+ public boolean isUseJBossWebLoader()
+ {
+ return useJBossWebLoader;
+ }
+
+ public void setUseJBossWebLoader(boolean useJBossWebLoader)
+ {
+ this.useJBossWebLoader = useJBossWebLoader;
+ }
+
+ public boolean isDeleteWorkDirs()
+ {
+ return deleteWorkDirs;
+ }
+
+ public void setDeleteWorkDirs(boolean deleteWorkDirs)
+ {
+ this.deleteWorkDirs = deleteWorkDirs;
+ }
+
+ public String getSnapshotMode()
+ {
+ return snapshotMode;
+ }
+
+ public void setSnapshotMode(String snapshotMode)
+ {
+ this.snapshotMode = snapshotMode;
+ }
+
+ public int getSnapshotInterval()
+ {
+ return snapshotInterval;
+ }
+
+ public void setSnapshotInterval(int snapshotInterval)
+ {
+ this.snapshotInterval = snapshotInterval;
+ }
+
+ public boolean isUseLocalCache()
+ {
+ return useLocalCache;
+ }
+
+ public void setUseLocalCache(boolean useLocalCache)
+ {
+ this.useLocalCache = useLocalCache;
+ }
+
+ public boolean isUseJK()
+ {
+ return useJK;
+ }
+
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK = useJK;
+ }
+
+ public String getSubjectAttributeName()
+ {
+ return subjectAttributeName;
+ }
+
+ public void setSubjectAttributeName(String subjectAttributeName)
+ {
+ this.subjectAttributeName = subjectAttributeName;
+ }
+
+ /**
+ * Get the default security domain implementation to use if a war
+ * does not declare a security-domain.
+ *
+ * @return jndi name of the security domain binding to use.
+ * @jmx:managed-attribute
+ */
+ public String getDefaultSecurityDomain()
+ {
+ return defaultSecurityDomain;
+ }
+
+ /**
+ * Set the default security domain implementation to use if a war
+ * does not declare a security-domain.
+ *
+ * @param defaultSecurityDomain - jndi name of the security domain binding
+ * to use.
+ * @jmx:managed-attribute
+ */
+ public void setDefaultSecurityDomain(String defaultSecurityDomain)
+ {
+ this.defaultSecurityDomain = defaultSecurityDomain;
+ }
+
+ public boolean isAllowSelfPrivilegedWebApps()
+ {
+ return allowSelfPrivilegedWebApps;
+ }
+
+ public void setAllowSelfPrivilegedWebApps(boolean allowSelfPrivilegedWebApps)
+ {
+ this.allowSelfPrivilegedWebApps = allowSelfPrivilegedWebApps;
+ }
+
+ public JaasSecurityManagerServiceMBean getSecurityManagerService()
+ {
+ return secMgrService;
+ }
+ public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr)
+ {
+ this.secMgrService = mgr;
+ }
+
+ public String[] getFilteredPackages()
+ {
+ return filteredPackages;
+ }
+ public void setFilteredPackages(String[] filteredPackages)
+ {
+ this.filteredPackages = filteredPackages;
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/DeployerConfig.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,204 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Session;
+import org.jboss.logging.Logger;
+import org.jboss.util.NestedRuntimeException;
+import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+
+/**
+ * Web request filter to specifically handle Tomcat jvmRoute using mod_jk(2)
+ * module. We assume that the session is set by cookie only for now, i.e., no
+ * support of that from URL. Furthermore, the session id has a format of
+ * id.jvmRoute where jvmRoute is used by JK module to determine sticky session
+ * during load balancing.
+ *
+ * @author Ben Wang
+ * @author Marco Antonioni
+ * @version $Revision: 45726 $
+ * @deprecated 4.0.3
+ */
+public class JvmRouteFilter
+ implements Filter
+{
+ protected AbstractJBossManager manager_;
+ protected static Logger log_ = Logger.getLogger(JvmRouteFilter.class);
+
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ if (log_.isDebugEnabled())
+ {
+ ServletContext sc = filterConfig.getServletContext();
+ Enumeration names = sc.getAttributeNames();
+ while (names.hasMoreElements())
+ {
+ String name = (String) names.nextElement();
+ Object value = sc.getAttribute(name);
+ log_.debug("name=" + name + ", value.className: [" + value.getClass().getName() + "] value.toString: [" + value.toString() + "]");
+ }
+ }
+ manager_ = (AbstractJBossManager) filterConfig.getServletContext().getAttribute("AbstractJBossManager");
+ if (manager_ == null)
+ {
+ throw new RuntimeException("JvmRouteFilter.init(): No AbstractJBossManager found for clustering support.");
+ }
+
+ if (log_.isDebugEnabled())
+ log_.debug("JvmRouteFilter.init(): initializing JvmRouteFilter");
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ // Check if request and response is valid
+ if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse))
+ {
+ // Don't know how to handle. There is a mistake.
+ throw new RuntimeException("JvmRouteFilter.doFilter(): Not a http request and response type.");
+ }
+
+ // get session id
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse res = (HttpServletResponse) response;
+ HttpSession session = req.getSession(false);
+ if (session != null)
+ {
+ String sessionId = session.getId();
+
+ // Obtain JvmRoute
+ String jvmRoute = manager_.getJvmRoute();
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("doFilter(): check if need to re-route based on JvmRoute. Session id: " +
+ sessionId + " jvmRoute: " + jvmRoute);
+ }
+
+ if (jvmRoute == null)
+ {
+ throw new RuntimeException("JvmRouteFilter.doFilter(): Tomcat JvmRoute is null. " +
+ "Need to assign a value in Tomcat server.xml for load balancing.");
+ }
+
+ // Check if incoming session id has JvmRoute appended. If not, append it.
+ // TODO. We handle only get session id by cookie only
+ if (req.isRequestedSessionIdFromURL())
+ {
+ // Warning but do nothing
+ log_.error("JvmRouteFilter.doFilter(): Can't handle clustering where session id is from URL. Will skip.");
+ }
+ else
+ {
+ handleJvmRoute(sessionId, jvmRoute, res);
+ }
+ }
+ chain.doFilter(request, response);
+ }
+
+ protected void handleJvmRoute(String sessionId, String jvmRoute, HttpServletResponse response)
+ {
+ // get requested jvmRoute.
+ // TODO. The current format is assumed to be id.jvmRoute. Can be generalized later.
+ String requestedJvmRoute = null;
+ int ind = sessionId.indexOf(".");
+ if (ind > 0)
+ {
+ requestedJvmRoute = sessionId.substring(sessionId.indexOf(".") + 1, sessionId.length());
+ }
+
+ String sid = sessionId;
+ if (requestedJvmRoute == null)
+ {
+ // If this filter is turned on, we assume we have an appendix of jvmRoute. So this request is new.
+ sid = sessionId + "." + jvmRoute;
+ manager_.setNewSessionCookie(sid, response);
+ }
+ else if (requestedJvmRoute.equals(jvmRoute))
+ {
+ return; // Nothing more needs to be done.
+ }
+ else
+ {
+ // We just have a failover since jvmRoute does not match. We will replace the old one with the new one.
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): We have detected a failover with differen jvmRoute." +
+ " old one: " + requestedJvmRoute + " new one: " + jvmRoute + ". Will reset the session id.");
+ }
+ int index = sessionId.indexOf(".");
+ if (index > 0)
+ {
+ String base = sessionId.substring(0, sessionId.indexOf("."));
+ sid = base + "." + jvmRoute;
+ }
+ else
+ {
+ throw new RuntimeException("JvmRouteFilter.handleJvmRoute(): session id doesn't JvmRoute.");
+ }
+
+ manager_.setNewSessionCookie(sid, response);
+ // Change the sessionId with the new one using local jvmRoute
+ Session catalinaSession = null;
+ try
+ {
+ catalinaSession = manager_.findSession(sessionId);
+ // change session id with the new one using local jvmRoute and update cluster if needed.
+ if( catalinaSession != null )
+ {
+ catalinaSession.setId(sid);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): changed catalina session to= [" + sid + "] old one= [" + sessionId + "]");
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): manager_.findSession() unable to find session= [" + sessionId + "]", e);
+ }
+ throw new NestedRuntimeException("JvmRouteFilter.handleJvmRoute(): cannot find session [" + sessionId + "]", e);
+ }
+ }
+ }
+
+
+ public void destroy()
+ {
+ manager_ = null;
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/JvmRouteFilter.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,749 @@
+/*
+ * Copyright 1999-2002,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JBoss, the OpenSource WebOS
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.Constants;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.logging.Logger;
+
+
+/**
+ * Standard implementation of the <code>Service</code> interface. The
+ * associated Container is generally an instance of Engine, but this is
+ * not required.
+ * <p/>
+ * This version does not start the connectors in the start method so that
+ * all web apps may be completely deployed before enabling the transport
+ * layer.
+ *
+ * @author Craig R. McClanahan
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 56603 $
+ */
+public class StandardService
+ implements Lifecycle, Service, MBeanRegistration
+{
+ private static Logger log = Logger.getLogger(StandardService.class);
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The set of Connectors associated with this Service.
+ */
+ private Connector connectors[] = new Connector[0];
+
+
+ /**
+ * The Container associated with this Service.
+ */
+ private Container container = null;
+
+
+ /**
+ * The debugging detail level for this component.
+ */
+ private int debug = 0;
+
+
+ /**
+ * Descriptive information about this component implementation.
+ */
+ private static final String info =
+ "org.jboss.web.tomcat.service.StandardService/1.0";
+
+
+ /**
+ * Has this component been initialized?
+ */
+ private boolean initialized = false;
+
+
+ /**
+ * The name of this service.
+ */
+ private String name = null;
+
+
+ /**
+ * The lifecycle event support for this component.
+ */
+ private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+ /**
+ * The string manager for this package.
+ */
+ private static final StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+ /**
+ * The <code>Server</code> that owns this Service, if any.
+ */
+ private Server server = null;
+
+ /**
+ * Has this component been started?
+ */
+ private boolean started = false;
+
+
+ /**
+ * The property change support for this component.
+ */
+ protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * Return the <code>Container</code> that handles requests for all
+ * <code>Connectors</code> associated with this Service.
+ */
+ public Container getContainer()
+ {
+
+ return (this.container);
+
+ }
+
+
+ /**
+ * Set the <code>Container</code> that handles requests for all
+ * <code>Connectors</code> associated with this Service.
+ *
+ * @param container The new Container
+ */
+ public void setContainer(Container container)
+ {
+
+ Container oldContainer = this.container;
+ if ((oldContainer != null) && (oldContainer instanceof Engine))
+ ((Engine) oldContainer).setService(null);
+ this.container = container;
+ if ((this.container != null) && (this.container instanceof Engine))
+ ((Engine) this.container).setService(this);
+ if (started && (this.container != null) &&
+ (this.container instanceof Lifecycle))
+ {
+ try
+ {
+ ((Lifecycle) this.container).start();
+ }
+ catch (LifecycleException e)
+ {
+ ;
+ }
+ }
+ synchronized (connectors)
+ {
+ for (int i = 0; i < connectors.length; i++)
+ connectors[i].setContainer(this.container);
+ }
+ if (started && (oldContainer != null) &&
+ (oldContainer instanceof Lifecycle))
+ {
+ try
+ {
+ ((Lifecycle) oldContainer).stop();
+ }
+ catch (LifecycleException e)
+ {
+ ;
+ }
+ }
+
+ // Report this property change to interested listeners
+ support.firePropertyChange("container", oldContainer, this.container);
+
+ }
+
+ public ObjectName getContainerName()
+ {
+ if (container instanceof ContainerBase)
+ {
+ return ((ContainerBase) container).getJmxName();
+ }
+ return null;
+ }
+
+
+ /**
+ * Return the debugging detail level of this component.
+ */
+ public int getDebug()
+ {
+
+ return (this.debug);
+
+ }
+
+
+ /**
+ * Set the debugging detail level of this component.
+ *
+ * @param debug The new debugging detail level
+ */
+ public void setDebug(int debug)
+ {
+
+ this.debug = debug;
+
+ }
+
+
+ /**
+ * Return descriptive information about this Service implementation and
+ * the corresponding version number, in the format
+ * <code><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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/StandardService.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,388 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tomcat.util.modeler.Registry;
+
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.manager.Constants;
+import org.apache.catalina.manager.StatusTransformer;
+
+/**
+ * This servlet will display a complete status of the HTTP/1.1 connector.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 56603 $ $Date: 2006-09-06 15:57:20 +0200 (mer., 06 sept. 2006) $
+ */
+
+public class StatusServlet
+ extends HttpServlet implements NotificationListener
+{
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The debugging detail level for this servlet.
+ */
+ private int debug = 0;
+
+
+ /**
+ * MBean server.
+ */
+ protected MBeanServer mBeanServer = null;
+
+
+ /**
+ * Vector of protocol handlers object names.
+ */
+ protected Vector protocolHandlers = new Vector();
+
+
+ /**
+ * Vector of thread pools object names.
+ */
+ protected Vector threadPools = new Vector();
+
+
+ /**
+ * Vector of request processors object names.
+ */
+ protected Vector requestProcessors = new Vector();
+
+
+ /**
+ * Vector of global request processors object names.
+ */
+ protected Vector globalRequestProcessors = new Vector();
+
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Initialize this servlet.
+ */
+ public void init() throws ServletException
+ {
+
+ // Retrieve the MBean server
+ mBeanServer = Registry.getServer();
+
+ // Set our properties from the initialization parameters
+ String value = null;
+ try
+ {
+ value = getServletConfig().getInitParameter("debug");
+ debug = Integer.parseInt(value);
+ }
+ catch (Throwable t)
+ {
+ ;
+ }
+
+ try
+ {
+
+ // Query protocol handlers
+ String onStr = "*:type=ProtocolHandler,*";
+ ObjectName objectName = new ObjectName(onStr);
+ Set set = mBeanServer.queryMBeans(objectName, null);
+ Iterator iterator = set.iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ protocolHandlers.addElement(oi.getObjectName());
+ }
+
+ // Query Thread Pools
+ onStr = "*:type=ThreadPool,*";
+ objectName = new ObjectName(onStr);
+ set = mBeanServer.queryMBeans(objectName, null);
+ iterator = set.iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ threadPools.addElement(oi.getObjectName());
+ }
+
+ // Query Global Request Processors
+ onStr = "*:type=GlobalRequestProcessor,*";
+ objectName = new ObjectName(onStr);
+ set = mBeanServer.queryMBeans(objectName, null);
+ iterator = set.iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ globalRequestProcessors.addElement(oi.getObjectName());
+ }
+
+ // Query Request Processors
+ onStr = "*:type=RequestProcessor,*";
+ objectName = new ObjectName(onStr);
+ set = mBeanServer.queryMBeans(objectName, null);
+ iterator = set.iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ requestProcessors.addElement(oi.getObjectName());
+ }
+
+ // Register with MBean server
+ onStr = "JMImplementation:type=MBeanServerDelegate";
+ objectName = new ObjectName(onStr);
+ mBeanServer.addNotificationListener(objectName, this, null, null);
+
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ }
+
+
+ /**
+ * Finalize this servlet.
+ */
+ public void destroy()
+ {
+
+ ; // No actions necessary
+
+ }
+
+
+ /**
+ * Process a GET request for the specified resource.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ * @throws IOException if an input/output error occurs
+ * @throws ServletException if a servlet-specified error occurs
+ */
+ public void doGet(HttpServletRequest request,
+ HttpServletResponse response)
+ throws IOException, ServletException
+ {
+
+ // mode is flag for HTML or XML output
+ int mode = 0;
+ // if ?XML=true, set the mode to XML
+ if ("true".equals(request.getParameter("XML")))
+ {
+ mode = 1;
+ }
+ StatusTransformer.setContentType(response, mode);
+
+ PrintWriter writer = response.getWriter();
+
+ boolean completeStatus = false;
+ if ("true".equals(request.getParameter("full")))
+ {
+ completeStatus = true;
+ }
+
+ // use StatusTransformer to output status
+ if (mode == 0)
+ {
+ // HTML Header Section
+ writer.print(HTML_HEADER);
+ }
+ else if (mode == 1)
+ {
+ writer.write(Constants.XML_DECLARATION);
+ writer.write
+ (Constants.XML_STYLE);
+ writer.write("<status>");
+ }
+
+ try
+ {
+
+ // Display virtual machine statistics
+ StatusTransformer.writeVMState(writer, mode);
+
+ Enumeration enumeration = threadPools.elements();
+ while (enumeration.hasMoreElements())
+ {
+ ObjectName objectName = (ObjectName) enumeration.nextElement();
+ String name = objectName.getKeyProperty("name");
+ // use StatusTransformer to output status
+ StatusTransformer.writeConnectorState
+ (writer, objectName,
+ name, mBeanServer, globalRequestProcessors,
+ requestProcessors, mode);
+ }
+
+ if (completeStatus)
+ {
+ // Note: Retrieving the full status is much slower
+ // use StatusTransformer to output status
+ StatusTransformer.writeDetailedState
+ (writer, mBeanServer, mode);
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new ServletException(e);
+ }
+
+ if (mode == 0)
+ {
+ writer.print(HTML_FOOTER);
+ }
+ else if (mode == 1)
+ {
+ writer.write("</status>");
+ }
+
+ }
+
+ // ------------------------------------------- NotificationListener Methods
+
+
+ public void handleNotification(Notification notification,
+ java.lang.Object handback)
+ {
+
+ if (notification instanceof MBeanServerNotification)
+ {
+ ObjectName objectName =
+ ((MBeanServerNotification) notification).getMBeanName();
+ if (notification.getType().equals
+ (MBeanServerNotification.REGISTRATION_NOTIFICATION))
+ {
+ String type = objectName.getKeyProperty("type");
+ if (type != null)
+ {
+ if (type.equals("ProtocolHandler"))
+ {
+ protocolHandlers.addElement(objectName);
+ }
+ else if (type.equals("ThreadPool"))
+ {
+ threadPools.addElement(objectName);
+ }
+ else if (type.equals("GlobalRequestProcessor"))
+ {
+ globalRequestProcessors.addElement(objectName);
+ }
+ else if (type.equals("RequestProcessor"))
+ {
+ requestProcessors.addElement(objectName);
+ }
+ }
+ }
+ else if (notification.getType().equals
+ (MBeanServerNotification.UNREGISTRATION_NOTIFICATION))
+ {
+ String type = objectName.getKeyProperty("type");
+ if (type != null)
+ {
+ if (type.equals("ProtocolHandler"))
+ {
+ protocolHandlers.removeElement(objectName);
+ }
+ else if (type.equals("ThreadPool"))
+ {
+ threadPools.removeElement(objectName);
+ }
+ else if (type.equals("GlobalRequestProcessor"))
+ {
+ globalRequestProcessors.removeElement(objectName);
+ }
+ else if (type.equals("RequestProcessor"))
+ {
+ requestProcessors.removeElement(objectName);
+ }
+ }
+ String j2eeType = objectName.getKeyProperty("j2eeType");
+ if (j2eeType != null)
+ {
+
+ }
+ }
+ }
+
+ }
+
+
+ // ------------------------------------------------------- Private Constats
+
+
+ private static final String HTML_HEADER =
+ "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
+ + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+ + "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ + "<head>"
+ + "<title>Tomcat Status</title>"
+ + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />"
+ + "<link rel=\"StyleSheet\" href=\"jboss.css\" type=\"text/css\"/>"
+ + "</head>"
+ + "<body>"
+ + "<!-- header begin -->"
+ + "<a href=\"http://www.jboss.org\">"
+ + "<img src=\"logo.gif\" alt=\"JBoss\" id=\"logo\" width=\"226\" height=\"105\" />"
+ + "</a>"
+ + "<div id=\"header\"> </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: trunk/tomcat/src/main/org/jboss/web/tomcat/service/StatusServlet.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/TomcatInjectionContainer.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/TomcatInjectionContainer.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/TomcatInjectionContainer.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,470 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.AnnotationProcessor;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.ejb3.Container;
+import org.jboss.ejb3.DependencyPolicy;
+import org.jboss.ejb3.DeploymentScope;
+import org.jboss.ejb3.JmxDependencyPolicy;
+import org.jboss.ejb3.enc.DeploymentPersistenceUnitResolver;
+import org.jboss.ejb3.entity.PersistenceUnitDeployment;
+import org.jboss.injection.DependsHandler;
+import org.jboss.injection.EJBHandler;
+import org.jboss.injection.EncInjector;
+import org.jboss.injection.InjectionContainer;
+import org.jboss.injection.InjectionHandler;
+import org.jboss.injection.InjectionUtil;
+import org.jboss.injection.Injector;
+import org.jboss.injection.JndiInjectHandler;
+import org.jboss.injection.PersistenceContextHandler;
+import org.jboss.injection.PersistenceUnitHandler;
+import org.jboss.injection.ResourceHandler;
+import org.jboss.injection.WebServiceRefHandler;
+import org.jboss.logging.Logger;
+import org.jboss.metamodel.descriptor.EnvironmentRefGroup;
+import org.jboss.metamodel.descriptor.Listener;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.WebApplication;
+import org.jboss.web.metamodel.descriptor.Filter;
+import org.jboss.web.metamodel.descriptor.JBossWebDDObjectFactory;
+import org.jboss.web.metamodel.descriptor.Servlet;
+import org.jboss.web.metamodel.descriptor.WebDD;
+import org.jboss.web.metamodel.descriptor.WebDDObjectFactory;
+import org.jboss.xb.binding.JBossXBException;
+
+/**
+ * Comment
+ *
+ * @author <a href="mailto:bill at jboss.org">Bill Burke</a>
+ * @version $Revision: 1.17 $
+ */
+public class TomcatInjectionContainer implements InjectionContainer, AnnotationProcessor
+{
+ private static final Logger log = Logger.getLogger(TomcatInjectionContainer.class);
+
+ private static class EncMap extends HashMap<String, EncInjector>
+ {
+ private HashMap<String, EncInjector> added;
+
+ public void recordAdded()
+ {
+ added = new HashMap<String, EncInjector>();
+ }
+
+ public void clearAdded()
+ {
+ added = null;
+ }
+
+ public Map<String, EncInjector> getAdded()
+ {
+ return added;
+ }
+
+ @Override
+ public EncInjector put(String key, EncInjector value)
+ {
+ if (added != null)
+ added.put(key, value);
+ return super.put(key, value);
+ }
+
+ @Override
+ public void putAll(Map<? extends String, ? extends EncInjector> m)
+ {
+ if (added != null)
+ added.putAll(m);
+ super.putAll(m);
+ }
+ }
+
+ protected EncMap encInjectors = new EncMap();
+ protected Map<String, Map<AccessibleObject, Injector>> encInjections = new HashMap<String, Map<AccessibleObject, Injector>>();
+ protected Map<String, Map<AccessibleObject, Injector>> resolvedClassInjections = new HashMap<String, Map<AccessibleObject, Injector>>();
+
+ protected List<PersistenceUnitDeployment> persistenceUnitDeployments = new ArrayList<PersistenceUnitDeployment>();
+ protected LinkedHashMap ejbContainers = new LinkedHashMap(); // will always be empty.
+ protected DeploymentPersistenceUnitResolver persistenceUnitResolver;
+ protected WarEjbResolver ejbResolver;
+ protected DependencyPolicy dependencyPolicy = new JmxDependencyPolicy();
+ protected Collection<InjectionHandler> handlers;
+ protected DeploymentUnit unit;
+ protected ClassLoader webLoader;
+ protected WebApplication appInfo;
+ protected WebDD webDD;
+
+ public TomcatInjectionContainer(WebApplication appInfo, DeploymentUnit unit)
+ {
+ this.unit = unit;
+ DeploymentScope deploymentScope = null;
+ this.appInfo = appInfo;
+
+ persistenceUnitResolver = new DeploymentPersistenceUnitResolver(persistenceUnitDeployments, deploymentScope, ejbContainers);
+ ejbResolver = new WarEjbResolver(deploymentScope, unit.getSimpleName());
+ }
+
+ public EnvironmentRefGroup getEnvironmentRefGroup()
+ {
+ return webDD;
+ }
+
+ public void postConstruct(Object object) throws IllegalAccessException, InvocationTargetException
+ {
+ // ignore for now
+ }
+
+ public void preDestroy(Object object) throws IllegalAccessException, InvocationTargetException
+ {
+ // ignore for now
+ }
+
+ /**
+ * When we get here, we are assuming that any XML defined ENC has been set up. We will set up more here
+ * if the class being processed is a JSP
+ *
+ *
+ * @param object
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ * @throws NamingException
+ */
+ public void processAnnotations(Object object) throws IllegalAccessException, InvocationTargetException, NamingException
+ {
+ log.debug("**************** Processing annotations for: " + object.getClass().getName());
+ Map<AccessibleObject, Injector> injectors = resolvedClassInjections.get(object.getClass().getName());
+ if (injectors == null)
+ {
+ log.debug("-- there was no prior annotation preprocessing done");
+ encInjectors.recordAdded();
+ // let's assume this is a JSP or some other artifact that cannot be found within XML
+ injectors = InjectionUtil.processAnnotations(this, handlers, object.getClass());
+ resolvedClassInjections.put(object.getClass().getName(), injectors);
+
+ // only execute injectors that were added additionally
+ if (encInjectors.getAdded().size() > 0)
+ {
+ for (EncInjector encInjector : encInjectors.getAdded().values())
+ {
+ encInjector.inject(this);
+ }
+ encInjectors.clearAdded();
+ }
+ }
+ if (injectors == null || injectors.size() == 0)
+ {
+ log.debug("-- no injectors found: " + injectors);
+ return;
+ }
+
+ log.debug("-- doing injections");
+ for (Injector injector : injectors.values())
+ {
+ injector.inject(object);
+ }
+ }
+
+ public void populateEnc(ClassLoader loader)
+ {
+ for (EncInjector injector : encInjectors.values())
+ {
+ injector.inject(this);
+ }
+ }
+
+ private InputStream[] getInputStreams()
+ {
+ InputStream jbossWebIS = null;
+ InputStream webIS = null;
+
+ try
+ {
+ VirtualFile webDD = unit.getMetaDataFile("web.xml");
+ if (webDD != null)
+ webIS = webDD.openStream();
+ }
+ catch (IOException e)
+ {
+ log.debug("Failed to find web.xml");
+ }
+ try
+ {
+ VirtualFile webDD = unit.getMetaDataFile("jboss-web.xml");
+ if (webDD != null)
+ jbossWebIS = webDD.openStream();
+ }
+ catch (IOException e)
+ {
+ log.debug("Failed to find jboss-web.xml");
+ }
+
+ InputStream[] streams = { webIS, jbossWebIS };
+ return streams;
+ }
+
+ /**
+ * introspects EJB container to find all dependencies
+ * and initialize any extra metadata.
+ * <p/>
+ * This must be called before container is registered with any microcontainer
+ *
+ * @param dependencyPolicy
+ */
+ public void processMetadata()
+ {
+ InputStream[] streams = getInputStreams();
+ if (streams[0] != null)
+ {
+ try
+ {
+ webDD = WebDDObjectFactory.parse(streams[0]);
+ if (streams[1] != null)
+ {
+ webDD = JBossWebDDObjectFactory.parse(webDD, streams[1]);
+ }
+ }
+ catch (JBossXBException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ else
+ {
+ throw new RuntimeException("web.xml is required");
+ }
+
+ // XML must be done first so that any annotation overrides are initialized
+
+ // todo injection handlers should be pluggable from XML
+ handlers = new ArrayList<InjectionHandler>();
+ handlers.add(new EJBHandler());
+ handlers.add(new DependsHandler());
+ handlers.add(new JndiInjectHandler());
+ handlers.add(new PersistenceContextHandler());
+ handlers.add(new PersistenceUnitHandler());
+ handlers.add(new ResourceHandler());
+ handlers.add(new WebServiceRefHandler());
+
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
+ ClassLoader webLoader = getClassloader();
+ Thread.currentThread().setContextClassLoader(webLoader);
+ try
+ {
+ for (InjectionHandler handler : handlers)
+ handler.loadXml(webDD, this);
+
+ for (Object obj : webDD.getServlets())
+ {
+ Servlet servlet = (Servlet)obj;
+ try
+ {
+ if (servlet.getServletClass() == null)
+ continue; // jsp
+ if (resolvedClassInjections.containsKey(servlet.getServletClass()))
+ continue;
+ Class servletClass = webLoader.loadClass(servlet.getServletClass());
+ Map<AccessibleObject, Injector> tmp = InjectionUtil.processAnnotations(this, handlers, servletClass);
+ resolvedClassInjections.put(servlet.getServletClass(), tmp);
+ }
+ catch (ClassNotFoundException e)
+ {
+ log.warn("could not find servlet class " + servlet.getServletClass() + " in classpath when processing annotations.");
+ }
+
+ }
+ for (Object obj : webDD.getFilters())
+ {
+ Filter filter = (Filter)obj;
+ try
+ {
+ if (resolvedClassInjections.containsKey(filter.getFilterClass()))
+ continue;
+ Class servletClass = webLoader.loadClass(filter.getFilterClass());
+ Map<AccessibleObject, Injector> tmp = InjectionUtil.processAnnotations(this, handlers, servletClass);
+ resolvedClassInjections.put(filter.getFilterClass(), tmp);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new RuntimeException("could not find filter class in classpath", e);
+ }
+ }
+ for (Object obj : webDD.getListeners())
+ {
+ Listener listener = (Listener)obj;
+ try
+ {
+ if (resolvedClassInjections.containsKey(listener.getListenerClass()))
+ continue;
+ Class servletClass = webLoader.loadClass(listener.getListenerClass());
+ Map<AccessibleObject, Injector> tmp = InjectionUtil.processAnnotations(this, handlers, servletClass);
+ resolvedClassInjections.put(listener.getListenerClass(), tmp);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new RuntimeException("could not find listener class in classpath", e);
+ }
+ }
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(old);
+ }
+ }
+
+ public Map<String, EncInjector> getEncInjectors()
+ {
+ return encInjectors;
+ }
+
+ public Map<String, Map<AccessibleObject, Injector>> getEncInjections()
+ {
+ return encInjections;
+ }
+
+ // EncInjectors/Handlers may need to add extra instance injectors
+ public List<Injector> getInjectors()
+ {
+ return new ArrayList<Injector>(); // no equivalent in WAR
+ }
+
+ public VirtualFile getRootFile()
+ {
+ return unit.getDeploymentContext().getRoot();
+ }
+
+ public String getIdentifier()
+ {
+ return unit.getSimpleName();
+ }
+
+ public String getDeploymentDescriptorType()
+ {
+ return "web.xml";
+ }
+
+ public ClassLoader getClassloader()
+ {
+ return webLoader;
+ }
+
+ public void setClassLoader(ClassLoader loader)
+ {
+ this.webLoader = loader;
+ }
+
+ public Context getEnc()
+ {
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ Thread.currentThread().setContextClassLoader(getClassloader());
+ try
+ {
+ return (Context)new InitialContext().lookup("java:comp");
+ }
+ catch (NamingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(old);
+ }
+ }
+
+ public PersistenceUnitDeployment getPersistenceUnitDeployment(String unitName) throws NameNotFoundException
+ {
+ return persistenceUnitResolver.getPersistenceUnitDeployment(unitName);
+ }
+
+ public Container resolveEjbContainer(String link, Class businessIntf)
+ {
+ return ejbResolver.getEjbContainer(link, businessIntf);
+ }
+
+ public Container resolveEjbContainer(Class businessIntf) throws NameNotFoundException
+ {
+ return ejbResolver.getEjbContainer(businessIntf);
+ }
+
+ public String getEjbJndiName(Class businessInterface) throws NameNotFoundException
+ {
+ return ejbResolver.getEjbJndiName(businessInterface);
+ }
+
+ public String getEjbJndiName(String link, Class businessInterface)
+ {
+ return ejbResolver.getEjbJndiName(link, businessInterface);
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType, Class<?> clazz)
+ {
+ return clazz.getAnnotation(annotationType);
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType, Class<?> clazz, Method method)
+ {
+ return method.getAnnotation(annotationType);
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType, Method method)
+ {
+ return method.getAnnotation(annotationType);
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType, Class<?> clazz, Field field)
+ {
+ return field.getAnnotation(annotationType);
+ }
+
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType, Field field)
+ {
+ return field.getAnnotation(annotationType);
+ }
+
+ public DependencyPolicy getDependencyPolicy()
+ {
+ return dependencyPolicy;
+ }
+
+}
\ No newline at end of file
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/TomcatInjectionContainer.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WarEjbResolver.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/WarEjbResolver.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/WarEjbResolver.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,52 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service;
+
+import org.jboss.ejb3.enc.DeploymentEjbResolver;
+import org.jboss.ejb3.EJBContainer;
+import org.jboss.ejb3.DeploymentScope;
+
+import javax.naming.NameNotFoundException;
+
+/**
+ * Comment
+ *
+ * @author <a href="mailto:bill at jboss.org">Bill Burke</a>
+ * @version $Revision: 1.17 $
+ */
+public class WarEjbResolver extends DeploymentEjbResolver
+{
+ public WarEjbResolver(DeploymentScope deploymentScope, String errorName)
+ {
+ super(deploymentScope, errorName);
+ }
+
+ protected EJBContainer searchDeploymentInternally(String ejbLink, Class businessIntf)
+ {
+ return null;
+ }
+
+ protected EJBContainer searchForEjbContainerInternally(Class businessIntf) throws NameNotFoundException
+ {
+ return null;
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WarEjbResolver.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,96 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service;
+
+import java.net.URL;
+
+import org.apache.catalina.loader.WebappClassLoader;
+import org.jboss.logging.Logger;
+
+/**
+ * Subclass the tomcat web app class loader to override the filter method
+ * to exclude classes which cannot be override by the web app due to their
+ * use in the tomcat web container/integration.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 58290 $
+ */
+public class WebAppClassLoader extends WebappClassLoader
+{
+ static Logger log = Logger.getLogger(WebAppClassLoader.class);
+ private String[] filteredPackages = {
+ "org.apache.commons.logging"
+ };
+
+ public WebAppClassLoader()
+ {
+ }
+
+ public WebAppClassLoader(ClassLoader parent)
+ {
+ super(parent);
+ }
+
+ public String[] getFilteredPackages()
+ {
+ return filteredPackages;
+ }
+ public void setFilteredPackages(String[] pkgs)
+ {
+ this.filteredPackages = pkgs;
+ }
+
+ @Override
+ public void addURL(URL url)
+ {
+ super.addURL(url);
+ }
+
+ /**
+ * Overriden to filter out classes in the packages listed in the
+ * filteredPackages settings.
+ *
+ * @param name
+ * @return true if the class should be loaded from the parent class loader,
+ * false if it can be loaded from this class loader.
+ */
+ protected boolean filter(String name)
+ {
+ boolean excludeClass = super.filter(name);
+ if( excludeClass == false )
+ {
+ // Check class against our filtered packages
+ int length = filteredPackages != null ? filteredPackages.length : 0;
+ for(int n = 0; n < length; n ++)
+ {
+ String pkg = filteredPackages[n];
+ if( name.startsWith(pkg) )
+ {
+ excludeClass = true;
+ break;
+ }
+ }
+ }
+ log.trace("filter name="+name+", exclude="+excludeClass);
+ return excludeClass;
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppClassLoader.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,107 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.loader.WebappLoader;
+import org.jboss.logging.Logger;
+
+/**
+ * Override the tomcat WebappLoader to set the default class loader to the
+ * WebAppClassLoader and pass the filtered packages to the WebAppClassLoader.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 58290 $
+ */
+public class WebAppLoader extends WebappLoader
+{
+ private static final Logger log = Logger.getLogger(WebAppLoader.class);
+
+ private String[] filteredPackages = {
+ "org.apache.commons.logging"
+ };
+ private List<URL> classpath;
+
+ private TomcatInjectionContainer injectionContainer;
+
+ public WebAppLoader()
+ {
+ super();
+ setLoaderClass(WebAppClassLoader.class.getName());
+ }
+
+ public WebAppLoader(ClassLoader parent, String[] filteredPackages)
+ {
+ this(parent, filteredPackages, null);
+ }
+ public WebAppLoader(ClassLoader parent, String[] filteredPackages, TomcatInjectionContainer container)
+ {
+ super(parent);
+ setLoaderClass(WebAppClassLoader.class.getName());
+ this.filteredPackages = filteredPackages;
+ injectionContainer = container;
+ }
+
+ /**
+ * Use an explicit classpath
+ *
+ * @param classpath
+ */
+ public void setClasspath(List<URL> classpath)
+ {
+ this.classpath = classpath;
+ }
+
+ /**
+ * Override to apply the filteredPackages to the jboss WebAppClassLoader
+ *
+ * @throws LifecycleException
+ */
+ public void start() throws LifecycleException
+ {
+ super.start();
+ ClassLoader loader = getClassLoader();
+ if (loader instanceof WebAppClassLoader)
+ {
+ WebAppClassLoader webLoader = (WebAppClassLoader) loader;
+ webLoader.setFilteredPackages(filteredPackages);
+ if( classpath != null )
+ {
+ for(URL url : classpath)
+ {
+ webLoader.addURL(url);
+ }
+ }
+ }
+ if (injectionContainer != null)
+ {
+ log.debug("injectionContainer enabled and processing beginning with Tomcat WebAppLoader");
+ // we need to do this because the classloader is initialize by the web container and
+ // the injection container needs the classloader so that it can build up Injectors and ENC populators
+ injectionContainer.setClassLoader(getClassLoader());
+ injectionContainer.processMetadata();
+ }
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebAppLoader.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,357 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service;
+
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.jboss.mx.loading.RepositoryClassLoader;
+import org.jboss.logging.Logger;
+
+/**
+ * Initial version of a JBoss implementation of the Tomcat Loader.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 58290 $
+ */
+public class WebCtxLoader
+ implements Lifecycle, Loader
+{
+ private static final Logger log = Logger.getLogger(WebCtxLoader.class);
+ /**
+ * The ClassLoader used to scope the ENC
+ */
+ protected ClassLoader encLoader;
+ /**
+ * The ClassLoader returned from getClassLoader
+ */
+ protected ENCLoader ctxLoader;
+ /**
+ * The war UCL used to load the war classes
+ */
+ protected RepositoryClassLoader delegate;
+ protected Container webContainer;
+ protected URL warURL;
+ protected TomcatInjectionContainer injectionContainer;
+
+ /**
+ * The set of repositories associated with this class loader.
+ */
+ private ArrayList repositories = new ArrayList();
+
+ /**
+ * Create a WebCtxLoader given the ENC scoping class loader.
+ *
+ * @param encLoader
+ */
+ public WebCtxLoader(ClassLoader encLoader)
+ {
+ this(encLoader, null);
+ }
+ public WebCtxLoader(ClassLoader encLoader, TomcatInjectionContainer container)
+ {
+ this.encLoader = encLoader;
+ this.ctxLoader = new ENCLoader(encLoader);
+ ClassLoader parent = encLoader;
+ while ((parent instanceof RepositoryClassLoader) == false && parent != null)
+ parent = parent.getParent();
+ this.delegate = (RepositoryClassLoader) parent;
+ injectionContainer = container;
+ }
+
+ /**
+ * Use an explicit classpath
+ *
+ * @param classpath
+ */
+ public void setClasspath(List<URL> classpath)
+ {
+ for(URL path : classpath)
+ {
+ delegate.addURL(path);
+ ctxLoader.addURLInternal(path);
+ }
+ }
+
+ /**
+ * Build the classpath from the war url WEB-INF/{classes,lib}.
+ *
+ * @param warURL
+ * @throws MalformedURLException
+ */
+ public void setWarURL(URL warURL) throws MalformedURLException
+ {
+ this.warURL = warURL;
+ String path = warURL.getFile();
+ File classesDir = new File(path, "WEB-INF/classes");
+ if (classesDir.exists())
+ {
+ delegate.addURL(classesDir.toURL());
+ ctxLoader.addURLInternal(classesDir.toURL());
+ }
+ File libDir = new File(path, "WEB-INF/lib");
+ if (libDir.exists())
+ {
+ File[] jars = libDir.listFiles();
+ int length = jars != null ? jars.length : 0;
+ for (int j = 0; j < length; j++)
+ {
+ File jar = jars[j];
+ if(jar.getAbsolutePath().endsWith(".jar"))
+ {
+ delegate.addURL(jar.toURL());
+ ctxLoader.addURLInternal(jar.toURL());
+ }
+ }
+ }
+ }
+
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return new LifecycleListener[0];
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ }
+
+ public void start() throws LifecycleException
+ {
+ // ctxLoader is set upon construction and nullified during stop
+ if (this.ctxLoader == null)
+ throw new LifecycleException("WebCtxLoader cannot be restarted");
+
+ setClassPath();
+ if (injectionContainer != null)
+ {
+ log.debug("injectionContainer enabled and processing beginning with JBoss WebCtxLoader");
+ // we need to do this because the classloader is initialize by the web container and
+ // the injection container needs the classloader so that it can build up Injectors and ENC populators
+ injectionContainer.setClassLoader(getClassLoader());
+ injectionContainer.processMetadata();
+ }
+ ServletContext servletContext = ((Context) webContainer).getServletContext();
+ if (servletContext == null)
+ return;
+ }
+
+ public void stop() throws LifecycleException
+ {
+ // Remove the ctxLoader mapping kept by the DirContextURLStreamHandler
+ DirContextURLStreamHandler.unbind(ctxLoader);
+ org.apache.commons.logging.LogFactory.release(ctxLoader);
+ org.apache.commons.logging.LogFactory.release(encLoader);
+ this.encLoader = null;
+ this.ctxLoader = null;
+ this.delegate = null;
+ this.repositories.clear();
+ this.warURL = null;
+ this.webContainer = null;
+ }
+
+ public void backgroundProcess()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ /**
+ * We must pass the wrapped encLoader as tomcat needs to see a unique
+ * class loader that is distinct from the thread context class loader seen
+ * to be in effect when the web app is started. This is due to how it
+ * binds contexts using the DirContextURLStreamHandler class.
+ *
+ * @return The ENC scoping class loader
+ * @see org.apache.naming.resources.DirContextURLStreamHandler
+ */
+ public ClassLoader getClassLoader()
+ {
+ return ctxLoader;
+ }
+
+ public Container getContainer()
+ {
+ return webContainer;
+ }
+
+ public void setContainer(Container container)
+ {
+ webContainer = container;
+
+ }
+
+ public boolean getDelegate()
+ {
+ return false;
+ }
+
+ public void setDelegate(boolean delegate)
+ {
+ }
+
+ public String getInfo()
+ {
+ return null;
+ }
+
+ public boolean getReloadable()
+ {
+ return false;
+ }
+
+ public void setReloadable(boolean reloadable)
+ {
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ }
+
+ public void addRepository(String repository)
+ {
+ if (repositories.contains(repository) == true)
+ return;
+ repositories.add(repository);
+ setClassPath();
+ }
+
+ public String[] findRepositories()
+ {
+ String[] tmp = new String[repositories.size()];
+ repositories.toArray(tmp);
+ return tmp;
+ }
+
+ public boolean modified()
+ {
+ return false;
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ }
+
+ /**
+ * Set the appropriate context attribute for our class path. This
+ * is required only because Jasper depends on it.
+ */
+ private void setClassPath()
+ {
+ // Validate our current state information
+ if (!(webContainer instanceof Context))
+ return;
+ ServletContext servletContext = ((Context) webContainer).getServletContext();
+ if (servletContext == null)
+ return;
+
+ try
+ {
+ Method method =
+ webContainer.getClass().getMethod("getCompilerClasspath", null);
+ Object baseClasspath = method.invoke(webContainer, null);
+ if (baseClasspath != null)
+ {
+ servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+ baseClasspath.toString());
+ return;
+ }
+ }
+ catch (Exception e)
+ {
+ // Ignore
+ e.printStackTrace();
+ }
+
+ StringBuffer classpath = new StringBuffer();
+
+ // Assemble the class path information from our repositories
+ for (int i = 0; i < repositories.size(); i++)
+ {
+ String repository = repositories.get(i).toString();
+ if (repository.startsWith("file://"))
+ repository = repository.substring(7);
+ else if (repository.startsWith("file:"))
+ repository = repository.substring(5);
+ else if (repository.startsWith("jndi:"))
+ repository = servletContext.getRealPath(repository.substring(5));
+ else
+ continue;
+ if (repository == null)
+ continue;
+ if (i > 0)
+ classpath.append(File.pathSeparator);
+ classpath.append(repository);
+ }
+
+ // Store the assembled class path as a servlet context attribute
+ servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+ classpath.toString());
+
+ }
+
+ /**
+ * A trival extension of URLClassLoader that uses an empty URL[] as its
+ * classpath so that all work is delegated to its parent.
+ */
+ static class ENCLoader extends URLClassLoader
+ {
+ private URL[] urllist = new URL[0];
+
+ ENCLoader(ClassLoader parent)
+ {
+ super(new URL[0], parent);
+ }
+
+ void addURLInternal(URL url)
+ {
+ URL[] result = new URL[urllist.length + 1];
+ for (int i = 0; i < urllist.length; i++)
+ result[i] = urllist[i];
+ result[urllist.length] = url;
+ urllist = result;
+ }
+
+ public URL[] getURLs()
+ {
+ return urllist;
+ }
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/WebCtxLoader.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/DeployerConfig.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/DeployerConfig.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/DeployerConfig.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,382 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.deployers;
+
+import java.net.URL;
+
+import javax.management.ObjectName;
+
+import org.jboss.metadata.WebMetaData;
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+
+/**
+ * The tomcat war deployer configuration passed in from the web container.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 56125 $
+ */
+public class DeployerConfig
+{
+ /**
+ * The tomcat sar class loader
+ */
+ private ClassLoader serviceClassLoader;
+ /**
+ * The domain used for the tomcat mbeans
+ */
+ private String catalinaDomain = "Catalina";
+
+ /**
+ * The fully qualified name of the class that will be used for session
+ * management if <tt>distributable</tt> is set to true.
+ */
+ private String managerClass = "org.jboss.web.tomcat.service.session.JBossManager";
+
+ /**
+ * JMX Object name of the TreeCache MBean. Used by JBossCacheManager
+ */
+ private String cacheName = "jboss.cache:service=TreeCache";
+
+ /**
+ * The web context class to create
+ */
+ private String contextClassName;
+ /**
+ * The parent class loader first model flag
+ */
+ private boolean java2ClassLoadingCompliance = false;
+ /**
+ * A flag indicating if war archives should be unpacked
+ */
+ private boolean unpackWars = true;
+ /**
+ * If true, ejb-links that don't resolve don't cause an error
+ * (fallback to jndi-name)
+ */
+ private boolean lenientEjbLink = false;
+ /**
+ * The tomcat service JMX object name
+ */
+ private ObjectName serviceName;
+ /**
+ * The catalina debug level
+ */
+ private int debugLevel;
+ /**
+ * A flag indicating if the JBoss UCL should be used
+ */
+ private boolean useJBossWebLoader = true;
+ /**
+ * A flag indicating if the working dir for a war deployment should be
+ * delete when the war is undeployed.
+ */
+ private boolean deleteWorkDirs = true;
+ /**
+ * Which snapshot mode should be used in clustered environment?
+ * Default: instant
+ */
+ private String snapshotMode = "instant"; // instant or interval
+ /**
+ * With IntervalSnapshotManager use this interval (in ms) for snapshotting
+ */
+ private int snapshotInterval = 1000;
+
+ /**
+ * Should the clustering code use caching or not?
+ */
+ private boolean useLocalCache;
+
+ /**
+ * Whether to use MOD_JK(2) for sticky session combined with JvmRoute. If set to true,
+ * it will insert a JvmRouteFilter to intercept every request and replace the JvmRoute
+ * if it detects a failover.
+ */
+ private boolean useJK = false;
+
+ /**
+ * Get the request attribute name under which the JAAS Subject is store
+ */
+ private String subjectAttributeName = null;
+ /**
+ * The default security-domain name to use
+ */
+ private String defaultSecurityDomain;
+ /** Package names that should be ignored for class loading */
+ private String[] filteredPackages;
+
+ /**
+ * Shared WebMetaData.
+ */
+ private WebMetaData sharedMetaData = null;
+
+ /**
+ * Flag indicating whether web-app specific context xmls may set the privileged flag.
+ */
+ private boolean allowSelfPrivilegedWebApps = false;
+ /** The service used to flush authentication cache on session invalidation. */
+ private JaasSecurityManagerServiceMBean secMgrService;
+
+ private URL xacmlPolicyURL = null;
+
+ public ClassLoader getServiceClassLoader()
+ {
+ return serviceClassLoader;
+ }
+
+ public void setServiceClassLoader(ClassLoader serviceClassLoader)
+ {
+ this.serviceClassLoader = serviceClassLoader;
+ }
+
+ public String getManagerClass()
+ {
+ return managerClass;
+ }
+
+ public void setManagerClass(String managerClass)
+ {
+ this.managerClass = managerClass;
+ }
+
+ public String getCacheName()
+ {
+ return cacheName;
+ }
+
+ public void setCacheName(String cacheName)
+ {
+ this.cacheName = cacheName;
+ }
+
+ public String getCatalinaDomain()
+ {
+ return catalinaDomain;
+ }
+
+ public void setCatalinaDomain(String catalinaDomain)
+ {
+ this.catalinaDomain = catalinaDomain;
+ }
+
+ public String getContextClassName()
+ {
+ return contextClassName;
+ }
+
+ public void setContextClassName(String contextClassName)
+ {
+ this.contextClassName = contextClassName;
+ }
+
+ public boolean isJava2ClassLoadingCompliance()
+ {
+ return java2ClassLoadingCompliance;
+ }
+
+ public void setJava2ClassLoadingCompliance(boolean java2ClassLoadingCompliance)
+ {
+ this.java2ClassLoadingCompliance = java2ClassLoadingCompliance;
+ }
+
+ public boolean isUnpackWars()
+ {
+ return unpackWars;
+ }
+
+ public void setUnpackWars(boolean unpackWars)
+ {
+ this.unpackWars = unpackWars;
+ }
+
+ public boolean isLenientEjbLink()
+ {
+ return lenientEjbLink;
+ }
+
+ public void setLenientEjbLink(boolean lenientEjbLink)
+ {
+ this.lenientEjbLink = lenientEjbLink;
+ }
+
+ public ObjectName getServiceName()
+ {
+ return serviceName;
+ }
+
+ public void setServiceName(ObjectName serviceName)
+ {
+ this.serviceName = serviceName;
+ }
+
+ public int getDebugLevel()
+ {
+ return debugLevel;
+ }
+
+ public void setDebugLevel(int debugLevel)
+ {
+ this.debugLevel = debugLevel;
+ }
+
+ public boolean isUseJBossWebLoader()
+ {
+ return useJBossWebLoader;
+ }
+
+ public void setUseJBossWebLoader(boolean useJBossWebLoader)
+ {
+ this.useJBossWebLoader = useJBossWebLoader;
+ }
+
+ public boolean isDeleteWorkDirs()
+ {
+ return deleteWorkDirs;
+ }
+
+ public void setDeleteWorkDirs(boolean deleteWorkDirs)
+ {
+ this.deleteWorkDirs = deleteWorkDirs;
+ }
+
+ public String getSnapshotMode()
+ {
+ return snapshotMode;
+ }
+
+ public void setSnapshotMode(String snapshotMode)
+ {
+ this.snapshotMode = snapshotMode;
+ }
+
+ public int getSnapshotInterval()
+ {
+ return snapshotInterval;
+ }
+
+ public void setSnapshotInterval(int snapshotInterval)
+ {
+ this.snapshotInterval = snapshotInterval;
+ }
+
+ public boolean isUseLocalCache()
+ {
+ return useLocalCache;
+ }
+
+ public void setUseLocalCache(boolean useLocalCache)
+ {
+ this.useLocalCache = useLocalCache;
+ }
+
+ public boolean isUseJK()
+ {
+ return useJK;
+ }
+
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK = useJK;
+ }
+
+ public String getSubjectAttributeName()
+ {
+ return subjectAttributeName;
+ }
+
+ public void setSubjectAttributeName(String subjectAttributeName)
+ {
+ this.subjectAttributeName = subjectAttributeName;
+ }
+
+ /**
+ * Get the default security domain implementation to use if a war
+ * does not declare a security-domain.
+ *
+ * @return jndi name of the security domain binding to use.
+ * @jmx:managed-attribute
+ */
+ public String getDefaultSecurityDomain()
+ {
+ return defaultSecurityDomain;
+ }
+
+ /**
+ * Set the default security domain implementation to use if a war
+ * does not declare a security-domain.
+ *
+ * @param defaultSecurityDomain - jndi name of the security domain binding
+ * to use.
+ * @jmx:managed-attribute
+ */
+ public void setDefaultSecurityDomain(String defaultSecurityDomain)
+ {
+ this.defaultSecurityDomain = defaultSecurityDomain;
+ }
+
+ public boolean isAllowSelfPrivilegedWebApps()
+ {
+ return allowSelfPrivilegedWebApps;
+ }
+
+ public void setAllowSelfPrivilegedWebApps(boolean allowSelfPrivilegedWebApps)
+ {
+ this.allowSelfPrivilegedWebApps = allowSelfPrivilegedWebApps;
+ }
+
+ public JaasSecurityManagerServiceMBean getSecurityManagerService()
+ {
+ return secMgrService;
+ }
+ public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr)
+ {
+ this.secMgrService = mgr;
+ }
+
+ public String[] getFilteredPackages()
+ {
+ return filteredPackages;
+ }
+ public void setFilteredPackages(String[] filteredPackages)
+ {
+ this.filteredPackages = filteredPackages;
+ }
+
+ public URL getXacmlPolicyURL()
+ {
+ return xacmlPolicyURL;
+ }
+
+ public void setXacmlPolicyURL(URL xacmlPolicyURL)
+ {
+ this.xacmlPolicyURL = xacmlPolicyURL;
+ }
+
+ public WebMetaData getSharedMetaData()
+ {
+ return sharedMetaData;
+ }
+
+ public void setSharedMetaData(WebMetaData sharedMetaData)
+ {
+ this.sharedMetaData = sharedMetaData;
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/DeployerConfig.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,280 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.deployers;
+
+import java.util.Iterator;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.ContextConfig;
+import org.jboss.metadata.Listener;
+import org.jboss.metadata.NameValuePair;
+import org.jboss.metadata.SecurityRoleRefMetaData;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.metadata.WebSecurityMetaData;
+import org.jboss.metadata.WebSecurityMetaData.WebResourceCollection;
+import org.jboss.metadata.web.ErrorPage;
+import org.jboss.metadata.web.Filter;
+import org.jboss.metadata.web.FilterMapping;
+import org.jboss.metadata.web.LocaleEncodingMapping;
+import org.jboss.metadata.web.LoginConfig;
+import org.jboss.metadata.web.MimeMapping;
+import org.jboss.metadata.web.ParamValue;
+import org.jboss.metadata.web.Servlet;
+import org.jboss.metadata.web.ServletMapping;
+import org.jboss.metadata.web.SessionConfig;
+import org.jboss.metadata.web.Taglib;
+
+public class JBossContextConfig extends ContextConfig {
+
+ public static ThreadLocal<WebMetaData> metaDataLocal = new ThreadLocal<WebMetaData>();
+ public static ThreadLocal<WebMetaData> metaDataShared = new ThreadLocal<WebMetaData>();
+
+ @Override
+ protected void applicationWebConfig() {
+ processWebMetaData(metaDataLocal.get());
+ }
+
+
+ @Override
+ protected void defaultWebConfig()
+ {
+ processWebMetaData(metaDataShared.get());
+ }
+
+
+ protected void processWebMetaData(WebMetaData metaData) {
+
+ if (context instanceof StandardContext) {
+ ((StandardContext) context).setReplaceWelcomeFiles(true);
+ }
+
+ // Metadata complete
+ context.setIgnoreAnnotations(metaData.isMetadataComplete());
+
+ // Context params
+ Iterator<ParamValue> iterator = metaData.getContextParams().iterator();
+ while (iterator.hasNext()) {
+ ParamValue paramValue = iterator.next();
+ context.addParameter(paramValue.getName(), paramValue.getValue());
+ }
+
+ // Display name
+ context.setDisplayName(metaData.getDisplayName());
+
+ // Distributable
+ context.setDistributable(metaData.getDistributable());
+
+ // Error pages
+ Iterator iterator2 = metaData.getErrorPages().iterator();
+ while (iterator2.hasNext()) {
+ ErrorPage value = (ErrorPage) iterator2.next();
+ org.apache.catalina.deploy.ErrorPage errorPage =
+ new org.apache.catalina.deploy.ErrorPage();
+ errorPage.setErrorCode(value.getErrorCode());
+ errorPage.setExceptionType(value.getExceptionType());
+ errorPage.setLocation(value.getLocation());
+ context.addErrorPage(errorPage);
+ }
+
+ // Filter definitions
+ Iterator<Filter> iterator3 = metaData.getFilters().iterator();
+ while (iterator3.hasNext()) {
+ Filter value = iterator3.next();
+ org.apache.catalina.deploy.FilterDef filterDef =
+ new org.apache.catalina.deploy.FilterDef();
+ filterDef.setFilterName(value.getName());
+ filterDef.setFilterClass(value.getFilterClass());
+ Iterator params = value.getInitParams().iterator();
+ while (params.hasNext()) {
+ NameValuePair nvp = (NameValuePair) params.next();
+ filterDef.addInitParameter(nvp.getName(), nvp.getValue());
+ }
+ context.addFilterDef(filterDef);
+ }
+
+ // Filter mappings
+ Iterator<FilterMapping> iterator4 = metaData.getFilterMappings().iterator();
+ while (iterator4.hasNext()) {
+ FilterMapping value = iterator4.next();
+ org.apache.catalina.deploy.FilterMap filterMap =
+ new org.apache.catalina.deploy.FilterMap();
+ filterMap.setFilterName(value.getFilterName());
+ Iterator<String> servletNames = value.getServletNames().iterator();
+ while (servletNames.hasNext()) {
+ filterMap.addServletName(servletNames.next());
+ }
+ Iterator<String> urlPatterns = value.getUrlPatterns().iterator();
+ while (urlPatterns.hasNext()) {
+ filterMap.addURLPattern(urlPatterns.next());
+ }
+ Iterator<String> dispatchers = value.getDispatchers().iterator();
+ while (dispatchers.hasNext()) {
+ filterMap.setDispatcher(dispatchers.next());
+ }
+ context.addFilterMap(filterMap);
+ }
+
+ // Listeners
+ Iterator iterator5 = metaData.getListeners().iterator();
+ while (iterator5.hasNext()) {
+ Listener value = (Listener) iterator5.next();
+ context.addApplicationListener(value.getListenerClass());
+ }
+
+ // Login configuration
+ LoginConfig loginConfig = metaData.getLoginConfig();
+ if (loginConfig != null) {
+ org.apache.catalina.deploy.LoginConfig loginConfig2 =
+ new org.apache.catalina.deploy.LoginConfig();
+ loginConfig2.setAuthMethod(loginConfig.getAuthMethod());
+ loginConfig2.setRealmName(loginConfig.getRealmName());
+ if (loginConfig.getFormLoginConfig() != null) {
+ loginConfig2.setLoginPage(loginConfig.getFormLoginConfig().getLoginPage());
+ loginConfig2.setErrorPage(loginConfig.getFormLoginConfig().getErrorPage());
+ }
+ context.setLoginConfig(loginConfig2);
+ }
+
+ // MIME mappings
+ Iterator<MimeMapping> iterator11 = metaData.getMimeMappings().iterator();
+ while (iterator11.hasNext()) {
+ MimeMapping value = iterator11.next();
+ context.addMimeMapping(value.getExtension(), value.getMimeType());
+ }
+
+ // Security constraints
+ Iterator<WebSecurityMetaData> iterator6 = metaData.getSecurityContraints();
+ while (iterator6.hasNext()) {
+ WebSecurityMetaData value = iterator6.next();
+ org.apache.catalina.deploy.SecurityConstraint constraint =
+ new org.apache.catalina.deploy.SecurityConstraint();
+ constraint.setUserConstraint(value.getTransportGuarantee());
+ constraint.setAuthConstraint(value.getAuthConstraint() != null);
+ if (value.getAuthConstraint() != null) {
+ Iterator<String> roles = value.getAuthConstraint().getRoleNames().iterator();
+ while (roles.hasNext()) {
+ constraint.addAuthRole(roles.next());
+ }
+ }
+ Iterator<WebResourceCollection> collections = value.getWebResources().values().iterator();
+ while (collections.hasNext()) {
+ WebResourceCollection collection = collections.next();
+ org.apache.catalina.deploy.SecurityCollection collection2 =
+ new org.apache.catalina.deploy.SecurityCollection();
+ collection2.setName(collection.getName());
+ String[] methods = collection.getHttpMethods();
+ for (int i = 0; i < methods.length; i++) {
+ collection2.addMethod(methods[i]);
+ }
+ String[] urlPatterns = collection.getUrlPatterns();
+ for (int i = 0; i < urlPatterns.length; i++) {
+ collection2.addPattern(urlPatterns[i]);
+ }
+ constraint.addCollection(collection2);
+ }
+ context.addConstraint(constraint);
+ }
+
+ // Security roles
+ Iterator iterator9 = metaData.getSecurityRoleNames().iterator();
+ while (iterator9.hasNext()) {
+ context.addSecurityRole((String) iterator9.next());
+ }
+
+ // Servlet
+ Iterator<Servlet> iterator7 = metaData.getServlets().iterator();
+ while (iterator7.hasNext()) {
+ Servlet value = iterator7.next();
+ org.apache.catalina.Wrapper wrapper = context.createWrapper();
+ wrapper.setName(value.getName());
+ wrapper.setServletClass(value.getServletClass());
+ if (value.getJspFile() != null) {
+ wrapper.setJspFile(value.getJspFile());
+ }
+ wrapper.setLoadOnStartup(value.getLoadOnStartup());
+ if (value.getRunAs() != null) {
+ wrapper.setRunAs(value.getRunAs().getRoleName());
+ }
+ Iterator nvps = value.getInitParams().iterator();
+ while (nvps.hasNext()) {
+ NameValuePair nvp = (NameValuePair) nvps.next();
+ wrapper.addInitParameter(nvp.getName(), nvp.getValue());
+ }
+ Iterator securityRoleRefs = value.getSecurityRoleRefs().iterator();
+ while (securityRoleRefs.hasNext()) {
+ SecurityRoleRefMetaData securityRoleRef = (SecurityRoleRefMetaData) securityRoleRefs.next();
+ wrapper.addSecurityReference(securityRoleRef.getName(), securityRoleRef.getLink());
+ }
+ context.addChild(wrapper);
+ }
+
+ // Servlet mapping
+ Iterator iterator8 = metaData.getServletMappings().iterator();
+ while (iterator8.hasNext()) {
+ ServletMapping value = (ServletMapping) iterator8.next();
+ Iterator<String> urlPatterns = value.getUrlPatterns().iterator();
+ while (urlPatterns.hasNext()) {
+ context.addServletMapping(urlPatterns.next(), value.getName());
+ }
+ }
+
+ // JSP mappings
+ Iterator<String> iterator10 = metaData.getJspMappings().iterator();
+ while (iterator10.hasNext()) {
+ String value = iterator10.next();
+ context.addJspMapping(value);
+ }
+
+ // Locale encoding mapping
+ Iterator<LocaleEncodingMapping> iterator12 = metaData.getLocaleEncodingMappings().iterator();
+ while (iterator12.hasNext()) {
+ LocaleEncodingMapping value = iterator12.next();
+ context.addLocaleEncodingMappingParameter(value.getLocale(), value.getEncoding());
+ }
+
+ // Welcome files
+ Iterator<String> iterator13 = metaData.getWelcomeFiles().iterator();
+ while (iterator13.hasNext()) {
+ String value = iterator13.next();
+ context.addWelcomeFile(value);
+ }
+
+ // Session timeout
+ Iterator iterator14 = metaData.getSessionConfigs().iterator();
+ if (iterator14.hasNext()) {
+ SessionConfig value = (SessionConfig) iterator14.next();
+ if (value.getSessionTimeout() != null) {
+ int i = Integer.decode(value.getSessionTimeout());
+ context.setSessionTimeout(i);
+ }
+ }
+
+ // Taglib
+ Iterator<Taglib> iterator15 = metaData.getTaglibs().iterator();
+ while (iterator15.hasNext()) {
+ Taglib value = iterator15.next();
+ context.addTaglib(value.getUri(), value.getLocation());
+ }
+
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/JBossContextConfig.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployer.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployer.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployer.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,805 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.deployers;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.security.jacc.PolicyContext;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.connector.Connector;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.metadata.web.WebMetaDataObjectFactory;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+import org.jboss.system.server.Server;
+import org.jboss.system.server.ServerImplMBean;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.deployers.AbstractWarDeployer;
+import org.jboss.web.deployers.AbstractWarDeployment;
+import org.jboss.web.tomcat.security.HttpServletRequestPolicyContextHandler;
+import org.jboss.web.tomcat.service.session.SessionIDGenerator;
+import org.jboss.xb.binding.Unmarshaller;
+import org.jboss.xb.binding.UnmarshallerFactory;
+
+
+/**
+ * A concrete implementation of the AbstractWarDeployer that creates
+ * {@link #TomcatDeployment} instances as the web application bean
+ * representation.
+ *
+ * @see #getDeployment(DeploymentUnit, WebMetaData)
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @author Wonne.Keysers at realsoftware.be
+ * @author Dimitris.Andreadis at jboss.org
+ * @version $Revision: 56809 $
+ * @see org.jboss.web.deployers.AbstractWarDeployer
+ */
+public class TomcatDeployer extends AbstractWarDeployer
+ implements NotificationListener, TomcatDeployerMBean
+{
+ // Constants -----------------------------------------------------
+ public static final String NAME = "TomcatDeployer";
+
+ /**
+ * Default value for property <code>cacheName</code>. This name will be used by JBossCache exclusively
+ * for Tomcat clustering, e.g., session and sso replication.
+ */
+ public static final String DEFAULT_CACHE_NAME =
+ "jboss.cache:service=TomcatClusteringCache";
+
+ /** The web app context implementation class */
+ private String contextClassName =
+ "org.apache.catalina.core.StandardContext";
+
+ /**
+ * Unmarshall factory used for parsing shared web.xml.
+ */
+ private static final UnmarshallerFactory factory = UnmarshallerFactory.newInstance();
+
+ /**
+ * Configurable map of tomcat authenticators
+ * Keyed in by the http auth method that gets
+ * plugged into the Context Config and then into the StandardContext
+ */
+ private Properties authenticators = null;
+
+ /**
+ * Domain for tomcat6 mbeans
+ */
+ private String catalinaDomain = "Catalina";
+
+ /**
+ * ObjectName of a shared TreeCache used for clustered session replication
+ * and clustered single-sign-on
+ */
+ private String cacheName = DEFAULT_CACHE_NAME;
+
+ /**
+ * The fully qualified name of the class that will be used for session
+ * management if <tt>distributable</tt> is set to true.
+ */
+ protected String managerClass = "org.jboss.web.tomcat.service.session.JBossCacheManager";
+
+ /**
+ * With IntervalSnapshotManager use this interval (in ms)
+ * for snapshotting
+ */
+ private int snapshotInterval = 1000;
+
+ /**
+ * Which snapshot mode should be used in clustered environment?
+ * Default: instant
+ */
+ private String snapshotMode = "instant"; // instant or interval
+
+ /**
+ * Should the clustering code use a local cache for the sessions?
+ */
+ private boolean useLocalCache = true;
+
+ /**
+ * Whether we are using Apache MOD_JK(2) module or not
+ */
+ private boolean useJK = false;
+
+ /**
+ * A flag indicating if the JBoss Loader should be used
+ */
+ private boolean useJBossWebLoader = true;
+
+ /**
+ * JBAS-3358: Work directory shouldn't be deleted on Context Destroy
+ */
+ private boolean deleteWorkDirOnContextDestroy = false;
+
+ /**
+ * JBAS-2283: Provide custom header based auth support
+ */
+ private String httpHeaderForSSOAuth = null;
+ private String sessionCookieForSSOAuth = null;
+
+ /**
+ * Shared metaData.
+ */
+ private WebMetaData sharedMetaData = null;
+
+
+ /**
+ * The server xml configuration file name
+ */
+ private String serverConfigFile = "server.xml";
+
+ /**
+ * Get the request attribute name under which the JAAS Subject is store
+ */
+ private String subjectAttributeName = null;
+
+ /**
+ * Flag indicating whether web-app specific context xmls may set the privileged flag.
+ */
+ private boolean allowSelfPrivilegedWebApps = false;
+
+ /** The service used to flush authentication cache on session invalidation. */
+ private JaasSecurityManagerServiceMBean secMgrService;
+ /** The AbstractWarDeployment implementation class */
+ private Class deploymentClass = TomcatDeployment.class;
+
+ /** */
+ private String[] filteredPackages;
+
+ public TomcatDeployer()
+ {
+ }
+
+ public String getName()
+ {
+ return NAME;
+ }
+
+
+ public String getManagerClass()
+ {
+ return managerClass;
+ }
+
+ public void setManagerClass(String managerClass)
+ {
+ this.managerClass = managerClass;
+ }
+
+
+ public String getDomain()
+ {
+ return this.catalinaDomain;
+ }
+
+ public Properties getAuthenticators()
+ {
+ return this.authenticators;
+ }
+
+ public void setAuthenticators(Properties prop)
+ {
+ this.authenticators = prop;
+ log.debug("Passed set of authenticators=" + prop);
+ }
+
+ /**
+ * The most important atteribute - defines the managed domain.
+ * A catalina instance (engine) corresponds to a JMX domain, that's
+ * how we know where to deploy webapps.
+ *
+ * @param catalinaDomain the domain portion of the JMX ObjectNames
+ */
+ public void setDomain(String catalinaDomain)
+ {
+ this.catalinaDomain = catalinaDomain;
+ }
+
+ public void setContextMBeanCode(String className)
+ {
+ this.contextClassName = className;
+ }
+
+ public String getContextMBeanCode()
+ {
+ return contextClassName;
+ }
+
+ /**
+ * Set the snapshot interval in milliseconds for snapshot mode = interval
+ */
+ public void setSnapshotInterval(int interval)
+ {
+ this.snapshotInterval = interval;
+ }
+
+ /**
+ * Get the snapshot interval
+ */
+ public int getSnapshotInterval()
+ {
+ return this.snapshotInterval;
+ }
+
+ /**
+ * Set the snapshot mode. Currently supported: instant or interval
+ */
+ public void setSnapshotMode(String mode)
+ {
+ this.snapshotMode = mode;
+ }
+
+ /**
+ * Get the snapshot mode
+ */
+ public String getSnapshotMode()
+ {
+ return this.snapshotMode;
+ }
+
+ /**
+ * Gets the JMX object name of a shared TreeCache to be used for clustered
+ * single-sign-on.
+ *
+ * @see #DEFAULT_CACHE_NAME
+ * @see org.jboss.web.tomcat.service.sso.TreeCacheSSOClusterManager
+ */
+ public String getCacheName()
+ {
+ return cacheName;
+ }
+
+ /**
+ * Gets the JMX object name of a shared TreeCache to be used for clustered
+ * single-sign-on.
+ * <p/>
+ * <b>NOTE:</b> TreeCache must be deployed before this service.
+ *
+ * @see #DEFAULT_CACHE_NAME
+ * @see org.jboss.web.tomcat.service.sso.TreeCacheSSOClusterManager
+ */
+ public void setCacheName(String cacheName)
+ {
+ this.cacheName = cacheName;
+ }
+
+ public boolean isUseLocalCache()
+ {
+ return useLocalCache;
+ }
+
+ public void setUseLocalCache(boolean useLocalCache)
+ {
+ this.useLocalCache = useLocalCache;
+ }
+
+ public boolean isUseJK()
+ {
+ return useJK;
+ }
+
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK = useJK;
+ }
+
+ public boolean getDeleteWorkDirOnContextDestroy()
+ {
+ return deleteWorkDirOnContextDestroy;
+ }
+
+ public void setDeleteWorkDirOnContextDestroy(boolean deleteFlag)
+ {
+ this.deleteWorkDirOnContextDestroy = deleteFlag;
+ }
+
+ public String getHttpHeaderForSSOAuth()
+ {
+ return httpHeaderForSSOAuth;
+ }
+
+ public void setHttpHeaderForSSOAuth(String httpHeader)
+ {
+ this.httpHeaderForSSOAuth = httpHeader;
+ }
+
+ public String getSessionCookieForSSOAuth()
+ {
+ return sessionCookieForSSOAuth;
+ }
+
+ public void setSessionCookieForSSOAuth(String sessionC)
+ {
+ this.sessionCookieForSSOAuth = sessionC;
+ }
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public void setSessionIdAlphabet(String sessionIdAlphabet)
+ {
+ SessionIDGenerator.getInstance().setSessionIdAlphabet(sessionIdAlphabet);
+ }
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public String getSessionIdAlphabet()
+ {
+ return SessionIDGenerator.getInstance().getSessionIdAlphabet();
+ }
+
+ public boolean getUseJBossWebLoader()
+ {
+ return useJBossWebLoader;
+ }
+
+ public void setUseJBossWebLoader(boolean flag)
+ {
+ this.useJBossWebLoader = flag;
+ }
+
+ public String getConfigFile()
+ {
+ return serverConfigFile;
+ }
+
+ public void setConfigFile(String configFile)
+ {
+ this.serverConfigFile = configFile;
+ }
+
+ public String getSubjectAttributeName()
+ {
+ return this.subjectAttributeName;
+ }
+
+ public void setSubjectAttributeName(String name)
+ {
+ this.subjectAttributeName = name;
+ }
+
+ public boolean isAllowSelfPrivilegedWebApps()
+ {
+ return allowSelfPrivilegedWebApps;
+ }
+
+ public void setAllowSelfPrivilegedWebApps(boolean allowSelfPrivilegedWebApps)
+ {
+ this.allowSelfPrivilegedWebApps = allowSelfPrivilegedWebApps;
+ }
+
+ public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr)
+ {
+ this.secMgrService = mgr;
+ }
+
+ public String[] getFilteredPackages()
+ {
+ return filteredPackages;
+ }
+ public void setFilteredPackages(String[] pkgs)
+ {
+ this.filteredPackages = pkgs;
+ }
+
+ public Class getDeploymentClass()
+ {
+ return deploymentClass;
+ }
+ public void setDeploymentClass(Class deploymentClass)
+ {
+ this.deploymentClass = deploymentClass;
+ }
+
+ /**
+ * Start the deployer. This sets up the tomcat core.
+ */
+ public void start()
+ throws Exception
+ {
+ super.start();
+
+ log.debug("Starting tomcat deployer");
+ MBeanServer server = super.getServer();
+ System.setProperty("catalina.ext.dirs",
+ (System.getProperty("jboss.server.home.dir")
+ + File.separator + "lib"));
+
+ String objectNameS = catalinaDomain + ":type=server";
+ ObjectName objectName = new ObjectName(objectNameS);
+
+ // Set the modeler Registry MBeanServer to the that of the tomcat service
+ Registry.getRegistry().setMBeanServer(server);
+
+ Registry.getRegistry().registerComponent(Class.forName("org.apache.catalina.startup.Catalina").newInstance(),
+ objectName, "org.apache.catalina.startup.Catalina");
+ /*
+ server.createMBean("org.apache.tomcat.util.modeler.BaseModelMBean",
+ objectName,
+ new Object[]{"org.apache.catalina.startup.Catalina"},
+ new String[]{"java.lang.String"});
+ */
+
+ server.setAttribute(objectName, new Attribute
+ ("catalinaHome",
+ System.getProperty("jboss.server.home.dir")));
+ server.setAttribute(objectName, new Attribute
+ ("configFile", serverConfigFile));
+ server.setAttribute(objectName, new Attribute
+ ("useNaming", new Boolean(false)));
+ server.setAttribute(objectName, new Attribute
+ ("useShutdownHook", new Boolean(false)));
+ server.setAttribute(objectName, new Attribute
+ ("await", new Boolean(false)));
+ server.setAttribute(objectName, new Attribute
+ ("redirectStreams", new Boolean(false)));
+
+ server.invoke(objectName, "create", new Object[]{},
+ new String[]{});
+
+ server.invoke(objectName, "start", new Object[]{},
+ new String[]{});
+
+ // Configure any SingleSignOn valves
+
+ ObjectName ssoQuery = new ObjectName(catalinaDomain + ":type=Valve,*");
+ Iterator iterator = server.queryMBeans(ssoQuery, null).iterator();
+ while (iterator.hasNext())
+ {
+ ObjectName ssoObjectName =
+ ((ObjectInstance) iterator.next()).getObjectName();
+ String name = ssoObjectName.getKeyProperty("name");
+
+ /* Ensure that the SingleSignOn valve requires that each
+ request be reauthenticated to the security mgr. Should not
+ be neccessary now that we cache the principal in the session.
+ if ((name != null) && (name.indexOf("SingleSignOn") >= 0))
+ {
+ log.info("Turning on reauthentication of each request on " +
+ ssoObjectName);
+ server.setAttribute(ssoObjectName, new Attribute
+ ("requireReauthentication", Boolean.TRUE));
+ }
+ */
+
+ // If the valve is a ClusteredSingleSignOn and we have a shared
+ // TreeCache configured, configure the valve to use the shared one
+ if (cacheName != null && "ClusteredSingleSignOn".equals(name))
+ {
+ String tcName = (String) server.getAttribute(ssoObjectName,
+ "treeCacheName");
+ tcName = (tcName != null ? tcName : DEFAULT_CACHE_NAME);
+ ObjectName ssoCacheName = new ObjectName(tcName);
+ // Only override if the valve's cacheName property was not
+ // explicitly set in server.xml to a non-default value
+ if (ssoCacheName.equals(new ObjectName(DEFAULT_CACHE_NAME)))
+ {
+ log.info("Setting the cache name to " + cacheName +
+ " on " + ssoObjectName);
+ server.setAttribute(ssoObjectName,
+ new Attribute("treeCacheName", cacheName));
+ }
+ }
+ }
+
+ //Set up the authenticators in JNDI such that they can be configured for web apps
+ InitialContext ic = new InitialContext();
+ try
+ {
+ ic.bind("TomcatAuthenticators", this.authenticators);
+ }
+ catch(NamingException ne)
+ {
+ if(log.isTraceEnabled())
+ log.trace("Binding Authenticators to JNDI failed",ne);
+ }
+ finally
+ {
+ try{ ic.close();}catch(NamingException nee){}
+ }
+
+ // Register the web container JACC PolicyContextHandlers
+ HttpServletRequestPolicyContextHandler handler = new HttpServletRequestPolicyContextHandler();
+ PolicyContext.registerHandler(HttpServletRequestPolicyContextHandler.WEB_REQUEST_KEY,
+ handler, true);
+
+ // Parse shared web.xml
+ Unmarshaller unmarshaller = factory.newUnmarshaller();
+ URL webXml = this.getClass().getClassLoader().getResource("web.xml");
+ if (webXml == null) {
+ webXml = this.getClass().getClassLoader().getResource("conf/web.xml");;
+ }
+ sharedMetaData = (WebMetaData)
+ unmarshaller.unmarshal(webXml.toString(), new WebMetaDataObjectFactory(), null);
+
+ // If we are hot-deployed *after* the overall server is started
+ // we'll never receive Server.START_NOTIFICATION_TYPE, so check
+ // with the Server and start the connectors immediately, if this is the case.
+ // Otherwise register to receive the server start-up notification.
+ Boolean started = (Boolean)server.getAttribute(ServerImplMBean.OBJECT_NAME, "Started");
+ if (started.booleanValue() == true)
+ {
+ log.debug("Server '" + ServerImplMBean.OBJECT_NAME +
+ "' already started, starting connectors now");
+
+ startConnectors();
+ }
+ else
+ {
+ // Register for notification of the overall server startup
+ log.debug("Server '" + ServerImplMBean.OBJECT_NAME +
+ "' not started, registering for start-up notification");
+
+ server.addNotificationListener(ServerImplMBean.OBJECT_NAME, this, null, null);
+ }
+ }
+
+
+ public void stop()
+ throws Exception
+ {
+ MBeanServer server = super.getServer();
+ String objectNameS = catalinaDomain + ":type=server";
+ ObjectName objectName = new ObjectName(objectNameS);
+
+ server.invoke(objectName, "stop", new Object[]{},
+ new String[]{});
+
+ server.invoke(objectName, "destroy", new Object[]{},
+ new String[]{});
+
+ server.unregisterMBean(objectName);
+
+ MBeanServer server2 = server;
+
+ // Unregister any remaining jboss.web or Catalina MBeans
+ ObjectName queryObjectName = new ObjectName
+ (catalinaDomain + ":*");
+ Iterator iterator =
+ server2.queryMBeans(queryObjectName, null).iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ ObjectName toRemove = oi.getObjectName();
+ // Exception: Don't unregister the service right now
+ if (!"WebServer".equals(toRemove.getKeyProperty("service")))
+ {
+ if (server2.isRegistered(toRemove))
+ {
+ server2.unregisterMBean(toRemove);
+ }
+ }
+ }
+ queryObjectName = new ObjectName("Catalina:*");
+ iterator = server2.queryMBeans(queryObjectName, null).iterator();
+ while (iterator.hasNext())
+ {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ ObjectName name = oi.getObjectName();
+ server2.unregisterMBean(name);
+ }
+ super.stop();
+ }
+
+ /**
+ * Start the tomcat service connectors.
+ */
+ public void startConnectors() throws Exception
+ {
+ MBeanServer server = super.getServer();
+ ObjectName service = new ObjectName(catalinaDomain + ":type=Service,serviceName=jboss.web");
+ Object[] args = {};
+ String[] sig = {};
+ Connector[] connectors = (Connector[]) server.invoke(service,
+ "findConnectors", args, sig);
+ for (int n = 0; n < connectors.length; n++)
+ {
+ Lifecycle lc = (Lifecycle) connectors[n];
+ lc.start();
+ }
+ /* TODO:
+ // Notify listeners that connectors have started processing requests
+ sendNotification(new Notification(TOMCAT_CONNECTORS_STARTED,
+ this, getNextNotificationSequenceNumber()));
+ */
+ }
+
+ /**
+ * Stop the tomcat service connectors.
+ */
+ public void stopConnectors() throws Exception
+ {
+ MBeanServer server = super.getServer();
+ ObjectName service = new ObjectName(catalinaDomain + ":type=Service,serviceName=jboss.web");
+ Object[] args = {};
+ String[] sig = {};
+ Connector[] connectors = (Connector[]) server.invoke(service,
+ "findConnectors", args, sig);
+ for (int n = 0; n < connectors.length; n++)
+ {
+ Lifecycle lc = (Lifecycle) connectors[n];
+ lc.stop();
+ }
+ }
+
+ /**
+ * Used to receive notification of the server start msg so the tomcat
+ * connectors can be started after all web apps are deployed.
+ */
+ public void handleNotification(Notification msg, Object handback)
+ {
+ String type = msg.getType();
+ if (type.equals(Server.START_NOTIFICATION_TYPE))
+ {
+ log.debug("Saw " + type + " notification, starting connectors");
+ try
+ {
+ startConnectors();
+ }
+ catch (Exception e)
+ {
+ log.warn("Failed to startConnectors", e);
+ }
+ }
+ }
+
+ /**
+ * A trival extension of URLClassLoader that uses an empty URL[] as its
+ * classpath so that all work is delegated to its parent.
+ */
+ static class ENCLoader extends URLClassLoader
+ {
+ private boolean parentFirst;
+
+ ENCLoader(URL[] urls, ClassLoader parent, boolean parentFirst)
+ {
+ super(urls, parent);
+ this.parentFirst = parentFirst;
+ }
+
+ protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
+ {
+ if (parentFirst) return super.loadClass(name, resolve);
+ try
+ {
+ Class clazz = findClass(name);
+ if (resolve) {
+ resolveClass(clazz);
+ }
+ return clazz;
+ }
+ catch (ClassNotFoundException e)
+ {
+ return getParent().loadClass(name);
+ }
+ }
+
+ }
+
+ public static ClassLoader getTmpLoader(URL warURL, ClassLoader parent, boolean parentFirst) throws Exception
+ {
+
+ String path = warURL.getFile();
+ ArrayList<URL> list = new ArrayList<URL>();
+ File classesDir = new File(path, "WEB-INF/classes");
+ if (classesDir.exists())
+ {
+ list.add(classesDir.toURL());
+ }
+ File libDir = new File(path, "WEB-INF/lib");
+ if (libDir.exists())
+ {
+ File[] jars = libDir.listFiles();
+ int length = jars != null ? jars.length : 0;
+ for (int j = 0; j < length; j++)
+ {
+ File jar = jars[j];
+ if(jar.getAbsolutePath().endsWith(".jar"))
+ {
+ list.add(jar.toURL());
+ }
+ }
+ }
+ ENCLoader loader = new ENCLoader(list.toArray(new URL[list.size()]), parent, parentFirst);
+ return loader;
+
+ }
+
+ /**
+ * Create a tomcat war deployment bean for the deployment unit/metaData.
+ * @param unit - the current web app deployment unit
+ * @param metaData - the parsed metdata for the web app deployment
+ * @return TomcatDeployment instnace
+ */
+ @Override
+ public AbstractWarDeployment getDeployment(DeploymentUnit unit, WebMetaData metaData)
+ throws Exception
+ {
+ AbstractWarDeployment deployment = (AbstractWarDeployment) deploymentClass.newInstance();
+
+ DeployerConfig config = new DeployerConfig();
+ config.setDefaultSecurityDomain(this.defaultSecurityDomain);
+ config.setSubjectAttributeName(this.subjectAttributeName);
+ config.setServiceClassLoader(getClass().getClassLoader());
+ config.setManagerClass(this.managerClass);
+ config.setJava2ClassLoadingCompliance(this.java2ClassLoadingCompliance);
+ config.setUnpackWars(this.unpackWars);
+ config.setLenientEjbLink(this.lenientEjbLink);
+ config.setCatalinaDomain(catalinaDomain);
+ config.setContextClassName(contextClassName);
+ config.setServiceName(null);
+ config.setSnapshotInterval(this.snapshotInterval);
+ config.setSnapshotMode(this.snapshotMode);
+ config.setUseLocalCache(this.useLocalCache);
+ config.setUseJK(this.useJK);
+ config.setSubjectAttributeName(this.subjectAttributeName);
+ config.setUseJBossWebLoader(this.useJBossWebLoader);
+ config.setAllowSelfPrivilegedWebApps(this.allowSelfPrivilegedWebApps);
+ config.setSecurityManagerService(this.secMgrService);
+ config.setFilteredPackages(filteredPackages);
+ config.setSharedMetaData(sharedMetaData);
+
+ //Check if there are any xacml policy files
+ VirtualFile vf = unit.getMetaDataFile("jboss-xacml-policy.xml");
+ if(vf != null)
+ config.setXacmlPolicyURL(vf.toURL());
+
+ deployment.setServer(super.getServer());
+ deployment.init(config);
+
+ return deployment;
+ }
+
+ public void create() throws Exception
+ {
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ if (server != null)
+ server.registerMBean(this, OBJECT_NAME);
+ }
+
+ public void destroy() throws Exception
+ {
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ if (server != null)
+ server.unregisterMBean(OBJECT_NAME);
+ }
+}
\ No newline at end of file
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployer.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,223 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.deployers;
+
+import javax.management.ObjectName;
+
+import org.jboss.mx.util.ObjectNameFactory;
+import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
+import org.jboss.web.AbstractWebContainerMBean;
+import org.jboss.web.deployers.AbstractWarDeployerMBean;
+
+/**
+ * An implementation of the AbstractWebContainer for the Jakarta Tomcat5
+ * servlet container. It has no code dependency on tomcat - only the new JMX
+ * model is used.
+ * <p/>
+ * Tomcat5 is organized as a set of mbeans - just like jboss.
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @version $Revision: 45726 $
+ * @see AbstractWebContainerMBean
+ */
+public interface TomcatDeployerMBean extends AbstractWarDeployerMBean
+{
+ // default object name
+ public static final ObjectName OBJECT_NAME = ObjectNameFactory.create("jboss.web:service=WebServer");
+
+ /** JMX notification type to signal after-start connector event */
+ public final String TOMCAT_CONNECTORS_STARTED = "jboss.tomcat.connectors.started";
+
+ /**
+ * @return the jmx domain for the tomcat management mbeans
+ */
+ public String getDomain();
+
+ /**
+ * The most important attribute - defines the managed domain.
+ * A catalina instance (engine) corresponds to a JMX domain, that's
+ * how we know where to deploy webapps.
+ *
+ * @param domainName the jmx domain under which tc registers
+ */
+ public void setDomain(String domainName);
+
+ /**
+ * Set the snapshot mode in a clustered environment
+ */
+ public void setSnapshotMode(String mode);
+
+ /**
+ * Get the snapshot mode in a clustered environment
+ */
+ public String getSnapshotMode();
+
+ /**
+ * Set the snapshot interval in ms for the interval snapshot mode
+ */
+ public void setSnapshotInterval(int interval);
+
+ /**
+ * Get the snapshot interval
+ */
+ public int getSnapshotInterval();
+
+ /**
+ * Get the clustering code cache behaviour
+ */
+ public boolean isUseLocalCache();
+
+ /**
+ * Set the clustering code cache behaviour
+ */
+ public void setUseLocalCache(boolean useLocalCache);
+
+ /**
+ * Get the clustering code failover behaviour whether MOD_JK(2) is used or not.
+ */
+ public boolean isUseJK();
+
+ /**
+ * Set the clustering code failover behaviour whether MOD_JK(2) is used or not.
+ */
+ public void setUseJK(boolean useJK);
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public void setSessionIdAlphabet(String sessionIdAlphabet);
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public String getSessionIdAlphabet();
+
+ /**
+ * Gets the JMX object name of a shared TreeCache to be used for clustered
+ * single-sign-on.
+ *
+ * @see org.jboss.web.tomcat.service.sso.TreeCacheSSOClusterManager
+ */
+ public String getCacheName();
+
+ /**
+ * Gets the JMX object name of a shared TreeCache to be used for clustered
+ * single-sign-on.
+ * <p/>
+ * <b>NOTE:</b> TreeCache must be deployed before this service.
+ *
+ * @see org.jboss.web.tomcat.service.sso.TreeCacheSSOClusterManager
+ */
+ public void setCacheName(String cacheName);
+
+ /**
+ * Get the JBoss UCL use flag
+ */
+ public boolean getUseJBossWebLoader();
+
+ /**
+ * Set the JBoss UCL use flag
+ */
+ public void setUseJBossWebLoader(boolean flag);
+
+ public String getManagerClass();
+
+ public void setManagerClass(String managerClass);
+
+ /** */
+ public String getContextMBeanCode();
+
+ /** */
+ public void setContextMBeanCode(String className);
+
+ /**
+ * Get the name of the external tomcat server configuration file.
+ *
+ * @return the config file name, server.xml for example
+ */
+ public String getConfigFile();
+
+ /**
+ * Set the name of the external tomcat server configuration file.
+ *
+ * @param configFile - the config file name, server.xml for example
+ */
+ public void setConfigFile(String configFile);
+
+ /**
+ * Get the request attribute name under which the JAAS Subject is store
+ */
+ public String getSubjectAttributeName();
+
+ /**
+ * Set the request attribute name under which the JAAS Subject will be
+ * stored when running with a security mgr that supports JAAS. If this is
+ * empty then the Subject will not be store in the request.
+ *
+ * @param name the HttpServletRequest attribute name to store the Subject
+ */
+ public void setSubjectAttributeName(String name);
+
+ /**
+ * Start all connectors of the Domain + ":type=Service,serviceName=jboss.web"
+ * service.
+ *
+ * @throws Exception
+ */
+ public void startConnectors() throws Exception;
+
+ /**
+ * Stop all connectors of the Domain + ":type=Service,serviceName=jboss.web"
+ * service.
+ *
+ * @throws Exception
+ */
+ public void stopConnectors() throws Exception;
+
+ /**
+ * Get whether web-apps are able to control the privileged flag
+ */
+ public boolean isAllowSelfPrivilegedWebApps();
+
+ /**
+ * Set whether web-apps are able to control the privileged flag
+ */
+ public void setAllowSelfPrivilegedWebApps(boolean flag);
+
+ /** Set the SecurityManagerService binding. This is used to flush any
+ * associated authentication cache on session invalidation.
+ * @param mgr the JaasSecurityManagerServiceMBean
+ */
+ public void setSecurityManagerService(JaasSecurityManagerServiceMBean mgr);
+
+ /**
+ *
+ * @return
+ */
+ public String[] getFilteredPackages();
+ /**
+ *
+ * @param pkgs
+ */
+ public void setFilteredPackages(String[] pkgs);
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployerMBean.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,702 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.deployers;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.security.CodeSource;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.LinkRef;
+
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.core.StandardContext;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.metadata.serviceref.ServiceRefDelegate;
+import org.jboss.metadata.serviceref.VirtualFileAdaptor;
+import org.jboss.metamodel.descriptor.EnvironmentRefGroup;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.naming.NonSerializableFactory;
+import org.jboss.security.AuthorizationManager;
+import org.jboss.security.authorization.PolicyRegistration;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.WebApplication;
+import org.jboss.web.deployers.AbstractWarDeployment;
+import org.jboss.web.tomcat.security.JaccContextValve;
+import org.jboss.web.tomcat.security.RunAsListener;
+import org.jboss.web.tomcat.security.SecurityAssociationValve;
+import org.jboss.web.tomcat.service.TomcatInjectionContainer;
+import org.jboss.web.tomcat.service.WebAppLoader;
+import org.jboss.web.tomcat.service.WebCtxLoader;
+import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+import org.jboss.web.tomcat.service.session.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.JBossCacheManager;
+import org.jboss.ws.integration.ServiceRefMetaData;
+import org.jboss.ws.integration.UnifiedVirtualFile;
+import org.omg.CORBA.ORB;
+
+/**
+ * A tomcat web application deployment.
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @version $Revision: 56605 $
+ */
+public class TomcatDeployment extends AbstractWarDeployment
+{
+ private static final Logger log = Logger.getLogger(TomcatDeployment.class);
+
+ /**
+ * The name of the war level context configuration descriptor
+ */
+ private static final String CONTEXT_CONFIG_FILE = "WEB-INF/context.xml";
+
+ private DeployerConfig config;
+ private String[] javaVMs = { " jboss.management.local:J2EEServer=Local,j2eeType=JVM,name=localhost" };
+ private String serverName = "jboss";
+ private HashMap vhostToHostNames = new HashMap();
+ private ORB orb = null;
+ private TomcatInjectionContainer injectionContainer;
+
+ public ORB getORB()
+ {
+ return orb;
+ }
+
+ public void setORB(ORB orb)
+ {
+ this.orb = orb;
+ }
+
+ @Override
+ public void init(Object containerConfig) throws Exception
+ {
+ this.config = (DeployerConfig)containerConfig;
+ super.setJava2ClassLoadingCompliance(config.isJava2ClassLoadingCompliance());
+ super.setUnpackWars(config.isUnpackWars());
+ super.setLenientEjbLink(config.isLenientEjbLink());
+ super.setDefaultSecurityDomain(config.getDefaultSecurityDomain());
+ }
+
+ @Override
+ protected void performDeploy(WebApplication webApp, String warUrl) throws Exception
+ {
+ // Decode the URL as tomcat can't deal with paths with escape chars
+ warUrl = URLDecoder.decode(warUrl, "UTF-8");
+ webApp.setDomain(config.getCatalinaDomain());
+ WebMetaData metaData = webApp.getMetaData();
+ String hostName = null;
+ // Get any jboss-web/virtual-hosts
+ Iterator vhostNames = metaData.getVirtualHosts();
+ // Map the virtual hosts onto the configured hosts
+ Iterator hostNames = mapVirtualHosts(vhostNames);
+ if (hostNames.hasNext())
+ {
+ hostName = hostNames.next().toString();
+ }
+ performDeployInternal(hostName, webApp, warUrl);
+ while (hostNames.hasNext())
+ {
+ String additionalHostName = hostNames.next().toString();
+ performDeployInternal(additionalHostName, webApp, warUrl);
+ }
+ }
+
+ protected void performDeployInternal(String hostName, WebApplication webApp, String warUrl) throws Exception
+ {
+
+ WebMetaData metaData = webApp.getMetaData();
+ String ctxPath = metaData.getContextRoot();
+ if (ctxPath.equals("/") || ctxPath.equals("/ROOT") || ctxPath.equals(""))
+ {
+ log.debug("deploy root context=" + ctxPath);
+ ctxPath = "/";
+ metaData.setContextRoot(ctxPath);
+ }
+
+ log.info("deploy, ctxPath=" + ctxPath + ", vfsUrl=" + webApp.getDeploymentUnit().getFile("").getPathName());
+
+ URL url = new URL(warUrl);
+
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ metaData.setContextLoader(loader);
+
+ injectionContainer = new TomcatInjectionContainer(webApp, webApp.getDeploymentUnit());
+
+ Loader webLoader = webApp.getDeploymentUnit().getAttachment(Loader.class);
+ if (webLoader == null)
+ webLoader = getWebLoader(webApp.getDeploymentUnit(), loader, url);
+
+ webApp.setName(url.getPath());
+ webApp.setClassLoader(loader);
+ webApp.setURL(url);
+
+ String objectNameS = config.getCatalinaDomain() + ":j2eeType=WebModule,name=//" + ((hostName == null) ? "localhost" : hostName) + ctxPath
+ + ",J2EEApplication=none,J2EEServer=none";
+
+ ObjectName objectName = new ObjectName(objectNameS);
+
+ StandardContext context = (StandardContext)Class.forName(config.getContextClassName()).newInstance();
+ Registry.getRegistry().registerComponent(context, objectName, config.getContextClassName());
+
+ // Find and set warInfo file on the context
+ // If WAR is packed, expand warInfo file to temp folder
+ String ctxConfig = null;
+ try
+ {
+ // TODO: this should be input metadata
+ ctxConfig = findConfig(url);
+ }
+ catch (IOException e)
+ {
+ log.debug("No " + CONTEXT_CONFIG_FILE + " in " + url, e);
+ }
+
+ VFSDirContext resources = new VFSDirContext();
+ resources.setVirtualFile(webApp.getDeploymentUnit().getFile(""));
+ context.setResources(resources);
+
+ context.setAnnotationProcessor(injectionContainer);
+ context.setDocBase(url.getFile());
+ context.setConfigFile(ctxConfig);
+ context.setDefaultContextXml("context.xml");
+ context.setDefaultWebXml("conf/web.xml");
+ // If there is an alt-dd set it
+ if (metaData.getAltDDPath() != null)
+ {
+ log.debug("Setting altDDName to: " + metaData.getAltDDPath());
+ context.setAltDDName(metaData.getAltDDPath());
+ }
+ context.setJavaVMs(javaVMs);
+ context.setServer(serverName);
+ context.setSaveConfig(false);
+
+ if (webLoader != null)
+ {
+ context.setLoader(webLoader);
+ }
+ else
+ {
+ context.setParentClassLoader(loader);
+ }
+ context.setDelegate(webApp.getJava2ClassLoadingCompliance());
+
+ String[] jspCP = getCompileClasspath(loader);
+ StringBuffer classpath = new StringBuffer();
+ for (int u = 0; u < jspCP.length; u++)
+ {
+ String repository = jspCP[u];
+ if (repository == null)
+ continue;
+ if (repository.startsWith("file://"))
+ repository = repository.substring(7);
+ else if (repository.startsWith("file:"))
+ repository = repository.substring(5);
+ else continue;
+ if (repository == null)
+ continue;
+ // ok it is a file. Make sure that is is a directory or jar file
+ File fp = new File(repository);
+ if (!fp.isDirectory())
+ {
+ // if it is not a directory, try to open it as a zipfile.
+ try
+ {
+ // avoid opening .xml files
+ if (fp.getName().toLowerCase().endsWith(".xml"))
+ continue;
+
+ ZipFile zip = new ZipFile(fp);
+ zip.close();
+ }
+ catch (IOException e)
+ {
+ continue;
+ }
+
+ }
+ if (u > 0)
+ classpath.append(File.pathSeparator);
+ classpath.append(repository);
+ }
+
+ context.setCompilerClasspath(classpath.toString());
+
+ // Set the session cookies flag according to metadata
+ switch (metaData.getSessionCookies())
+ {
+ case WebMetaData.SESSION_COOKIES_ENABLED:
+ context.setCookies(true);
+ log.debug("Enabling session cookies");
+ break;
+ case WebMetaData.SESSION_COOKIES_DISABLED:
+ context.setCookies(false);
+ log.debug("Disabling session cookies");
+ break;
+ default:
+ log.debug("Using session cookies default setting");
+ }
+
+ // Add a valve to estalish the JACC context before authorization valves
+ Certificate[] certs = null;
+ CodeSource cs = new CodeSource(url, certs);
+ JaccContextValve jaccValve = new JaccContextValve(metaData, cs);
+ context.addValve(jaccValve);
+
+ // Set listener
+ context.setConfigClass("org.jboss.web.tomcat.service.deployers.JBossContextConfig");
+ context.addLifecycleListener(new EncListener(loader, webLoader, webApp));
+
+ // Pass the metadata to the RunAsListener via a thread local
+ RunAsListener.metaDataLocal.set(metaData);
+ JBossContextConfig.metaDataLocal.set(metaData);
+ JBossContextConfig.metaDataShared.set(config.getSharedMetaData());
+ try
+ {
+ // Init the container; this will also start it
+ context.init();
+ // Start it
+ context.start();
+ // Build the ENC
+ }
+ finally
+ {
+ RunAsListener.metaDataLocal.set(null);
+ JBossContextConfig.metaDataLocal.set(null);
+ JBossContextConfig.metaDataShared.set(null);
+ }
+
+ // Clustering
+ if (metaData.getDistributable())
+ {
+ // Try to initate clustering, fallback to standard if no clustering is
+ // available
+ try
+ {
+ AbstractJBossManager manager = null;
+ String managerClassName = config.getManagerClass();
+ Class managerClass = Thread.currentThread().getContextClassLoader().loadClass(managerClassName);
+ manager = (AbstractJBossManager)managerClass.newInstance();
+ String name = "//" + ((hostName == null) ? "localhost" : hostName) + ctxPath;
+ manager.init(name, metaData, config.isUseJK(), config.isUseLocalCache());
+
+ if (manager instanceof JBossCacheManager)
+ {
+ String snapshotMode = config.getSnapshotMode();
+ int snapshotInterval = config.getSnapshotInterval();
+ JBossCacheManager jbcm = (JBossCacheManager)manager;
+ jbcm.setSnapshotMode(snapshotMode);
+ jbcm.setSnapshotInterval(snapshotInterval);
+ }
+
+ server.setAttribute(objectName, new Attribute("manager", manager));
+
+ log.debug("Enabled clustering support for ctxPath=" + ctxPath);
+ }
+ catch (ClusteringNotSupportedException e)
+ {
+ // JBAS-3513 Just log a WARN, not an ERROR
+ log.warn("Failed to setup clustering, clustering disabled. ClusteringNotSupportedException: " + e.getMessage());
+ }
+ catch (NoClassDefFoundError ncdf)
+ {
+ // JBAS-3513 Just log a WARN, not an ERROR
+ log.debug("Classes needed for clustered webapp unavailable", ncdf);
+ log.warn("Failed to setup clustering, clustering disabled. NoClassDefFoundError: " + ncdf.getMessage());
+ }
+ catch (Throwable t)
+ {
+ // TODO consider letting this through and fail the deployment
+ log.error("Failed to setup clustering, clustering disabled. Exception: ", t);
+ }
+ }
+
+ /*
+ * Add security association valve after the authorization valves so that
+ * the authenticated user may be associated with the request
+ * thread/session.
+ */
+ SecurityAssociationValve valve = new SecurityAssociationValve(metaData, config.getSecurityManagerService());
+ valve.setSubjectAttributeName(config.getSubjectAttributeName());
+ server.invoke(objectName, "addValve", new Object[] { valve }, new String[] { "org.apache.catalina.Valve" });
+
+ /* TODO: Retrieve the state, and throw an exception in case of a failure
+ Integer state = (Integer) server.getAttribute(objectName, "state");
+ if (state.intValue() != 1)
+ {
+ throw new DeploymentException("URL " + warUrl + " deployment failed");
+ }
+ */
+
+ webApp.setAppData(objectName);
+
+ /*
+ * TODO: Create mbeans for the servlets ObjectName servletQuery = new
+ * ObjectName (config.getCatalinaDomain() + ":j2eeType=Servlet,WebModule=" +
+ * objectName.getKeyProperty("name") + ",*"); Iterator iterator =
+ * server.queryMBeans(servletQuery, null).iterator(); while
+ * (iterator.hasNext()) {
+ * di.mbeans.add(((ObjectInstance)iterator.next()).getObjectName()); }
+ */
+
+ if (metaData.getSecurityDomain() != null)
+ {
+ String secDomain = org.jboss.security.Util.unprefixSecurityDomain(metaData.getSecurityDomain());
+ // Associate the Context Id with the Security Domain
+ String contextID = metaData.getJaccContextID();
+
+ //Check if an xacml policy file is present
+ URL xacmlPolicyFile = this.config.getXacmlPolicyURL();
+ if (xacmlPolicyFile != null)
+ {
+ AuthorizationManager authzmgr = org.jboss.security.Util.getAuthorizationManager(secDomain);
+ if (authzmgr instanceof PolicyRegistration)
+ {
+ PolicyRegistration xam = (PolicyRegistration)authzmgr;
+ xam.registerPolicy(contextID, xacmlPolicyFile);
+ }
+ }
+ }
+
+ log.debug("Initialized: " + webApp + " " + objectName);
+ }
+
+ public class EncListener implements LifecycleListener
+ {
+ protected ClassLoader loader;
+ protected Loader webLoader;
+ protected WebApplication webApp;
+ protected WebMetaData metaData;
+ protected DeploymentUnit unit;
+
+ public EncListener(ClassLoader loader, Loader webLoader, WebApplication webApp)
+ {
+ this.loader = loader;
+ this.webLoader = webLoader;
+ this.webApp = webApp;
+ this.metaData = webApp.getMetaData();
+ this.unit = webApp.getDeploymentUnit();
+ }
+
+ public void lifecycleEvent(LifecycleEvent event)
+ {
+
+ if (event.getType().equals(StandardContext.AFTER_START_EVENT))
+ {
+
+ // make the context class loader known to the WebMetaData, ws4ee needs it
+ // to instanciate service endpoint pojos that live in this webapp
+ metaData.setContextLoader(webLoader.getClassLoader());
+
+ Thread currentThread = Thread.currentThread();
+ ClassLoader currentLoader = currentThread.getContextClassLoader();
+ try
+ {
+ // Create a java:comp/env environment unique for the web application
+ log.debug("Creating ENC using ClassLoader: " + loader);
+ ClassLoader parent = loader.getParent();
+ while (parent != null)
+ {
+ log.debug(".." + parent);
+ parent = parent.getParent();
+ }
+ // TODO: The enc should be an input?
+ currentThread.setContextClassLoader(webLoader.getClassLoader());
+ metaData.setENCLoader(webLoader.getClassLoader());
+ InitialContext iniCtx = new InitialContext();
+ Context envCtx = (Context)iniCtx.lookup("java:comp");
+ // Add ORB/UserTransaction
+ ORB orb = null;
+ try
+ {
+ ObjectName ORB_NAME = new ObjectName("jboss:service=CorbaORB");
+ orb = (ORB)server.getAttribute(ORB_NAME, "ORB");
+ // Bind the orb
+ if (orb != null)
+ {
+ NonSerializableFactory.rebind(envCtx, "ORB", orb);
+ log.debug("Bound java:comp/ORB");
+ }
+ }
+ catch (Throwable t)
+ {
+ log.debug("Unable to retrieve orb", t);
+ }
+
+ // TODO: injection, Add a link to the global transaction manager
+ envCtx.bind("UserTransaction", new LinkRef("UserTransaction"));
+ log.debug("Linked java:comp/UserTransaction to JNDI name: UserTransaction");
+ envCtx = envCtx.createSubcontext("env");
+ injectionContainer.populateEnc(webLoader.getClassLoader());
+
+ // TODO: this should be bindings in the metadata
+ currentThread.setContextClassLoader(webLoader.getClassLoader());
+ String securityDomain = metaData.getSecurityDomain();
+ log.debug("linkSecurityDomain");
+ linkSecurityDomain(securityDomain, envCtx);
+
+ // Bind <service-ref> elements
+ UnifiedVirtualFile vfsRoot = new VirtualFileAdaptor(unit.getDeploymentContext().getRoot());
+ EnvironmentRefGroup envRefGroup = injectionContainer.getEnvironmentRefGroup();
+ for (ServiceRefMetaData sref : metaData.getServiceRefs().values())
+ {
+ String refName = sref.getServiceRefName();
+ ServiceRefMetaData injectedRef = envRefGroup.getServiceRef(refName);
+ if (injectedRef == null || injectedRef.isProcessed() == false)
+ {
+ new ServiceRefDelegate().bindServiceRef(envCtx, refName, vfsRoot, sref);
+ }
+ }
+ }
+ catch (Throwable t)
+ {
+ log.error("ENC setup failed", t);
+ }
+ finally
+ {
+ currentThread.setContextClassLoader(currentLoader);
+ }
+
+ log.debug("injectionContainer enabled and processing beginning");
+ // we need to do this because the classloader is initialize by the web container and
+ // the injection container needs the classloader so that it can build up Injectors and ENC populators
+ injectionContainer.setClassLoader(webLoader.getClassLoader());
+ injectionContainer.processMetadata();
+
+ }
+ }
+
+ }
+
+ public Loader getWebLoader(DeploymentUnit unit, ClassLoader loader, URL url) throws MalformedURLException
+ {
+ Loader webLoader = null;
+
+ /*
+ * If we are using the jboss class loader we need to augment its path to
+ * include the WEB-INF/{lib,classes} dirs or else scoped class loading
+ * does not see the war level overrides. The call to setWarURL adds these
+ * paths to the deployment UCL.
+ */
+ ArrayList<URL> classpath = (ArrayList<URL>)unit.getAttachment("org.jboss.web.expandedWarClasspath");
+ if (classpath == null)
+ {
+ try
+ {
+ VirtualFile classes = unit.getFile("WEB-INF/classes");
+ // Tomcat can't handle the vfs urls yet
+ URL vfsURL = classes.toURL();
+ String vfsurl = vfsURL.toString();
+ if (vfsurl.startsWith("vfs"))
+ vfsURL = new URL(vfsurl.substring(3));
+ classpath = new ArrayList<URL>();
+ classpath.add(classes.toURL());
+ }
+ catch (Exception e)
+ {
+
+ }
+ }
+
+ if (config.isUseJBossWebLoader())
+ {
+ WebCtxLoader jbossLoader = new WebCtxLoader(loader, injectionContainer);
+ if (classpath != null)
+ jbossLoader.setClasspath(classpath);
+ webLoader = jbossLoader;
+ }
+ else
+ {
+ String[] pkgs = config.getFilteredPackages();
+ WebAppLoader jbossLoader = new WebAppLoader(loader, pkgs, injectionContainer);
+ jbossLoader.setDelegate(getJava2ClassLoadingCompliance());
+ if (classpath != null)
+ jbossLoader.setClasspath(classpath);
+ webLoader = jbossLoader;
+ }
+ return webLoader;
+ }
+
+ public void setInjectionContainer(TomcatInjectionContainer container)
+ {
+ this.injectionContainer = container;
+ }
+
+ /**
+ * Called as part of the undeploy() method template to ask the subclass for
+ * perform the web container specific undeployment steps.
+ */
+ protected void performUndeploy(WebApplication warInfo, String warUrl) throws Exception
+ {
+ if (warInfo == null)
+ {
+ log.debug("performUndeploy, no WebApplication found for URL " + warUrl);
+ return;
+ }
+
+ log.info("undeploy, ctxPath=" + warInfo.getMetaData().getContextRoot() + ", vfsUrl=" + warInfo.getDeploymentUnit().getFile("").getPathName());
+
+ WebMetaData metaData = warInfo.getMetaData();
+ String hostName = null;
+ Iterator hostNames = metaData.getVirtualHosts();
+ if (hostNames.hasNext())
+ {
+ hostName = hostNames.next().toString();
+ }
+ performUndeployInternal(hostName, warUrl, warInfo);
+ while (hostNames.hasNext())
+ {
+ String additionalHostName = hostNames.next().toString();
+ performUndeployInternal(additionalHostName, warUrl, warInfo);
+ }
+
+ }
+
+ protected void performUndeployInternal(String hostName, String warUrl, WebApplication warInfo) throws Exception
+ {
+
+ WebMetaData metaData = warInfo.getMetaData();
+ String ctxPath = metaData.getContextRoot();
+
+ // TODO: Need to remove the dependency on MBeanServer
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ // If the server is gone, all apps were stopped already
+ if (server == null)
+ return;
+
+ ObjectName objectName = new ObjectName(config.getCatalinaDomain() + ":j2eeType=WebModule,name=//" + ((hostName == null) ? "localhost" : hostName) + ctxPath
+ + ",J2EEApplication=none,J2EEServer=none");
+
+ if (server.isRegistered(objectName))
+ {
+ // Contexts should be stopped by the host already
+ server.invoke(objectName, "destroy", new Object[] {}, new String[] {});
+ }
+
+ }
+
+ /**
+ * Resolve the input virtual host names to the names of the configured Hosts
+ * @param vhostNames Iterator<String> for the jboss-web/virtual-host elements
+ * @return Iterator<String> of the unique Host names
+ * @throws Exception
+ */
+ protected synchronized Iterator mapVirtualHosts(Iterator vhostNames) throws Exception
+ {
+ if (vhostToHostNames.size() == 0)
+ {
+ // Query the configured Host mbeans
+ String hostQuery = config.getCatalinaDomain() + ":type=Host,*";
+ ObjectName query = new ObjectName(hostQuery);
+ Set hosts = server.queryNames(query, null);
+ Iterator iter = hosts.iterator();
+ while (iter.hasNext())
+ {
+ ObjectName host = (ObjectName)iter.next();
+ String name = host.getKeyProperty("host");
+ if (name != null)
+ {
+ vhostToHostNames.put(name, name);
+ String[] aliases = (String[])server.invoke(host, "findAliases", null, null);
+ int count = aliases != null ? aliases.length : 0;
+ for (int n = 0; n < count; n++)
+ {
+ vhostToHostNames.put(aliases[n], name);
+ }
+ }
+ }
+ }
+
+ // Map the virtual host names to the hosts
+ HashSet hosts = new HashSet();
+ while (vhostNames.hasNext())
+ {
+ String vhost = (String)vhostNames.next();
+ String host = (String)vhostToHostNames.get(vhost);
+ if (host == null)
+ {
+ log.warn("Failed to map vhost: " + vhost);
+ // This will cause a new host to be created
+ host = vhost;
+ }
+ hosts.add(host);
+ }
+ return hosts.iterator();
+ }
+
+ private String findConfig(URL warURL) throws IOException
+ {
+ String result = null;
+ // See if the warUrl is a dir or a file
+ File warFile = new File(warURL.getFile());
+ if (warURL.getProtocol().equals("file") && warFile.isDirectory() == true)
+ {
+ File webDD = new File(warFile, CONTEXT_CONFIG_FILE);
+ if (webDD.exists() == true)
+ result = webDD.getAbsolutePath();
+ }
+ else
+ {
+ ZipFile zipFile = new ZipFile(warFile);
+ ZipEntry entry = zipFile.getEntry(CONTEXT_CONFIG_FILE);
+ if (entry != null)
+ {
+ InputStream zipIS = zipFile.getInputStream(entry);
+ byte[] buffer = new byte[512];
+ int bytes;
+ result = warFile.getAbsolutePath() + "-context.xml";
+ FileOutputStream fos = new FileOutputStream(result);
+ while ((bytes = zipIS.read(buffer)) > 0)
+ {
+ fos.write(buffer, 0, bytes);
+ }
+ zipIS.close();
+ fos.close();
+ }
+ zipFile.close();
+ }
+ return result;
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,670 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.deployers;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.security.CodeSource;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.LinkRef;
+
+import org.apache.catalina.Loader;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.metadata.serviceref.ServiceRefDelegate;
+import org.jboss.metadata.serviceref.VirtualFileAdaptor;
+import org.jboss.metamodel.descriptor.EnvironmentRefGroup;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.naming.NonSerializableFactory;
+import org.jboss.security.AuthorizationManager;
+import org.jboss.security.authorization.PolicyRegistration;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.WebApplication;
+import org.jboss.web.deployers.AbstractWarDeployment;
+import org.jboss.web.tomcat.security.JaccContextValve;
+import org.jboss.web.tomcat.security.RunAsListener;
+import org.jboss.web.tomcat.security.SecurityAssociationValve;
+import org.jboss.web.tomcat.service.TomcatInjectionContainer;
+import org.jboss.web.tomcat.service.WebAppLoader;
+import org.jboss.web.tomcat.service.WebCtxLoader;
+import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+import org.jboss.web.tomcat.service.session.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.JBossCacheManager;
+import org.jboss.ws.integration.ServiceRefMetaData;
+import org.jboss.ws.integration.UnifiedVirtualFile;
+import org.omg.CORBA.ORB;
+
+/**
+ * A tomcat web application deployment.
+ *
+ * @author Scott.Stark at jboss.org
+ * @author Costin Manolache
+ * @version $Revision: 56605 $
+ */
+public class TomcatDeployment2 extends AbstractWarDeployment
+{
+ private static final Logger log = Logger.getLogger(TomcatDeployment2.class);
+
+ /**
+ * The name of the war level context configuration descriptor
+ */
+ private static final String CONTEXT_CONFIG_FILE = "WEB-INF/context.xml";
+
+ private DeployerConfig config;
+ private String[] javaVMs = { " jboss.management.local:J2EEServer=Local,j2eeType=JVM,name=localhost" };
+ private String serverName = "jboss";
+ private HashMap vhostToHostNames = new HashMap();
+ private ORB orb = null;
+ private TomcatInjectionContainer injectionContainer;
+
+ public ORB getORB()
+ {
+ return orb;
+ }
+
+ public void setORB(ORB orb)
+ {
+ this.orb = orb;
+ }
+
+ @Override
+ public void init(Object containerConfig) throws Exception
+ {
+ this.config = (DeployerConfig)containerConfig;
+ super.setJava2ClassLoadingCompliance(config.isJava2ClassLoadingCompliance());
+ super.setUnpackWars(config.isUnpackWars());
+ super.setLenientEjbLink(config.isLenientEjbLink());
+ super.setDefaultSecurityDomain(config.getDefaultSecurityDomain());
+ }
+
+ @Override
+ protected void performDeploy(WebApplication webApp, String warUrl) throws Exception
+ {
+ // Decode the URL as tomcat can't deal with paths with escape chars
+ warUrl = URLDecoder.decode(warUrl, "UTF-8");
+ webApp.setDomain(config.getCatalinaDomain());
+ WebMetaData metaData = webApp.getMetaData();
+ String hostName = null;
+ // Get any jboss-web/virtual-hosts
+ Iterator vhostNames = metaData.getVirtualHosts();
+ // Map the virtual hosts onto the configured hosts
+ Iterator hostNames = mapVirtualHosts(vhostNames);
+ if (hostNames.hasNext())
+ {
+ hostName = hostNames.next().toString();
+ }
+ performDeployInternal(hostName, webApp, warUrl);
+ while (hostNames.hasNext())
+ {
+ String additionalHostName = hostNames.next().toString();
+ performDeployInternal(additionalHostName, webApp, warUrl);
+ }
+ }
+
+ protected void performDeployInternal(String hostName, WebApplication webApp, String warUrl) throws Exception
+ {
+ DeploymentUnit unit = webApp.getDeploymentUnit();
+ WebMetaData metaData = webApp.getMetaData();
+ String ctxPath = metaData.getContextRoot();
+ if (ctxPath.equals("/") || ctxPath.equals("/ROOT") || ctxPath.equals(""))
+ {
+ log.debug("deploy root context=" + ctxPath);
+ ctxPath = "/";
+ metaData.setContextRoot(ctxPath);
+ }
+
+ log.info("deploy, ctxPath=" + ctxPath + ", warUrl=" + shortWarUrlFromServerHome(warUrl));
+
+ URL url = new URL(warUrl);
+
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ metaData.setContextLoader(loader);
+
+ injectionContainer = new TomcatInjectionContainer(webApp, webApp.getDeploymentUnit());
+
+ Loader webLoader = webApp.getDeploymentUnit().getAttachment(Loader.class);
+ if (webLoader == null)
+ webLoader = getWebLoader(webApp.getDeploymentUnit(), loader, url);
+
+ webApp.setName(url.getPath());
+ webApp.setClassLoader(loader);
+ webApp.setURL(url);
+
+ String objectNameS = config.getCatalinaDomain() + ":j2eeType=WebModule,name=//" + ((hostName == null) ? "localhost" : hostName) + ctxPath
+ + ",J2EEApplication=none,J2EEServer=none";
+
+ ObjectName objectName = new ObjectName(objectNameS);
+
+ String contextClassName = config.getContextClassName();
+ Registry.getRegistry().registerComponent(Class.forName(contextClassName).newInstance(), objectName, contextClassName);
+
+ // Find and set warInfo file on the context
+ // If WAR is packed, expand warInfo file to temp folder
+ String ctxConfig = null;
+ try
+ {
+ // TODO: this should be input metadata
+ ctxConfig = findConfig(url);
+ }
+ catch (IOException e)
+ {
+ log.debug("No " + CONTEXT_CONFIG_FILE + " in " + url, e);
+ }
+
+ if (injectionContainer != null)
+ {
+ server.setAttribute(objectName, new Attribute("annotationProcessor", injectionContainer));
+ }
+
+ server.setAttribute(objectName, new Attribute("docBase", url.getFile()));
+
+ server.setAttribute(objectName, new Attribute("configFile", ctxConfig));
+
+ server.setAttribute(objectName, new Attribute("defaultContextXml", "context.xml"));
+ server.setAttribute(objectName, new Attribute("defaultWebXml", "conf/web.xml"));
+ // If there is an alt-dd set it
+ if (metaData.getAltDDPath() != null)
+ {
+ String altPath = metaData.getAltDDPath();
+ log.debug("Setting altDDName to: " + altPath);
+ server.setAttribute(objectName, new Attribute("altDDName", altPath));
+ }
+
+ server.setAttribute(objectName, new Attribute("javaVMs", javaVMs));
+
+ server.setAttribute(objectName, new Attribute("server", serverName));
+
+ server.setAttribute(objectName, new Attribute("saveConfig", Boolean.FALSE));
+
+ if (webLoader != null)
+ {
+ server.setAttribute(objectName, new Attribute("loader", webLoader));
+ }
+ else
+ {
+ server.setAttribute(objectName, new Attribute("parentClassLoader", loader));
+ }
+
+ server.setAttribute(objectName, new Attribute("delegate", new Boolean(webApp.getJava2ClassLoadingCompliance())));
+
+ String[] jspCP = getCompileClasspath(loader);
+ StringBuffer classpath = new StringBuffer();
+ for (int u = 0; u < jspCP.length; u++)
+ {
+ String repository = jspCP[u];
+ if (repository == null)
+ continue;
+ if (repository.startsWith("file://"))
+ repository = repository.substring(7);
+ else if (repository.startsWith("file:"))
+ repository = repository.substring(5);
+ else continue;
+ if (repository == null)
+ continue;
+ // ok it is a file. Make sure that is is a directory or jar file
+ File fp = new File(repository);
+ if (!fp.isDirectory())
+ {
+ // if it is not a directory, try to open it as a zipfile.
+ try
+ {
+ // avoid opening .xml files
+ if (fp.getName().toLowerCase().endsWith(".xml"))
+ continue;
+
+ ZipFile zip = new ZipFile(fp);
+ zip.close();
+ }
+ catch (IOException e)
+ {
+ continue;
+ }
+
+ }
+ if (u > 0)
+ classpath.append(File.pathSeparator);
+ classpath.append(repository);
+ }
+
+ server.setAttribute(objectName, new Attribute("compilerClasspath", classpath.toString()));
+
+ // Set the session cookies flag according to metadata
+ switch (metaData.getSessionCookies())
+ {
+ case WebMetaData.SESSION_COOKIES_ENABLED:
+ server.setAttribute(objectName, new Attribute("cookies", new Boolean(true)));
+ log.debug("Enabling session cookies");
+ break;
+ case WebMetaData.SESSION_COOKIES_DISABLED:
+ server.setAttribute(objectName, new Attribute("cookies", new Boolean(false)));
+ log.debug("Disabling session cookies");
+ break;
+ default:
+ log.debug("Using session cookies default setting");
+ }
+
+ // Add a valve to estalish the JACC context before authorization valves
+ Certificate[] certs = null;
+ CodeSource cs = new CodeSource(url, certs);
+ JaccContextValve jaccValve = new JaccContextValve(metaData, cs);
+ server.invoke(objectName, "addValve", new Object[] { jaccValve }, new String[] { "org.apache.catalina.Valve" });
+
+ // Pass the metadata to the RunAsListener via a thread local
+ RunAsListener.metaDataLocal.set(metaData);
+ try
+ {
+ // Init the container; this will also start it
+ server.invoke(objectName, "init", new Object[] {}, new String[] {});
+ }
+ finally
+ {
+ RunAsListener.metaDataLocal.set(null);
+ }
+
+ // make the context class loader known to the WebMetaData, ws4ee needs it
+ // to instanciate service endpoint pojos that live in this webapp
+ Loader ctxLoader = (Loader)server.getAttribute(objectName, "loader");
+ metaData.setContextLoader(ctxLoader.getClassLoader());
+
+ // Start it
+ server.invoke(objectName, "start", new Object[] {}, new String[] {});
+ // Build the ENC
+
+ if (injectionContainer == null)
+ {
+ super.processEnc(webLoader.getClassLoader(), webApp);
+ }
+ else
+ {
+ Thread currentThread = Thread.currentThread();
+ ClassLoader currentLoader = loader;
+ try
+ {
+ // Create a java:comp/env environment unique for the web application
+ log.debug("Creating ENC using ClassLoader: " + loader);
+ ClassLoader parent = loader.getParent();
+ while (parent != null)
+ {
+ log.debug(".." + parent);
+ parent = parent.getParent();
+ }
+ // TODO: The enc should be an input?
+ currentThread.setContextClassLoader(webLoader.getClassLoader());
+ metaData.setENCLoader(webLoader.getClassLoader());
+ InitialContext iniCtx = new InitialContext();
+ Context envCtx = (Context)iniCtx.lookup("java:comp");
+ // Add ORB/UserTransaction
+ ORB orb = null;
+ try
+ {
+ ObjectName ORB_NAME = new ObjectName("jboss:service=CorbaORB");
+ orb = (ORB)server.getAttribute(ORB_NAME, "ORB");
+ // Bind the orb
+ if (orb != null)
+ {
+ NonSerializableFactory.rebind(envCtx, "ORB", orb);
+ log.debug("Bound java:comp/ORB");
+ }
+ }
+ catch (Throwable t)
+ {
+ log.debug("Unable to retrieve orb" + t.toString());
+ }
+
+ // TODO: injection, Add a link to the global transaction manager
+ envCtx.bind("UserTransaction", new LinkRef("UserTransaction"));
+ log.debug("Linked java:comp/UserTransaction to JNDI name: UserTransaction");
+
+ envCtx = envCtx.createSubcontext("env");
+ injectionContainer.populateEnc(webLoader.getClassLoader());
+
+ // TODO: this should be bindings in the metadata
+ currentThread.setContextClassLoader(webLoader.getClassLoader());
+ String securityDomain = metaData.getSecurityDomain();
+ log.debug("linkSecurityDomain");
+ linkSecurityDomain(securityDomain, envCtx);
+
+ // Bind <service-ref> elements
+ UnifiedVirtualFile vfsRoot = new VirtualFileAdaptor(unit.getDeploymentContext().getRoot());
+ EnvironmentRefGroup envRefGroup = injectionContainer.getEnvironmentRefGroup();
+ for (ServiceRefMetaData sref : metaData.getServiceRefs().values())
+ {
+ String refName = sref.getServiceRefName();
+ ServiceRefMetaData injectedRef = envRefGroup.getServiceRef(refName);
+ if (injectedRef == null || injectedRef.isProcessed() == false)
+ {
+ new ServiceRefDelegate().bindServiceRef(envCtx, refName, vfsRoot, sref);
+ }
+ }
+ }
+ finally
+ {
+ currentThread.setContextClassLoader(currentLoader);
+ }
+ }
+
+ // Clustering
+ if (metaData.getDistributable())
+ {
+ // Try to initate clustering, fallback to standard if no clustering is
+ // available
+ try
+ {
+ AbstractJBossManager manager = null;
+ String managerClassName = config.getManagerClass();
+ Class managerClass = Thread.currentThread().getContextClassLoader().loadClass(managerClassName);
+ manager = (AbstractJBossManager)managerClass.newInstance();
+ String name = "//" + ((hostName == null) ? "localhost" : hostName) + ctxPath;
+ manager.init(name, metaData, config.isUseJK(), config.isUseLocalCache());
+
+ if (manager instanceof JBossCacheManager)
+ {
+ String snapshotMode = config.getSnapshotMode();
+ int snapshotInterval = config.getSnapshotInterval();
+ JBossCacheManager jbcm = (JBossCacheManager)manager;
+ jbcm.setSnapshotMode(snapshotMode);
+ jbcm.setSnapshotInterval(snapshotInterval);
+ }
+
+ server.setAttribute(objectName, new Attribute("manager", manager));
+
+ log.debug("Enabled clustering support for ctxPath=" + ctxPath);
+ }
+ catch (ClusteringNotSupportedException e)
+ {
+ // JBAS-3513 Just log a WARN, not an ERROR
+ log.warn("Failed to setup clustering, clustering disabled. ClusteringNotSupportedException: " + e.getMessage());
+ }
+ catch (NoClassDefFoundError ncdf)
+ {
+ // JBAS-3513 Just log a WARN, not an ERROR
+ log.debug("Classes needed for clustered webapp unavailable", ncdf);
+ log.warn("Failed to setup clustering, clustering disabled. NoClassDefFoundError: " + ncdf.getMessage());
+ }
+ catch (Throwable t)
+ {
+ // TODO consider letting this through and fail the deployment
+ log.error("Failed to setup clustering, clustering disabled. Exception: ", t);
+ }
+ }
+
+ /*
+ * Add security association valve after the authorization valves so that
+ * the authenticated user may be associated with the request
+ * thread/session.
+ */
+ SecurityAssociationValve valve = new SecurityAssociationValve(metaData, config.getSecurityManagerService());
+ valve.setSubjectAttributeName(config.getSubjectAttributeName());
+ server.invoke(objectName, "addValve", new Object[] { valve }, new String[] { "org.apache.catalina.Valve" });
+
+ /* TODO: Retrieve the state, and throw an exception in case of a failure
+ Integer state = (Integer) server.getAttribute(objectName, "state");
+ if (state.intValue() != 1)
+ {
+ throw new DeploymentException("URL " + warUrl + " deployment failed");
+ }
+ */
+
+ webApp.setAppData(objectName);
+
+ /*
+ * TODO: Create mbeans for the servlets ObjectName servletQuery = new
+ * ObjectName (config.getCatalinaDomain() + ":j2eeType=Servlet,WebModule=" +
+ * objectName.getKeyProperty("name") + ",*"); Iterator iterator =
+ * server.queryMBeans(servletQuery, null).iterator(); while
+ * (iterator.hasNext()) {
+ * di.mbeans.add(((ObjectInstance)iterator.next()).getObjectName()); }
+ */
+
+ if (metaData.getSecurityDomain() != null)
+ {
+ String secDomain = org.jboss.security.Util.unprefixSecurityDomain(metaData.getSecurityDomain());
+ // Associate the Context Id with the Security Domain
+ String contextID = metaData.getJaccContextID();
+
+ //Check if an xacml policy file is present
+ URL xacmlPolicyFile = this.config.getXacmlPolicyURL();
+ if (xacmlPolicyFile != null)
+ {
+ AuthorizationManager authzmgr = org.jboss.security.Util.getAuthorizationManager(secDomain);
+ if (authzmgr instanceof PolicyRegistration)
+ {
+ PolicyRegistration xam = (PolicyRegistration)authzmgr;
+ xam.registerPolicy(contextID, xacmlPolicyFile);
+ }
+ }
+ }
+
+ log.debug("Initialized: " + webApp + " " + objectName);
+ }
+
+ public Loader getWebLoader(DeploymentUnit unit, ClassLoader loader, URL url) throws MalformedURLException
+ {
+ Loader webLoader = null;
+
+ /*
+ * If we are using the jboss class loader we need to augment its path to
+ * include the WEB-INF/{lib,classes} dirs or else scoped class loading
+ * does not see the war level overrides. The call to setWarURL adds these
+ * paths to the deployment UCL.
+ */
+ ArrayList<URL> classpath = (ArrayList<URL>)unit.getAttachment("org.jboss.web.expandedWarClasspath");
+ if (classpath == null)
+ {
+ try
+ {
+ VirtualFile classes = unit.getFile("WEB-INF/classes");
+ // Tomcat can't handle the vfs urls yet
+ URL vfsURL = classes.toURL();
+ String vfsurl = vfsURL.toString();
+ if (vfsurl.startsWith("vfs"))
+ vfsURL = new URL(vfsurl.substring(3));
+ classpath = new ArrayList<URL>();
+ classpath.add(classes.toURL());
+ }
+ catch (Exception e)
+ {
+
+ }
+ }
+
+ if (config.isUseJBossWebLoader())
+ {
+ WebCtxLoader jbossLoader = new WebCtxLoader(loader, injectionContainer);
+ if (classpath != null)
+ jbossLoader.setClasspath(classpath);
+ webLoader = jbossLoader;
+ }
+ else
+ {
+ String[] pkgs = config.getFilteredPackages();
+ WebAppLoader jbossLoader = new WebAppLoader(loader, pkgs, injectionContainer);
+ jbossLoader.setDelegate(getJava2ClassLoadingCompliance());
+ if (classpath != null)
+ jbossLoader.setClasspath(classpath);
+ webLoader = jbossLoader;
+ }
+ return webLoader;
+ }
+
+ public void setInjectionContainer(TomcatInjectionContainer container)
+ {
+ this.injectionContainer = container;
+ }
+
+ /**
+ * Called as part of the undeploy() method template to ask the subclass for
+ * perform the web container specific undeployment steps.
+ */
+ protected void performUndeploy(WebApplication warInfo, String warUrl) throws Exception
+ {
+ if (warInfo == null)
+ {
+ log.debug("performUndeploy, no WebApplication found for URL " + warUrl);
+ return;
+ }
+
+ log.info("undeploy, ctxPath=" + warInfo.getMetaData().getContextRoot() + ", warUrl=" + shortWarUrlFromServerHome(warUrl));
+
+ WebMetaData metaData = warInfo.getMetaData();
+ String hostName = null;
+ Iterator hostNames = metaData.getVirtualHosts();
+ if (hostNames.hasNext())
+ {
+ hostName = hostNames.next().toString();
+ }
+ performUndeployInternal(hostName, warUrl, warInfo);
+ while (hostNames.hasNext())
+ {
+ String additionalHostName = hostNames.next().toString();
+ performUndeployInternal(additionalHostName, warUrl, warInfo);
+ }
+
+ }
+
+ protected void performUndeployInternal(String hostName, String warUrl, WebApplication warInfo) throws Exception
+ {
+
+ WebMetaData metaData = warInfo.getMetaData();
+ String ctxPath = metaData.getContextRoot();
+
+ // TODO: Need to remove the dependency on MBeanServer
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ // If the server is gone, all apps were stopped already
+ if (server == null)
+ return;
+
+ ObjectName objectName = new ObjectName(config.getCatalinaDomain() + ":j2eeType=WebModule,name=//" + ((hostName == null) ? "localhost" : hostName) + ctxPath
+ + ",J2EEApplication=none,J2EEServer=none");
+
+ if (server.isRegistered(objectName))
+ {
+ // Contexts should be stopped by the host already
+ server.invoke(objectName, "destroy", new Object[] {}, new String[] {});
+ }
+
+ }
+
+ /**
+ * Resolve the input virtual host names to the names of the configured Hosts
+ * @param vhostNames Iterator<String> for the jboss-web/virtual-host elements
+ * @return Iterator<String> of the unique Host names
+ * @throws Exception
+ */
+ protected synchronized Iterator mapVirtualHosts(Iterator vhostNames) throws Exception
+ {
+ if (vhostToHostNames.size() == 0)
+ {
+ // Query the configured Host mbeans
+ String hostQuery = config.getCatalinaDomain() + ":type=Host,*";
+ ObjectName query = new ObjectName(hostQuery);
+ Set hosts = server.queryNames(query, null);
+ Iterator iter = hosts.iterator();
+ while (iter.hasNext())
+ {
+ ObjectName host = (ObjectName)iter.next();
+ String name = host.getKeyProperty("host");
+ if (name != null)
+ {
+ vhostToHostNames.put(name, name);
+ String[] aliases = (String[])server.invoke(host, "findAliases", null, null);
+ int count = aliases != null ? aliases.length : 0;
+ for (int n = 0; n < count; n++)
+ {
+ vhostToHostNames.put(aliases[n], name);
+ }
+ }
+ }
+ }
+
+ // Map the virtual host names to the hosts
+ HashSet hosts = new HashSet();
+ while (vhostNames.hasNext())
+ {
+ String vhost = (String)vhostNames.next();
+ String host = (String)vhostToHostNames.get(vhost);
+ if (host == null)
+ {
+ log.warn("Failed to map vhost: " + vhost);
+ // This will cause a new host to be created
+ host = vhost;
+ }
+ hosts.add(host);
+ }
+ return hosts.iterator();
+ }
+
+ private String findConfig(URL warURL) throws IOException
+ {
+ String result = null;
+ // See if the warUrl is a dir or a file
+ File warFile = new File(warURL.getFile());
+ if (warURL.getProtocol().equals("file") && warFile.isDirectory() == true)
+ {
+ File webDD = new File(warFile, CONTEXT_CONFIG_FILE);
+ if (webDD.exists() == true)
+ result = webDD.getAbsolutePath();
+ }
+ else
+ {
+ ZipFile zipFile = new ZipFile(warFile);
+ ZipEntry entry = zipFile.getEntry(CONTEXT_CONFIG_FILE);
+ if (entry != null)
+ {
+ InputStream zipIS = zipFile.getInputStream(entry);
+ byte[] buffer = new byte[512];
+ int bytes;
+ result = warFile.getAbsolutePath() + "-context.xml";
+ FileOutputStream fos = new FileOutputStream(result);
+ while ((bytes = zipIS.read(buffer)) > 0)
+ {
+ fos.write(buffer, 0, bytes);
+ }
+ zipIS.close();
+ fos.close();
+ }
+ zipFile.close();
+ }
+ return result;
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment2.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,854 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.jboss.web.tomcat.service.deployers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import javax.naming.CompositeName;
+import javax.naming.Name;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NotContextException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.AttributeModificationException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributesException;
+import javax.naming.directory.InvalidSearchControlsException;
+import javax.naming.directory.InvalidSearchFilterException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+import org.apache.naming.NamingContextBindingsEnumeration;
+import org.apache.naming.NamingContextEnumeration;
+import org.apache.naming.NamingEntry;
+import org.apache.naming.resources.BaseDirContext;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.ResourceAttributes;
+import org.jboss.logging.Logger;
+import org.jboss.virtual.VirtualFile;
+
+/**
+ * VFS Directory Context implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
+ */
+
+public class VFSDirContext extends BaseDirContext {
+
+ private static final Logger log = Logger.getLogger(VFSDirContext.class);
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Builds a WAR directory context using the given environment.
+ */
+ public VFSDirContext() {
+ super();
+ }
+
+
+ /**
+ * Builds a WAR directory context using the given environment.
+ */
+ public VFSDirContext(Hashtable env) {
+ super(env);
+ }
+
+
+ /**
+ * Constructor used for returning fake subcontexts.
+ */
+ protected VFSDirContext(VirtualFile base) {
+ this.base = base;
+ }
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The virtual file.
+ */
+ protected VirtualFile base = null;
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * Set the virtual file root.
+ */
+ public void setVirtualFile(VirtualFile base) {
+ this.base = base;
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Release any resources allocated for this directory context.
+ */
+ public void release() {
+
+ if (base != null) {
+ base.close();
+ }
+ base = null;
+ super.release();
+
+ }
+
+
+ // -------------------------------------------------------- Context Methods
+
+
+ /**
+ * Retrieves the named object.
+ *
+ * @param name the name of the object to look up
+ * @return the object bound to name
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Object lookup(String name)
+ throws NamingException {
+ return lookup(new CompositeName(name));
+ }
+
+
+ /**
+ * Retrieves the named object. If name is empty, returns a new instance
+ * of this context (which represents the same naming context as this
+ * context, but its environment may be modified independently and it may
+ * be accessed concurrently).
+ *
+ * @param name the name of the object to look up
+ * @return the object bound to name
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Object lookup(Name name)
+ throws NamingException {
+ if (name.isEmpty())
+ return this;
+ VirtualFile entry = null;
+ try {
+ entry = treeLookup(name);
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+ if (entry == null)
+ throw new NamingException
+ (sm.getString("resources.notFound", name));
+
+ try {
+ if (!isFile(entry)) {
+ return new VFSDirContext(entry);
+ } else {
+ return new VFSResource(entry);
+ }
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+
+ /**
+ * Unbinds the named object. Removes the terminal atomic name in name
+ * from the target context--that named by all but the terminal atomic
+ * part of name.
+ * <p>
+ * This method is idempotent. It succeeds even if the terminal atomic
+ * name is not bound in the target context, but throws
+ * NameNotFoundException if any of the intermediate contexts do not exist.
+ *
+ * @param name the name to bind; may not be empty
+ * @exception NameNotFoundException if an intermediate context does not
+ * exist
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void unbind(String name)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Binds a new name to the object bound to an old name, and unbinds the
+ * old name. Both names are relative to this context. Any attributes
+ * associated with the old name become associated with the new name.
+ * Intermediate contexts of the old name are not changed.
+ *
+ * @param oldName the name of the existing binding; may not be empty
+ * @param newName the name of the new binding; may not be empty
+ * @exception NameAlreadyBoundException if newName is already bound
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void rename(String oldName, String newName)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Enumerates the names bound in the named context, along with the class
+ * names of objects bound to them. The contents of any subcontexts are
+ * not included.
+ * <p>
+ * If a binding is added to or removed from this context, its effect on
+ * an enumeration previously returned is undefined.
+ *
+ * @param name the name of the context to list
+ * @return an enumeration of the names and class names of the bindings in
+ * this context. Each element of the enumeration is of type NameClassPair.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration list(String name)
+ throws NamingException {
+ return list(new CompositeName(name));
+ }
+
+
+ /**
+ * Enumerates the names bound in the named context, along with the class
+ * names of objects bound to them. The contents of any subcontexts are
+ * not included.
+ * <p>
+ * If a binding is added to or removed from this context, its effect on
+ * an enumeration previously returned is undefined.
+ *
+ * @param name the name of the context to list
+ * @return an enumeration of the names and class names of the bindings in
+ * this context. Each element of the enumeration is of type NameClassPair.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration list(Name name)
+ throws NamingException {
+ if (name.isEmpty()) {
+ try {
+ return new NamingContextEnumeration(list(base).iterator());
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+ VirtualFile entry = null;
+ try {
+ entry = treeLookup(name);
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+ if (entry == null)
+ throw new NamingException
+ (sm.getString("resources.notFound", name));
+ try {
+ return new NamingContextEnumeration(list(entry).iterator());
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+
+ /**
+ * Enumerates the names bound in the named context, along with the
+ * objects bound to them. The contents of any subcontexts are not
+ * included.
+ * <p>
+ * If a binding is added to or removed from this context, its effect on
+ * an enumeration previously returned is undefined.
+ *
+ * @param name the name of the context to list
+ * @return an enumeration of the bindings in this context.
+ * Each element of the enumeration is of type Binding.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration listBindings(String name)
+ throws NamingException {
+ return listBindings(new CompositeName(name));
+ }
+
+
+ /**
+ * Enumerates the names bound in the named context, along with the
+ * objects bound to them. The contents of any subcontexts are not
+ * included.
+ * <p>
+ * If a binding is added to or removed from this context, its effect on
+ * an enumeration previously returned is undefined.
+ *
+ * @param name the name of the context to list
+ * @return an enumeration of the bindings in this context.
+ * Each element of the enumeration is of type Binding.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration listBindings(Name name)
+ throws NamingException {
+ if (name.isEmpty()) {
+ try {
+ return new NamingContextBindingsEnumeration(list(base).iterator(),
+ this);
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+ VirtualFile entry = null;
+ try {
+ entry = treeLookup(name);
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+ if (entry == null)
+ throw new NamingException
+ (sm.getString("resources.notFound", name));
+ try {
+ return new NamingContextBindingsEnumeration(list(entry).iterator(),
+ this);
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+
+ /**
+ * Destroys the named context and removes it from the namespace. Any
+ * attributes associated with the name are also removed. Intermediate
+ * contexts are not destroyed.
+ * <p>
+ * This method is idempotent. It succeeds even if the terminal atomic
+ * name is not bound in the target context, but throws
+ * NameNotFoundException if any of the intermediate contexts do not exist.
+ *
+ * In a federated naming system, a context from one naming system may be
+ * bound to a name in another. One can subsequently look up and perform
+ * operations on the foreign context using a composite name. However, an
+ * attempt destroy the context using this composite name will fail with
+ * NotContextException, because the foreign context is not a "subcontext"
+ * of the context in which it is bound. Instead, use unbind() to remove
+ * the binding of the foreign context. Destroying the foreign context
+ * requires that the destroySubcontext() be performed on a context from
+ * the foreign context's "native" naming system.
+ *
+ * @param name the name of the context to be destroyed; may not be empty
+ * @exception NameNotFoundException if an intermediate context does not
+ * exist
+ * @exception NotContextException if the name is bound but does not name
+ * a context, or does not name a context of the appropriate type
+ */
+ public void destroySubcontext(String name)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Retrieves the named object, following links except for the terminal
+ * atomic component of the name. If the object bound to name is not a
+ * link, returns the object itself.
+ *
+ * @param name the name of the object to look up
+ * @return the object bound to name, not following the terminal link
+ * (if any).
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Object lookupLink(String name)
+ throws NamingException {
+ // Note : Links are not supported
+ return lookup(name);
+ }
+
+
+ /**
+ * Retrieves the full name of this context within its own namespace.
+ * <p>
+ * Many naming services have a notion of a "full name" for objects in
+ * their respective namespaces. For example, an LDAP entry has a
+ * distinguished name, and a DNS record has a fully qualified name. This
+ * method allows the client application to retrieve this name. The string
+ * returned by this method is not a JNDI composite name and should not be
+ * passed directly to context methods. In naming systems for which the
+ * notion of full name does not make sense,
+ * OperationNotSupportedException is thrown.
+ *
+ * @return this context's name in its own namespace; never null
+ * @exception OperationNotSupportedException if the naming system does
+ * not have the notion of a full name
+ * @exception NamingException if a naming exception is encountered
+ */
+ public String getNameInNamespace()
+ throws NamingException {
+ return docBase;
+ }
+
+
+ // ----------------------------------------------------- DirContext Methods
+
+
+ /**
+ * Retrieves selected attributes associated with a named object.
+ * See the class description regarding attribute models, attribute type
+ * names, and operational attributes.
+ *
+ * @return the requested attributes; never null
+ * @param name the name of the object from which to retrieve attributes
+ * @param attrIds the identifiers of the attributes to retrieve. null
+ * indicates that all attributes should be retrieved; an empty array
+ * indicates that none should be retrieved
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Attributes getAttributes(String name, String[] attrIds)
+ throws NamingException {
+ return getAttributes(new CompositeName(name), attrIds);
+ }
+
+
+ /**
+ * Retrieves all of the attributes associated with a named object.
+ *
+ * @return the set of attributes associated with name.
+ * Returns an empty attribute set if name has no attributes; never null.
+ * @param name the name of the object from which to retrieve attributes
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Attributes getAttributes(Name name, String[] attrIds)
+ throws NamingException {
+
+ VirtualFile entry = null;
+ if (name.isEmpty()) {
+ entry = base;
+ } else {
+ try {
+ entry = treeLookup(name);
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+ if (entry == null)
+ throw new NamingException
+ (sm.getString("resources.notFound", name));
+
+ ResourceAttributes attrs = new ResourceAttributes();
+ try {
+ attrs.setCreationDate(new Date(entry.getLastModified()));
+ attrs.setName(entry.getName());
+ if (isFile(entry))
+ attrs.setResourceType("");
+ attrs.setContentLength(entry.getSize());
+ attrs.setLastModified(entry.getLastModified());
+ } catch (IOException e) {
+ NamingException ex = new NamingException
+ (sm.getString("resources.notFound", name));
+ ex.initCause(e);
+ throw ex;
+ }
+
+ return attrs;
+
+ }
+
+
+ /**
+ * Modifies the attributes associated with a named object. The order of
+ * the modifications is not specified. Where possible, the modifications
+ * are performed atomically.
+ *
+ * @param name the name of the object whose attributes will be updated
+ * @param mod_op the modification operation, one of: ADD_ATTRIBUTE,
+ * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+ * @param attrs the attributes to be used for the modification; may not
+ * be null
+ * @exception AttributeModificationException if the modification cannot be
+ * completed successfully
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void modifyAttributes(String name, int mod_op, Attributes attrs)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Modifies the attributes associated with a named object using an an
+ * ordered list of modifications. The modifications are performed in the
+ * order specified. Each modification specifies a modification operation
+ * code and an attribute on which to operate. Where possible, the
+ * modifications are performed atomically.
+ *
+ * @param name the name of the object whose attributes will be updated
+ * @param mods an ordered sequence of modifications to be performed; may
+ * not be null
+ * @exception AttributeModificationException if the modification cannot be
+ * completed successfully
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void modifyAttributes(String name, ModificationItem[] mods)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Binds a name to an object, along with associated attributes. If attrs
+ * is null, the resulting binding will have the attributes associated
+ * with obj if obj is a DirContext, and no attributes otherwise. If attrs
+ * is non-null, the resulting binding will have attrs as its attributes;
+ * any attributes associated with obj are ignored.
+ *
+ * @param name the name to bind; may not be empty
+ * @param obj the object to bind; possibly null
+ * @param attrs the attributes to associate with the binding
+ * @exception NameAlreadyBoundException if name is already bound
+ * @exception InvalidAttributesException if some "mandatory" attributes
+ * of the binding are not supplied
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void bind(String name, Object obj, Attributes attrs)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Binds a name to an object, along with associated attributes,
+ * overwriting any existing binding. If attrs is null and obj is a
+ * DirContext, the attributes from obj are used. If attrs is null and obj
+ * is not a DirContext, any existing attributes associated with the object
+ * already bound in the directory remain unchanged. If attrs is non-null,
+ * any existing attributes associated with the object already bound in
+ * the directory are removed and attrs is associated with the named
+ * object. If obj is a DirContext and attrs is non-null, the attributes
+ * of obj are ignored.
+ *
+ * @param name the name to bind; may not be empty
+ * @param obj the object to bind; possibly null
+ * @param attrs the attributes to associate with the binding
+ * @exception InvalidAttributesException if some "mandatory" attributes
+ * of the binding are not supplied
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void rebind(String name, Object obj, Attributes attrs)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Creates and binds a new context, along with associated attributes.
+ * This method creates a new subcontext with the given name, binds it in
+ * the target context (that named by all but terminal atomic component of
+ * the name), and associates the supplied attributes with the newly
+ * created object. All intermediate and target contexts must already
+ * exist. If attrs is null, this method is equivalent to
+ * Context.createSubcontext().
+ *
+ * @param name the name of the context to create; may not be empty
+ * @param attrs the attributes to associate with the newly created context
+ * @return the newly created context
+ * @exception NameAlreadyBoundException if the name is already bound
+ * @exception InvalidAttributesException if attrs does not contain all
+ * the mandatory attributes required for creation
+ * @exception NamingException if a naming exception is encountered
+ */
+ public DirContext createSubcontext(String name, Attributes attrs)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Retrieves the schema associated with the named object. The schema
+ * describes rules regarding the structure of the namespace and the
+ * attributes stored within it. The schema specifies what types of
+ * objects can be added to the directory and where they can be added;
+ * what mandatory and optional attributes an object can have. The range
+ * of support for schemas is directory-specific.
+ *
+ * @param name the name of the object whose schema is to be retrieved
+ * @return the schema associated with the context; never null
+ * @exception OperationNotSupportedException if schema not supported
+ * @exception NamingException if a naming exception is encountered
+ */
+ public DirContext getSchema(String name)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Retrieves a context containing the schema objects of the named
+ * object's class definitions.
+ *
+ * @param name the name of the object whose object class definition is to
+ * be retrieved
+ * @return the DirContext containing the named object's class
+ * definitions; never null
+ * @exception OperationNotSupportedException if schema not supported
+ * @exception NamingException if a naming exception is encountered
+ */
+ public DirContext getSchemaClassDefinition(String name)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Searches in a single context for objects that contain a specified set
+ * of attributes, and retrieves selected attributes. The search is
+ * performed using the default SearchControls settings.
+ *
+ * @param name the name of the context to search
+ * @param matchingAttributes the attributes to search for. If empty or
+ * null, all objects in the target context are returned.
+ * @param attributesToReturn the attributes to return. null indicates
+ * that all attributes are to be returned; an empty array indicates that
+ * none are to be returned.
+ * @return a non-null enumeration of SearchResult objects. Each
+ * SearchResult contains the attributes identified by attributesToReturn
+ * and the name of the corresponding object, named relative to the
+ * context named by name.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration search(String name, Attributes matchingAttributes,
+ String[] attributesToReturn)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Searches in a single context for objects that contain a specified set
+ * of attributes. This method returns all the attributes of such objects.
+ * It is equivalent to supplying null as the atributesToReturn parameter
+ * to the method search(Name, Attributes, String[]).
+ *
+ * @param name the name of the context to search
+ * @param matchingAttributes the attributes to search for. If empty or
+ * null, all objects in the target context are returned.
+ * @return a non-null enumeration of SearchResult objects. Each
+ * SearchResult contains the attributes identified by attributesToReturn
+ * and the name of the corresponding object, named relative to the
+ * context named by name.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration search(String name, Attributes matchingAttributes)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Searches in the named context or object for entries that satisfy the
+ * given search filter. Performs the search as specified by the search
+ * controls.
+ *
+ * @param name the name of the context or object to search
+ * @param filter the filter expression to use for the search; may not be
+ * null
+ * @param cons the search controls that control the search. If null,
+ * the default search controls are used (equivalent to
+ * (new SearchControls())).
+ * @return an enumeration of SearchResults of the objects that satisfy
+ * the filter; never null
+ * @exception InvalidSearchFilterException if the search filter specified
+ * is not supported or understood by the underlying directory
+ * @exception InvalidSearchControlsException if the search controls
+ * contain invalid settings
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration search(String name, String filter,
+ SearchControls cons)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Searches in the named context or object for entries that satisfy the
+ * given search filter. Performs the search as specified by the search
+ * controls.
+ *
+ * @param name the name of the context or object to search
+ * @param filterExpr the filter expression to use for the search.
+ * The expression may contain variables of the form "{i}" where i is a
+ * nonnegative integer. May not be null.
+ * @param filterArgs the array of arguments to substitute for the
+ * variables in filterExpr. The value of filterArgs[i] will replace each
+ * occurrence of "{i}". If null, equivalent to an empty array.
+ * @param cons the search controls that control the search. If null, the
+ * default search controls are used (equivalent to (new SearchControls())).
+ * @return an enumeration of SearchResults of the objects that satisy the
+ * filter; never null
+ * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i}
+ * expressions where i is outside the bounds of the array filterArgs
+ * @exception InvalidSearchControlsException if cons contains invalid
+ * settings
+ * @exception InvalidSearchFilterException if filterExpr with filterArgs
+ * represents an invalid search filter
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration search(String name, String filterExpr,
+ Object[] filterArgs, SearchControls cons)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Check if a VirtualFile is a regular file or a folder.
+ */
+ protected boolean isFile(VirtualFile file)
+ throws IOException {
+ return (file.isLeaf() || (file.getSize() > 0));
+ }
+
+
+ /**
+ * Entry tree lookup.
+ */
+ protected VirtualFile treeLookup(Name name)
+ throws IOException {
+ if (base == null)
+ return null;
+ if (name.isEmpty())
+ return base;
+ VirtualFile currentFile = base;
+ for (int i = 0; i < name.size(); i++) {
+ if (name.get(i).length() == 0)
+ continue;
+ currentFile = currentFile.findChild(name.get(i));
+ if (currentFile == null)
+ return null;
+ }
+ return currentFile;
+ }
+
+
+ /**
+ * List children as objects.
+ */
+ protected ArrayList<NamingEntry> list(VirtualFile entry)
+ throws IOException {
+
+ ArrayList<NamingEntry> entries = new ArrayList<NamingEntry>();
+ if (!isFile(entry)) {
+ Iterator<VirtualFile> children = entry.getChildren().iterator();
+ NamingEntry namingEntry = null;
+
+ while (children.hasNext()) {
+ VirtualFile current = children.next();
+ Object object = null;
+ if (!isFile(current)) {
+ object = new VFSDirContext(current);
+ } else {
+ object = new VFSResource(current);
+ }
+ namingEntry = new NamingEntry
+ (current.getName(), object, NamingEntry.ENTRY);
+ entries.add(namingEntry);
+ }
+ }
+
+ return entries;
+
+ }
+
+
+ // ------------------------------------------------ WARResource Inner Class
+
+
+ /**
+ * This specialized resource implementation avoids opening the IputStream
+ * to the WAR right away.
+ */
+ protected class VFSResource extends Resource {
+
+
+ // -------------------------------------------------------- Constructor
+
+
+ public VFSResource(VirtualFile entry) {
+ this.entry = entry;
+ }
+
+
+ // --------------------------------------------------- Member Variables
+
+
+ protected VirtualFile entry;
+
+
+ // ----------------------------------------------------- Public Methods
+
+
+ /**
+ * Content accessor.
+ *
+ * @return InputStream
+ */
+ public InputStream streamContent()
+ throws IOException {
+ if (binaryContent == null) {
+ inputStream = entry.openStream();
+ }
+ return super.streamContent();
+ }
+
+
+ }
+
+
+}
+
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/VFSDirContext.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,190 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.web.tomcat.service.deployers;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.catalina.Loader;
+import org.jboss.deployers.plugins.deployer.AbstractSimpleDeployer;
+import org.jboss.deployers.spi.DeploymentException;
+import org.jboss.deployers.spi.classloader.ClassLoaderFactory;
+import org.jboss.deployers.spi.deployer.DeploymentUnit;
+import org.jboss.deployers.spi.structure.DeploymentContext;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.web.tomcat.service.TomcatInjectionContainer;
+import org.jboss.web.tomcat.service.WebAppLoader;
+import org.jboss.web.tomcat.service.WebCtxLoader;
+
+/**
+ * A class loader deployer for wars that handles installing a war first class
+ * loader unless the WebMetaData indicates otherwise.
+ * AbstractClassLoaderDeployer
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision:$
+ */
+public class WarClassLoaderDeployer extends AbstractSimpleDeployer
+ implements ClassLoaderFactory
+{
+ /** The parent class loader first model flag */
+ private boolean java2ClassLoadingCompliance = false;
+ private boolean useJBossWebLoader;
+ /** Package names that should be ignored for class loading */
+ private String[] filteredPackages;
+
+ public WarClassLoaderDeployer()
+ {
+ // Should be after the default class loading deployer to pickup parent cl
+ super.setRelativeOrder(CLASSLOADER_DEPLOYER - 1);
+ }
+
+ public boolean isJava2ClassLoadingCompliance()
+ {
+ return java2ClassLoadingCompliance;
+ }
+ public void setJava2ClassLoadingCompliance(boolean flag)
+ {
+ this.java2ClassLoadingCompliance = flag;
+ }
+
+ public String[] getFilteredPackages()
+ {
+ return filteredPackages;
+ }
+ public void setFilteredPackages(String[] pkgs)
+ {
+ this.filteredPackages = pkgs;
+ }
+
+ public boolean isUseJBossWebLoader()
+ {
+ return useJBossWebLoader;
+ }
+ public void setUseJBossWebLoader(boolean useJBossWebLoader)
+ {
+ this.useJBossWebLoader = useJBossWebLoader;
+ }
+
+
+ @Override
+ public void deploy(DeploymentUnit unit) throws DeploymentException
+ {
+ WebMetaData metaData = unit.getAttachment(WebMetaData.class);
+ if( metaData != null )
+ {
+ unit.addAttachment(ClassLoaderFactory.class, this);
+ }
+ }
+
+ public ClassLoader createClassLoader(DeploymentContext context) throws Exception
+ {
+ ClassLoader loader = null;
+ try
+ {
+ DeploymentUnit unit = context.getDeploymentUnit();
+ WebMetaData metaData = unit.getAttachment(WebMetaData.class);
+ loader = createClassLoader(metaData, context);
+ if( loader != null )
+ context.setClassLoader(loader);
+ }
+ catch(Exception e)
+ {
+ log.warn("", e);
+ }
+ return loader;
+ }
+
+ public void removeClassLoader(DeploymentContext context) throws Exception
+ {
+
+ }
+
+ /**
+ *
+ */
+ protected ClassLoader createClassLoader(WebMetaData metaData, DeploymentContext context)
+ throws Exception
+ {
+ ClassLoader loader = null;
+ if (metaData != null)
+ {
+ ClassLoader parent = context.getClassLoader();
+ if( parent == null )
+ parent = context.getTopLevel().getClassLoader();
+ if( parent == null )
+ parent = Thread.currentThread().getContextClassLoader();
+ Loader webLoader = getWebLoader(parent, metaData, context.getClassPath());
+ context.getDeploymentUnit().addAttachment(Loader.class, webLoader);
+ loader = webLoader.getClassLoader();
+ }
+ return loader;
+ }
+
+ /**
+ * Create the web app class loader.
+ *
+ * @param loader
+ * @param metaData
+ * @param classpath
+ * @return
+ * @throws MalformedURLException
+ */
+ protected Loader getWebLoader(ClassLoader loader, WebMetaData metaData,
+ List<VirtualFile> classpath)
+ throws Exception
+ {
+ Loader webLoader = null;
+ TomcatInjectionContainer injectionContainer = null;
+ if (useJBossWebLoader)
+ {
+ WebCtxLoader jbossLoader = new WebCtxLoader(loader, injectionContainer);
+ ArrayList<URL> cp = new ArrayList<URL>();
+ for(VirtualFile vf : classpath)
+ {
+ try
+ {
+ URL path = vf.toURL();
+ cp.add(path);
+ }
+ catch(Exception e)
+ {
+ log.debug("Ignoring path element: "+vf, e);
+ }
+ }
+ jbossLoader.setClasspath(cp);
+ webLoader = jbossLoader;
+ }
+ else
+ {
+ WebAppLoader jbossLoader = new WebAppLoader(loader, filteredPackages, injectionContainer);
+ jbossLoader.setDelegate(java2ClassLoadingCompliance);
+ jbossLoader.start();
+ webLoader = jbossLoader;
+ }
+ return webLoader;
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/WarClassLoaderDeployer.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,821 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.jasper;
+
+import java.util.Properties;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.Map;
+import java.io.File;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.apache.jasper.Options;
+import org.apache.jasper.Constants;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.apache.jasper.compiler.TldLocationsCache;
+import org.apache.jasper.compiler.JspConfig;
+import org.apache.jasper.compiler.TagPluginManager;
+import org.apache.jasper.compiler.Localizer;
+import org.jboss.logging.Logger;
+
+/**
+ * Override the default JspServletOptions to externalize the jsp layer
+ * configuration. This overrides the default TagLibCache to the jboss version.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 58265 $
+ */
+public class JspServletOptions
+ implements Options
+{
+ static Logger log = Logger.getLogger(JspServletOptions.class);
+
+ private Properties settings = new Properties();
+
+ /**
+ * Is Jasper being used in development mode?
+ */
+ private boolean development = true;
+
+ /**
+ * Should we include a source fragment in exception messages, which could be displayed
+ * to the developer ?
+ */
+ private boolean displaySourceFragment = true;
+
+ /**
+ * Should Ant fork its java compiles of JSP pages.
+ */
+ public boolean fork = true;
+
+ /**
+ * Do you want to keep the generated Java files around?
+ */
+ private boolean keepGenerated = true;
+
+ /**
+ * Should white spaces between directives or actions be trimmed?
+ */
+ private boolean trimSpaces = false;
+
+ /**
+ * Determines whether tag handler pooling is enabled.
+ */
+ private boolean isPoolingEnabled = true;
+
+ /**
+ * Do you want support for "mapped" files? This will generate servlet that
+ * has a print statement per line of the JSP file. This seems like a really
+ * nice feature to have for debugging.
+ */
+ private boolean mappedFile = true;
+
+ /**
+ * Do you want stack traces and such displayed in the client's browser? If
+ * this is false, such messages go to the standard error or a log file if the
+ * standard error is redirected.
+ */
+ private boolean sendErrorToClient = false;
+
+ /**
+ * Do we want to include debugging information in the class file?
+ */
+ private boolean classDebugInfo = true;
+
+ /**
+ * Background compile thread check interval in seconds.
+ */
+ private int checkInterval = 0;
+
+ /**
+ * Is the generation of SMAP info for JSR45 debuggin suppressed?
+ */
+ private boolean isSmapSuppressed = false;
+
+ /**
+ * Should SMAP info for JSR45 debugging be dumped to a file?
+ */
+ private boolean isSmapDumped = false;
+
+ /**
+ * Are Text strings to be generated as char arrays?
+ */
+ private boolean genStringAsCharArray = false;
+
+ private boolean errorOnUseBeanInvalidClassAttribute = true;
+
+ /**
+ * I want to see my generated servlets. Which directory are they in?
+ */
+ private File scratchDir;
+
+ /**
+ * Need to have this as is for versions 4 and 5 of IE. Can be set from the
+ * initParams so if it changes in the future all that is needed is to have a
+ * jsp initParam of type ieClassId="<value>"
+ */
+ private String ieClassId = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93";
+
+ /**
+ * What classpath should I use while compiling generated servlets?
+ */
+ private String classpath = null;
+
+ /**
+ * Compiler to use.
+ */
+ private String compiler = null;
+
+ /**
+ * The compiler class name.
+ */
+ private String compilerClassName = null;
+
+ /**
+ * Compiler target VM.
+ */
+ private String compilerTargetVM = "1.4";
+
+ /**
+ * The compiler source VM.
+ */
+ private String compilerSourceVM = "1.4";
+
+ /**
+ * Cache for the TLD locations
+ */
+ private TldLocationsCache tldLocationsCache = null;
+
+ /**
+ * Jsp config information
+ */
+ private JspConfig jspConfig = null;
+
+ /**
+ * TagPluginManager
+ */
+ private TagPluginManager tagPluginManager = null;
+
+ /**
+ * Java platform encoding to generate the JSP page servlet.
+ */
+ private String javaEncoding = "UTF8";
+
+ /**
+ * Modification test interval.
+ */
+ private int modificationTestInterval = 4;
+
+ private boolean ignoreAnnotations;
+
+
+
+ /**
+ * Is generation of X-Powered-By response header enabled/disabled?
+ */
+ private boolean xpoweredBy;
+
+
+ public boolean getIgnoreAnnotations()
+ {
+ return ignoreAnnotations;
+ }
+
+ public void setIgnoreAnnotations(boolean ignoreAnnotations)
+ {
+ this.ignoreAnnotations = ignoreAnnotations;
+ }
+
+ public String getProperty(String name)
+ {
+ return settings.getProperty(name);
+ }
+
+ public void setProperty(String name, String value)
+ {
+ if (name != null && value != null)
+ {
+ settings.setProperty(name, value);
+ }
+ }
+
+ /**
+ * Are we keeping generated code around?
+ */
+ public boolean getKeepGenerated()
+ {
+ return keepGenerated;
+ }
+
+ /**
+ * Should white spaces between directives or actions be trimmed?
+ */
+ public boolean getTrimSpaces()
+ {
+ return trimSpaces;
+ }
+
+ public boolean isPoolingEnabled()
+ {
+ return isPoolingEnabled;
+ }
+
+ /**
+ * Are we supporting HTML mapped servlets?
+ */
+ public boolean getMappedFile()
+ {
+ return mappedFile;
+ }
+
+ /**
+ * Should errors be sent to client or thrown into stderr?
+ */
+ public boolean getSendErrorToClient()
+ {
+ return sendErrorToClient;
+ }
+
+ /**
+ * Should class files be compiled with debug information?
+ */
+ public boolean getClassDebugInfo()
+ {
+ return classDebugInfo;
+ }
+
+ /**
+ * Background JSP compile thread check intervall
+ */
+ public int getCheckInterval()
+ {
+ return checkInterval;
+ }
+
+ /**
+ * Modification test interval.
+ */
+ public int getModificationTestInterval()
+ {
+ return modificationTestInterval;
+ }
+
+ /**
+ * Is Jasper being used in development mode?
+ */
+ public boolean getDevelopment()
+ {
+ return development;
+ }
+
+ public boolean getDisplaySourceFragment()
+ {
+ // TODO Auto-generated method stub
+ return displaySourceFragment;
+ }
+
+ /**
+ * Is the generation of SMAP info for JSR45 debuggin suppressed?
+ */
+ public boolean isSmapSuppressed()
+ {
+ return isSmapSuppressed;
+ }
+
+ /**
+ * Should SMAP info for JSR45 debugging be dumped to a file?
+ */
+ public boolean isSmapDumped()
+ {
+ return isSmapDumped;
+ }
+
+ /**
+ * Are Text strings to be generated as char arrays?
+ */
+ public boolean genStringAsCharArray()
+ {
+ return this.genStringAsCharArray;
+ }
+
+ /**
+ * Class ID for use in the plugin tag when the browser is IE.
+ */
+ public String getIeClassId()
+ {
+ return ieClassId;
+ }
+
+ /**
+ * What is my scratch dir?
+ */
+ public File getScratchDir()
+ {
+ return scratchDir;
+ }
+
+ /**
+ * What classpath should I use while compiling the servlets generated from
+ * JSP files?
+ */
+ public String getClassPath()
+ {
+ return classpath;
+ }
+
+ /**
+ * Is generation of X-Powered-By response header enabled/disabled?
+ */
+ public boolean isXpoweredBy()
+ {
+ return xpoweredBy;
+ }
+
+ /**
+ * Compiler to use.
+ */
+ public String getCompiler()
+ {
+ return compiler;
+ }
+
+ /**
+ * @see Options#getCompilerClassName
+ */
+ public String getCompilerClassName()
+ {
+ return compilerClassName;
+ }
+
+ /**
+ * @see Options#getCompilerTargetVM
+ */
+ public String getCompilerTargetVM()
+ {
+ return compilerTargetVM;
+ }
+
+ /**
+ * @see Options#getCompilerSourceVM
+ */
+ public String getCompilerSourceVM()
+ {
+ return compilerSourceVM;
+ }
+
+ public boolean getErrorOnUseBeanInvalidClassAttribute()
+ {
+ return errorOnUseBeanInvalidClassAttribute;
+ }
+
+ public void setErrorOnUseBeanInvalidClassAttribute(boolean b)
+ {
+ errorOnUseBeanInvalidClassAttribute = b;
+ }
+
+ public TldLocationsCache getTldLocationsCache()
+ {
+ return tldLocationsCache;
+ }
+
+ public void setTldLocationsCache(TldLocationsCache tldC)
+ {
+ tldLocationsCache = tldC;
+ }
+
+ public String getJavaEncoding()
+ {
+ return javaEncoding;
+ }
+
+ public boolean getFork()
+ {
+ return fork;
+ }
+
+ public JspConfig getJspConfig()
+ {
+ return jspConfig;
+ }
+
+ public TagPluginManager getTagPluginManager()
+ {
+ return tagPluginManager;
+ }
+
+ /**
+ * Is caching enabled (used for precompilation).
+ */
+ public boolean isCaching()
+ {
+ return false;
+ }
+
+ /**
+ * The web-application wide cache for the returned TreeNode
+ * by parseXMLDocument in TagLibraryInfoImpl.parseTLD,
+ * if isCaching returns true.
+ *
+ * @return the Map(String uri, TreeNode tld) instance.
+ */
+ public Map getCache()
+ {
+ return null;
+ }
+
+ /**
+ * Create an EmbeddedServletOptions object using data available from
+ * ServletConfig and ServletContext.
+ */
+ public JspServletOptions(ServletConfig config, ServletContext context)
+ {
+
+ Enumeration enumeration = config.getInitParameterNames();
+ while (enumeration.hasMoreElements())
+ {
+ String k = (String) enumeration.nextElement();
+ String v = config.getInitParameter(k);
+ setProperty(k, v);
+ }
+
+ // quick hack
+ String validating = config.getInitParameter("validating");
+ if ("false".equals(validating)) ParserUtils.validating = false;
+
+ String keepgen = config.getInitParameter("keepgenerated");
+ if (keepgen != null)
+ {
+ if (keepgen.equalsIgnoreCase("true"))
+ {
+ this.keepGenerated = true;
+ }
+ else if (keepgen.equalsIgnoreCase("false"))
+ {
+ this.keepGenerated = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.keepgen"));
+ }
+ }
+
+
+ String trimsp = config.getInitParameter("trimSpaces");
+ if (trimsp != null)
+ {
+ if (trimsp.equalsIgnoreCase("true"))
+ {
+ trimSpaces = true;
+ }
+ else if (trimsp.equalsIgnoreCase("false"))
+ {
+ trimSpaces = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.trimspaces"));
+ }
+ }
+
+ this.isPoolingEnabled = true;
+ String poolingEnabledParam
+ = config.getInitParameter("enablePooling");
+ if (poolingEnabledParam != null
+ && !poolingEnabledParam.equalsIgnoreCase("true"))
+ {
+ if (poolingEnabledParam.equalsIgnoreCase("false"))
+ {
+ this.isPoolingEnabled = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.enablePooling"));
+ }
+ }
+
+ String mapFile = config.getInitParameter("mappedfile");
+ if (mapFile != null)
+ {
+ if (mapFile.equalsIgnoreCase("true"))
+ {
+ this.mappedFile = true;
+ }
+ else if (mapFile.equalsIgnoreCase("false"))
+ {
+ this.mappedFile = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.mappedFile"));
+ }
+ }
+
+ String senderr = config.getInitParameter("sendErrToClient");
+ if (senderr != null)
+ {
+ if (senderr.equalsIgnoreCase("true"))
+ {
+ this.sendErrorToClient = true;
+ }
+ else if (senderr.equalsIgnoreCase("false"))
+ {
+ this.sendErrorToClient = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.sendErrToClient"));
+ }
+ }
+
+ String debugInfo = config.getInitParameter("classdebuginfo");
+ if (debugInfo != null)
+ {
+ if (debugInfo.equalsIgnoreCase("true"))
+ {
+ this.classDebugInfo = true;
+ }
+ else if (debugInfo.equalsIgnoreCase("false"))
+ {
+ this.classDebugInfo = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.classDebugInfo"));
+ }
+ }
+
+ String checkInterval = config.getInitParameter("checkInterval");
+ if (checkInterval != null)
+ {
+ try
+ {
+ this.checkInterval = Integer.parseInt(checkInterval);
+ if (this.checkInterval == 0)
+ {
+ this.checkInterval = 300;
+ log.warn(Localizer.getMessage("jsp.warning.checkInterval"));
+ }
+ }
+ catch (NumberFormatException ex)
+ {
+ log.warn(Localizer.getMessage("jsp.warning.checkInterval"));
+ }
+ }
+
+ String modificationTestInterval = config.getInitParameter("modificationTestInterval");
+ if (modificationTestInterval != null)
+ {
+ try
+ {
+ this.modificationTestInterval = Integer.parseInt(modificationTestInterval);
+ }
+ catch (NumberFormatException ex)
+ {
+ log.warn(Localizer.getMessage("jsp.warning.modificationTestInterval"));
+ }
+ }
+
+ String development = config.getInitParameter("development");
+ if (development != null)
+ {
+ if (development.equalsIgnoreCase("true"))
+ {
+ this.development = true;
+ }
+ else if (development.equalsIgnoreCase("false"))
+ {
+ this.development = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.development"));
+ }
+ }
+
+ String displaySourceFragment = config.getInitParameter("displaySourceFragment");
+ if (displaySourceFragment != null)
+ {
+ if (displaySourceFragment.equalsIgnoreCase("true"))
+ {
+ this.displaySourceFragment = true;
+ }
+ else if (displaySourceFragment.equalsIgnoreCase("false"))
+ {
+ this.displaySourceFragment = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.displaySourceFragment"));
+ }
+ }
+
+ String suppressSmap = config.getInitParameter("suppressSmap");
+ if (suppressSmap != null)
+ {
+ if (suppressSmap.equalsIgnoreCase("true"))
+ {
+ isSmapSuppressed = true;
+ }
+ else if (suppressSmap.equalsIgnoreCase("false"))
+ {
+ isSmapSuppressed = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.suppressSmap"));
+ }
+ }
+
+ String dumpSmap = config.getInitParameter("dumpSmap");
+ if (dumpSmap != null)
+ {
+ if (dumpSmap.equalsIgnoreCase("true"))
+ {
+ isSmapDumped = true;
+ }
+ else if (dumpSmap.equalsIgnoreCase("false"))
+ {
+ isSmapDumped = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.dumpSmap"));
+ }
+ }
+
+ String genCharArray = config.getInitParameter("genStrAsCharArray");
+ if (genCharArray != null)
+ {
+ if (genCharArray.equalsIgnoreCase("true"))
+ {
+ genStringAsCharArray = true;
+ }
+ else if (genCharArray.equalsIgnoreCase("false"))
+ {
+ genStringAsCharArray = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.genchararray"));
+ }
+ }
+
+ String errBeanClass =
+ config.getInitParameter("errorOnUseBeanInvalidClassAttribute");
+ if (errBeanClass != null)
+ {
+ if (errBeanClass.equalsIgnoreCase("true"))
+ {
+ errorOnUseBeanInvalidClassAttribute = true;
+ }
+ else if (errBeanClass.equalsIgnoreCase("false"))
+ {
+ errorOnUseBeanInvalidClassAttribute = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.errBean"));
+ }
+ }
+
+ String ieClassId = config.getInitParameter("ieClassId");
+ if (ieClassId != null)
+ this.ieClassId = ieClassId;
+
+ String classpath = config.getInitParameter("classpath");
+ if (classpath != null)
+ this.classpath = classpath;
+
+ /*
+ * scratchdir
+ */
+ String dir = config.getInitParameter("scratchdir");
+ if (dir != null)
+ {
+ scratchDir = new File(dir);
+ }
+ else
+ {
+ // First try the Servlet 2.2 javax.servlet.context.tempdir property
+ scratchDir = (File) context.getAttribute(Constants.TMP_DIR);
+ if (scratchDir == null)
+ {
+ // Not running in a Servlet 2.2 container.
+ // Try to get the JDK 1.2 java.io.tmpdir property
+ dir = System.getProperty("java.io.tmpdir");
+ if (dir != null)
+ scratchDir = new File(dir);
+ }
+ }
+ if (this.scratchDir == null)
+ {
+ log.fatal(Localizer.getMessage("jsp.error.no.scratch.dir"));
+ return;
+ }
+
+ if (!(scratchDir.exists() && scratchDir.canRead() &&
+ scratchDir.canWrite() && scratchDir.isDirectory()))
+ log.fatal(Localizer.getMessage("jsp.error.bad.scratch.dir",
+ scratchDir.getAbsolutePath()));
+
+ this.compiler = config.getInitParameter("compiler");
+
+ String compilerTargetVM = config.getInitParameter("compilerTargetVM");
+ if (compilerTargetVM != null)
+ {
+ this.compilerTargetVM = compilerTargetVM;
+ }
+
+ String compilerSourceVM = config.getInitParameter("compilerSourceVM");
+ if (compilerSourceVM != null)
+ {
+ this.compilerSourceVM = compilerSourceVM;
+ }
+
+ String javaEncoding = config.getInitParameter("javaEncoding");
+ if (javaEncoding != null)
+ {
+ this.javaEncoding = javaEncoding;
+ }
+
+ String compilerClassName = config.getInitParameter("compilerClassName");
+ if(compilerClassName != null)
+ {
+ this.compilerClassName = compilerClassName;
+ }
+
+ String fork = config.getInitParameter("fork");
+ if (fork != null)
+ {
+ if (fork.equalsIgnoreCase("true"))
+ {
+ this.fork = true;
+ }
+ else if (fork.equalsIgnoreCase("false"))
+ {
+ this.fork = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.fork"));
+ }
+ }
+
+ String xpoweredBy = config.getInitParameter("xpoweredBy");
+ if (xpoweredBy != null)
+ {
+ if (xpoweredBy.equalsIgnoreCase("true"))
+ {
+ this.xpoweredBy = true;
+ }
+ else if (xpoweredBy.equalsIgnoreCase("false"))
+ {
+ this.xpoweredBy = false;
+ }
+ else
+ {
+ log.warn(Localizer.getMessage("jsp.warning.xpoweredBy"));
+ }
+ }
+
+ /* Setup the global Tag Libraries location cache for this web app. The
+ tagLibJarN entries define the jars the cache should scan.
+ */
+ String base = "tagLibJar";
+ ArrayList tldJars = new ArrayList();
+ int count = 0;
+ String jarPath = null;
+ do
+ {
+ String name = base + count;
+ jarPath = config.getInitParameter(name);
+ if( jarPath != null )
+ tldJars.add(jarPath);
+ count ++;
+ } while( jarPath != null );
+
+ tldLocationsCache = new TagLibCache(context, tldJars);
+
+ // Setup the jsp config info for this web app.
+ jspConfig = new JspConfig(context);
+
+ // Create a Tag plugin instance
+ tagPluginManager = new TagPluginManager(context);
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/JspServletOptions.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,446 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.jasper;
+
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.MalformedURLException;
+import javax.servlet.ServletContext;
+import java.util.Map; // SYNC
+
+import org.apache.jasper.compiler.TldLocationsCache;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.JasperException;
+import org.apache.jasper.Constants;
+import org.apache.jasper.xmlparser.TreeNode;
+import org.apache.jasper.xmlparser.ParserUtils;
+import org.jboss.logging.Logger;
+
+/**
+ * A prototype TagLibCache that allows one to obtain shared tlds from the
+ * jbossweb sar conf/tlds directory.
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 45726 $
+ */
+public class TagLibCache extends TldLocationsCache
+{
+ private static final String WEB_XML = "/WEB-INF/web.xml";
+ private static final String JAR_FILE_SUFFIX = ".jar";
+
+ private static Logger log = Logger.getLogger(TagLibCache.class);
+
+ private ServletContext ctx;
+ private HashMap mappings;
+ private ArrayList tagLibJars;
+
+ public TagLibCache(ServletContext ctx, ArrayList tagLibJars)
+ {
+ super(ctx, true);
+ this.ctx = ctx;
+ this.tagLibJars = tagLibJars;
+ }
+
+ /**
+ * Gets the 'location' of the TLD associated with the given taglib 'uri'.
+ *
+ * Returns null if the uri is not associated with any tag library 'exposed'
+ * in the web application. A tag library is 'exposed' either explicitly in
+ * web.xml or implicitly via the uri tag in the TLD of a taglib deployed in a
+ * jar file (WEB-INF/lib).
+ * @param uri The taglib uri
+ * @return An array of two Strings: The first element denotes the real path
+ * to the TLD. If the path to the TLD points to a jar file, then the
+ * second element denotes the name of the TLD entry in the jar file.
+ * Returns null if the uri is not associated with any tag library
+ * 'exposed' in the web application.
+ */
+ public String[] getLocation(String uri) throws JasperException
+ {
+ if (mappings == null)
+ init();
+ String[] locations = (String[]) mappings.get(uri);
+ return locations;
+ }
+
+ private synchronized void init() throws JasperException
+ {
+ if (mappings != null)
+ {
+ return;
+ }
+
+ HashMap tmpMappings = null;
+ try
+ {
+ tmpMappings = new HashMap();
+ processWebDotXml(tmpMappings);
+ loadStandardTlds(tmpMappings);
+ processTldsInFileSystem("/WEB-INF/", tmpMappings);
+ }
+ catch (Exception ex)
+ {
+ String msg = Localizer.getMessage("jsp.error.internal.tldinit", ex.getMessage());
+ throw new JasperException(msg, ex);
+ }
+ finally
+ {
+ mappings = tmpMappings;
+ }
+ }
+
+ /*
+ * Populates taglib map described in web.xml.
+ */
+ protected void processWebDotXml(Map tmpMappings) throws Exception
+ {
+
+ InputStream is = null;
+
+ try
+ {
+ // Acquire input stream to web application deployment descriptor
+ String altDDName = (String) ctx.getAttribute(Constants.ALT_DD_ATTR);
+ if (altDDName != null)
+ {
+ try
+ {
+ is = new FileInputStream(altDDName);
+ }
+ catch (FileNotFoundException e)
+ {
+ log.warn(Localizer.getMessage("jsp.error.internal.filenotfound",
+ altDDName));
+ }
+ }
+ else
+ {
+ is = ctx.getResourceAsStream(WEB_XML);
+ if (is == null)
+ {
+ log.warn(Localizer.getMessage("jsp.error.internal.filenotfound",
+ WEB_XML));
+ }
+ }
+
+ if (is == null)
+ {
+ return;
+ }
+
+ // Parse the web application deployment descriptor
+ TreeNode webtld = null;
+ // altDDName is the absolute path of the DD
+ if (altDDName != null)
+ {
+ webtld = new ParserUtils().parseXMLDocument(altDDName, is);
+ }
+ else
+ {
+ webtld = new ParserUtils().parseXMLDocument(WEB_XML, is);
+ }
+
+ // Allow taglib to be an element of the root or jsp-config (JSP2.0)
+ TreeNode jspConfig = webtld.findChild("jsp-config");
+ if (jspConfig != null)
+ {
+ webtld = jspConfig;
+ }
+ Iterator taglibs = webtld.findChildren("taglib");
+ while (taglibs.hasNext())
+ {
+
+ // Parse the next <taglib> element
+ TreeNode taglib = (TreeNode) taglibs.next();
+ String tagUri = null;
+ String tagLoc = null;
+ TreeNode child = taglib.findChild("taglib-uri");
+ if (child != null)
+ tagUri = child.getBody();
+ child = taglib.findChild("taglib-location");
+ if (child != null)
+ tagLoc = child.getBody();
+
+ // Save this location if appropriate
+ if (tagLoc == null)
+ continue;
+ if (uriType(tagLoc) == NOROOT_REL_URI)
+ tagLoc = "/WEB-INF/" + tagLoc;
+ String tagLoc2 = null;
+ if (tagLoc.endsWith(JAR_FILE_SUFFIX))
+ {
+ tagLoc = ctx.getResource(tagLoc).toString();
+ tagLoc2 = "META-INF/taglib.tld";
+ }
+ tmpMappings.put(tagUri, new String[]{tagLoc, tagLoc2}); // SYNC
+ }
+ }
+ finally
+ {
+ if (is != null)
+ {
+ try
+ {
+ is.close();
+ }
+ catch (Throwable t)
+ {
+ }
+ }
+ }
+ }
+
+ protected void loadStandardTlds(Map tmpMappings) throws MalformedURLException // SYNC
+ {
+ if( tagLibJars.size() == 0 )
+ return;
+
+ // Locate the conf/web.xml
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ URL web = loader.getResource("conf/web.xml");
+ URL sarURL = new URL(web, "..");
+ for(int n = 0; n < tagLibJars.size(); n ++)
+ {
+ String jarPath = (String) tagLibJars.get(n);
+ try
+ {
+ URL url = new URL(sarURL, jarPath);
+ String resourcePath = url.toString();
+ log.debug("Scanning for tlds in: "+resourcePath);
+ URLConnection conn = url.openConnection();
+ conn.setUseCaches(false);
+ scanJar(conn, resourcePath, true, tmpMappings); // SYNC
+ }
+ catch (Exception e)
+ {
+ log.debug("Failed to scan: "+jarPath, e);
+ }
+ }
+ }
+
+ /*
+ * Searches the filesystem under /WEB-INF for any TLD files, and adds
+ * an implicit map entry to the taglib map for any TLD that has a <uri>
+ * element.
+ */
+ protected void processTldsInFileSystem(String startPath, Map tmpMappings) // SYNC
+ throws Exception
+ {
+
+ Set dirList = ctx.getResourcePaths(startPath);
+ if (dirList != null)
+ {
+ Iterator it = dirList.iterator();
+ while (it.hasNext())
+ {
+ String path = (String) it.next();
+ if (path.endsWith("/"))
+ {
+ processTldsInFileSystem(path, tmpMappings); // SYNC
+ }
+ if( path.endsWith(".jar") )
+ {
+ URL resURL = ctx.getResource(path);
+ URLConnection conn = resURL.openConnection();
+ conn.setUseCaches(false);
+ this.scanJar(conn, resURL.toString(), false, tmpMappings); // SYNC
+ }
+ else if ( path.endsWith(".tld") == true )
+ {
+ InputStream stream = ctx.getResourceAsStream(path);
+ String uri = null;
+ try
+ {
+ uri = getUriFromTld(path, stream);
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (Throwable t)
+ {
+ // do nothing
+ }
+ }
+ }
+ // Add implicit map entry only if its uri is not already
+ // present in the map
+ if (uri != null && tmpMappings.get(uri) == null) // SYNC
+ {
+ tmpMappings.put(uri, new String[]{path, null}); // SYNC
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Scans the given JarInputStream for TLD files located in META-INF (or a
+ * subdirectory of it), adding an implicit map entry to the taglib map for
+ * any TLD that has a <uri> element.
+ * @param conn - the
+ * @param ignore true if any exceptions raised when processing the given JAR
+ * should be ignored, false otherwise
+ */
+ private void scanJar(URLConnection conn, String resourcePath, boolean ignore, Map tmpMappings) // SYNC
+ throws JasperException, IOException
+ {
+ InputStream connIS = conn.getInputStream();
+ JarInputStream jis = new JarInputStream(connIS);
+ try
+ {
+ JarEntry entry = jis.getNextJarEntry();
+ while( entry != null )
+ {
+ String name = entry.getName();
+ if( name.endsWith(".tld") == false )
+ {
+ entry = jis.getNextJarEntry();
+ continue;
+ }
+
+ EntryInputStream eis = new EntryInputStream(jis);
+ String uri = getUriFromTld(resourcePath, eis);
+ // Add implicit map entry only if its uri is not already
+ // present in the map
+ if (uri != null && tmpMappings.get(uri) == null) // SYNC
+ {
+ tmpMappings.put(uri, new String[]{resourcePath, name}); // SYNC
+ }
+ entry = jis.getNextJarEntry();
+ }
+ }
+ catch (Exception ex)
+ {
+ if (!ignore)
+ {
+ throw new JasperException(ex);
+ }
+ }
+ finally
+ {
+ if( jis != null )
+ {
+ try
+ {
+ jis.close();
+ }
+ catch (Throwable t)
+ {
+ // ignore
+ }
+ }
+
+ if (connIS != null)
+ {
+ try
+ {
+ connIS.close();
+ }
+ catch (Throwable t)
+ {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /*
+ * Returns the value of the uri element of the given TLD, or null if the
+ * given TLD does not contain any such element.
+ */
+ private String getUriFromTld(String resourcePath, InputStream in)
+ throws JasperException
+ {
+ // Parse the tag library descriptor at the specified resource path
+ TreeNode tld = new ParserUtils().parseXMLDocument(resourcePath, in);
+ TreeNode uri = tld.findChild("uri");
+ if (uri != null)
+ {
+ String body = uri.getBody();
+ if (body != null)
+ return body;
+ }
+
+ return null;
+ }
+
+ /**
+ * Used to ignore the close on the jar entry input stream since this
+ * closes the jar stream, not just the entry.
+ */
+ static class EntryInputStream extends InputStream
+ {
+ private JarInputStream jis;
+ EntryInputStream(JarInputStream jis)
+ {
+ this.jis = jis;
+ }
+
+ public int read() throws IOException
+ {
+ return jis.read();
+ }
+
+ public int available() throws IOException
+ {
+ return jis.available();
+ }
+
+ public void close() throws IOException
+ {
+
+ }
+
+ public void reset() throws IOException
+ {
+ jis.reset();
+ }
+
+ public boolean markSupported()
+ {
+ return jis.markSupported();
+ }
+
+ public synchronized void mark(int readlimit)
+ {
+ jis.mark(readlimit);
+ }
+
+ public long skip(long n) throws IOException
+ {
+ return jis.skip(n);
+ }
+
+ public int read(byte b[], int off, int len) throws IOException
+ {
+ return jis.read(b, off, len);
+ }
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jasper/TagLibCache.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,264 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.jca;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.resource.ResourceException;
+import javax.servlet.ServletException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.resource.connectionmanager.CachedConnectionManager;
+
+/**
+ * This valve checks for unclosed connections on a servlet request
+ *
+ * @author <a href="mailto:adrian at jboss.com">Adrian Brock</a>
+ * @version $Revision: 56732 $
+ */
+public class CachedConnectionValve extends ValveBase implements Lifecycle
+{
+ /**
+ * The log
+ */
+ private static final Logger log = Logger.getLogger(CachedConnectionValve.class);
+
+ /**
+ * The info string for this Valve
+ */
+ private static final String info = "CachedConnectionValve/1.0";
+
+ /**
+ * Valve-lifecycle helper object
+ */
+ protected LifecycleSupport support = new LifecycleSupport(this);
+
+ /**
+ * The object name of the cached connection manager
+ */
+ protected String ccmName;
+
+ /**
+ * The cached connection manager
+ */
+ protected CachedConnectionManager ccm;
+
+ /**
+ * The object name of the transaction manager service
+ */
+ protected String tmName;
+
+ /**
+ * The transaction manager
+ */
+ protected TransactionManager tm;
+
+ /**
+ * The unshareable resources
+ */
+ protected Set unsharableResources = new HashSet();
+
+ /**
+ * Create a new valve
+ *
+ * @param ccm the cached connection manager for the valve
+ */
+ public CachedConnectionValve()
+ {
+ super();
+ }
+
+ /**
+ * Get information about this Valve.
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ /**
+ * Get the cached connection manager object name
+ */
+ public String getCachedConnectionManagerObjectName()
+ {
+ return ccmName;
+ }
+
+ /**
+ * Set the cached connection manager object name
+ */
+ public void setCachedConnectionManagerObjectName(String ccmName)
+ {
+ this.ccmName = ccmName;
+ }
+
+ /**
+ * Get the transaction manager object name
+ */
+ public String getTransactionManagerObjectName()
+ {
+ return tmName;
+ }
+
+ /**
+ * Set the transaction manager object name
+ */
+ public void setTransactionManagerObjectName(String tmName)
+ {
+ this.tmName = tmName;
+ }
+
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+ if(ccm == null)
+ throw new IllegalStateException("Please uncomment the dependency on CachedConnectionManager"
+ + " in META-INF/jboss-service.xml of jbossweb-tomcatxxx.sar");
+ try
+ {
+ ccm.pushMetaAwareObject(this, unsharableResources);
+ try
+ {
+ getNext().invoke(request, response);
+ }
+ finally
+ {
+ try
+ {
+ ccm.popMetaAwareObject(unsharableResources);
+ }
+ finally
+ {
+ checkTransactionComplete(request);
+ }
+ }
+ }
+ catch (ResourceException e)
+ {
+ throw new ServletException("Error invoking cached connection manager", e);
+ }
+ }
+
+ // Lifecycle-interface
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ support.addLifecycleListener(listener);
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ support.removeLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return support.findLifecycleListeners();
+ }
+
+ public void start() throws LifecycleException
+ {
+ try
+ {
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ ccm = (CachedConnectionManager) server.getAttribute(new ObjectName(ccmName), "Instance");
+ tm = (TransactionManager) server.getAttribute(new ObjectName(tmName), "TransactionManager");
+ }
+ catch (Exception e)
+ {
+ throw new LifecycleException(e);
+ }
+
+ // TODO unshareable resources
+ support.fireLifecycleEvent(START_EVENT, this);
+ }
+
+ public void stop() throws LifecycleException
+ {
+ support.fireLifecycleEvent(STOP_EVENT, this);
+ unsharableResources.clear();
+ }
+
+ protected void checkTransactionComplete(Request request)
+ {
+ int status = Status.STATUS_NO_TRANSACTION;
+
+ try
+ {
+ status = tm.getStatus();
+ }
+ catch (SystemException ex)
+ {
+ log.error("Failed to get status", ex);
+ }
+
+ switch (status)
+ {
+ case Status.STATUS_ACTIVE:
+ case Status.STATUS_COMMITTING:
+ case Status.STATUS_MARKED_ROLLBACK:
+ case Status.STATUS_PREPARING:
+ case Status.STATUS_ROLLING_BACK:
+ try
+ {
+ tm.rollback();
+ }
+ catch (Exception ex)
+ {
+ log.error("Failed to rollback", ex);
+ }
+ // fall through...
+ case Status.STATUS_PREPARED:
+ String servletName = "<Unknown>";
+ try
+ {
+ Wrapper servlet = request.getWrapper();
+ if (servlet != null)
+ {
+ servletName = servlet.getName();
+ if (servlet.getJspFile() != null)
+ servletName = servlet.getJspFile();
+ }
+ }
+ catch (Throwable ignored)
+ {
+ }
+
+ String msg = "Application error: " + servletName + " did not complete its transaction";
+ log.error(msg);
+ }
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/CachedConnectionValve.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+ <mbean name="CachedConnectionValve"
+ description="Valve checks for unclosed connections on a servlet request"
+ domain="Catalina"
+ group="Valve"
+ type="org.jboss.web.tomcat.service.jca.CachedConnectionValve">
+
+ <attribute name="className"
+ description="Fully qualified class name of the managed object"
+ type="java.lang.String"
+ writeable="false"/>
+
+ <attribute name="debug"
+ description="The debugging detail level for this component"
+ type="int"/>
+
+ <attribute name="containerName"
+ description="Object name of the container"
+ type="javax.management.ObjectName"/>
+
+ <attribute name="cachedConnectionManagerObjectName"
+ description="The cached connection manager"
+ type="java.lang.String"/>
+
+ </mbean>
+
+</mbeans-descriptors>
\ No newline at end of file
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/jca/mbeans-descriptors.xml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,81 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.Manager;
+import org.jboss.metadata.WebMetaData;
+
+/** Common interface for the http session replication managers.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision: 58583 $
+ */
+public interface AbstractJBossManager extends Manager
+{
+ /** Initialize the manager with the web metadata and
+ * @param name
+ * @param webMetaData
+ * @param useJK
+ * @param useLocalCache
+ * @throws ClusteringNotSupportedException
+ */
+ public void init(String name, WebMetaData webMetaData, boolean useJK,
+ boolean useLocalCache)
+ throws ClusteringNotSupportedException;
+
+ /** The session invalidation policy.
+ * @return the invalidation policy constant
+ */
+ public InvalidateSessionPolicy getInvalidateSessionPolicy();
+
+ /**
+ * Retrieve the JvmRoute for the enclosing Engine.
+ *
+ * @return the JvmRoute or null.
+ */
+ public String getJvmRoute();
+
+ /**
+ * Sets a new cookie for the given session id and response
+ *
+ * @param sessionId The session id
+ */
+ public void setNewSessionCookie(String sessionId, HttpServletResponse response);
+
+ /**
+ * Remove the active session locally from the manager without replicating to the cluster. This can be
+ * useful when the session is exipred, for example, where there is not need to propagate the expiration.
+ *
+ * @param session
+ */
+ public void removeLocal(Session session);
+
+ /**
+ * Store the modified session.
+ *
+ * @param session
+ */
+ public boolean storeSession(Session session);
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,31 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+/**
+ * Marker interface for use in aop annotation declaration. For example, can use this in pojo
+ * annotation instead of declaring jboss-aop.xml. For a pojo annotation declaration, just declare
+ * this in the pojo class level, and then there is a corresponding jboss-aop.xml that is needed.
+ * @author Ben Wang
+ */
+public interface AopMarker {
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AopMarker.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,278 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+/**
+ * Implementation of a clustered session for the JBossCacheManager. The replication granularity
+ * level is attribute based; that is, we replicate only the dirty attributes.
+ * We use JBossCache for our internal, deplicated data store.
+ * The internal structure is like in JBossCache:
+ * <pre>
+ * /JSESSION
+ * /hostname
+ * /web_app_path (path + session id is unique)
+ * /id Map(id, session)
+ * (VERSION_KEY, version) // Used for version tracking. version is an Integer.
+ * /ATTRIBUTE Map(attr_key, value)
+ * </pre>
+ * <p/>
+ * Note that the isolation level of the cache dictates the
+ * concurrency behavior. Also note that session and its associated attribtues are stored in different nodes.
+ * This will be ok since cache will take care of concurrency. When replicating, we will need to replicate both
+ * session and its attributes.</p>
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 56542 $
+ */
+class AttributeBasedClusteredSession
+ extends JBossCacheClusteredSession
+{
+ static final long serialVersionUID = -5625209785550936713L;
+ /**
+ * Descriptive information describing this Session implementation.
+ */
+ protected static final String info = "AttributeBasedClusteredSession/1.0";
+
+ // Transient map to store attr changes for replication.
+ private transient Map attrModifiedMap_ = new HashMap();
+ // Note that the removed attr is intentionally stored in a map
+ // instead of a Set so it is faster to lookup and remove.
+ private transient Map attrRemovedMap_ = new HashMap();
+ private static final int REMOVE = 0; // Used to track attribute changes
+ private static final int MODIFY = 1;
+ // TODO why isn't the superclass field sufficient?
+ private transient Map attributes_ = Collections.synchronizedMap(new HashMap());
+
+ public AttributeBasedClusteredSession(JBossCacheManager manager)
+ {
+ super(manager);
+ }
+
+ // ----------------------------------------------- Overridden Public Methods
+
+
+ /**
+ * Override the superclass to additionally reset this class' fields.
+ * <p>
+ * <strong>NOTE:</strong> It is not anticipated that this method will be
+ * called on a ClusteredSession, but we are overriding the method to be
+ * thorough.
+ * </p>
+ */
+ public void recycle()
+ {
+ super.recycle();
+
+ attributes_.clear();
+ clearAttrChangedMaps();
+ }
+
+ /**
+ * Return a string representation of this object.
+ */
+ public String toString()
+ {
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("AttributeBasedClusteredSession[");
+ sb.append(super.toString());
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+ /**
+ * Overrides the superclass version to read in the attributes.
+ */
+ public synchronized void processSessionRepl()
+ {
+ // Replicate the metadata first. Note this will be lightweight since many
+ // of the fields are transient and the attribute map isn't included.
+ if (log.isTraceEnabled())
+ {
+ log.trace("processSessionRepl(): session is dirty. Will increment " +
+ "version from: " + getVersion() + " and replicate.");
+ }
+ this.incrementVersion();
+ proxy_.putSession(realId, this);
+
+ // Go thru the attribute change list
+
+ if (getSessionAttributesDirty())
+ {
+ // Go thru the modified attr list first
+ int modCount = attrModifiedMap_.size();
+ if (modCount == 1)
+ {
+ for (Iterator it = attrModifiedMap_.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Entry) it.next();
+ proxy_.putAttribute(realId, (String) entry.getKey(), entry.getValue());
+ }
+ }
+ else if (modCount > 0)
+ {
+ // It's more efficient to write a map than 2 method calls,
+ // plus it reduces the number of CacheListener notifications
+ proxy_.putAttribute(realId, attrModifiedMap_);
+ }
+
+ // Go thru the remove attr list
+ if (attrRemovedMap_.size() > 0)
+ {
+ for (Iterator it = attrRemovedMap_.keySet().iterator(); it.hasNext(); )
+ {
+ proxy_.removeAttribute(realId, (String) it.next());
+ }
+ }
+
+ clearAttrChangedMaps();
+ }
+
+ sessionAttributesDirty = false;
+ sessionMetadataDirty = false;
+
+ updateLastReplicated();
+ }
+
+ public void removeMyself()
+ {
+ // This is a shortcut to remove session and it's child attributes.
+ proxy_.removeSession(realId);
+ }
+
+ public void removeMyselfLocal()
+ {
+ // Need to evict attribute first before session to clean up everything.
+ // BRIAN -- the attributes *are* already evicted, but we leave the
+ // removeAttributesLocal call here in order to evict the ATTRIBUTE node.
+ // Otherwise empty nodes for the session root and child ATTRIBUTE will
+ // remain in the tree and screw up our list of session names.
+ proxy_.removeAttributesLocal(realId);
+ proxy_.removeSessionLocal(realId);
+ }
+
+ // ------------------------------------------------ JBoss internal abstract method
+
+ /**
+ * Populate the attributes stored in the distributed store to local transient ones.
+ */
+ protected void populateAttributes()
+ {
+ Map map = proxy_.getAttributes(realId);
+
+ // Preserve any local attributes that were excluded from replication
+ Map excluded = removeExcludedAttributes(attributes_);
+ if (excluded != null)
+ map.putAll(excluded);
+
+ attributes_ = Collections.synchronizedMap(map);
+ attrModifiedMap_.clear();
+ attrRemovedMap_.clear();
+ }
+
+ protected Object getJBossInternalAttribute(String name)
+ {
+ Object result = attributes_.get(name);
+
+ // Do dirty check even if result is null, as w/ SET_AND_GET null
+ // still makes us dirty (ensures timely replication w/o using ACCESS)
+ if (isGetDirty(result) && !replicationExcludes.contains(name))
+ {
+ attributeChanged(name, result, MODIFY);
+ }
+
+ return result;
+ }
+
+ protected Object removeJBossInternalAttribute(String name,
+ boolean localCall,
+ boolean localOnly)
+ {
+ Object result = attributes_.remove(name);
+ if (localCall && !replicationExcludes.contains(name))
+ attributeChanged(name, result, REMOVE);
+ return result;
+ }
+
+ protected Map getJBossInternalAttributes()
+ {
+ return attributes_;
+ }
+
+ protected Set getJBossInternalKeys()
+ {
+ return attributes_.keySet();
+ }
+
+ /**
+ * Method inherited from Tomcat. Return zero-length based string if not found.
+ */
+ protected String[] keys()
+ {
+ return ((String[]) getJBossInternalKeys().toArray(EMPTY_ARRAY));
+ }
+
+ protected Object setJBossInternalAttribute(String key, Object value)
+ {
+ Object old = attributes_.put(key, value);
+ if (!replicationExcludes.contains(key))
+ attributeChanged(key, value, MODIFY);
+ return old;
+ }
+
+ protected synchronized void attributeChanged(Object key, Object value, int op)
+ {
+ if (op == MODIFY)
+ {
+ if (attrRemovedMap_.containsKey(key))
+ {
+ attrRemovedMap_.remove(key);
+ }
+ attrModifiedMap_.put(key, value);
+ }
+ else if (op == REMOVE)
+ {
+ if (attrModifiedMap_.containsKey(key))
+ {
+ attrModifiedMap_.remove(key);
+ }
+ attrRemovedMap_.put(key, value);
+ }
+ sessionAttributesDirty();
+ }
+
+ protected synchronized void clearAttrChangedMaps()
+ {
+ attrRemovedMap_.clear();
+ attrModifiedMap_.clear();
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,156 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+import org.jboss.logging.Logger;
+
+/**
+ * This Valve handles batch replication mode. It uses the cache tm to performa batch replication.
+ *
+ * @author Ben Wang
+ * @version $Revision: 60079 $
+ */
+public class BatchReplicationClusteredSessionValve extends ValveBase implements Lifecycle
+{
+ private static Logger log_ = Logger.getLogger(BatchReplicationClusteredSessionValve.class);
+
+ // The info string for this Valve
+ private static final String info = "BatchReplicationClusteredSessionValve/1.0";
+
+ // Valve-lifecycle_ helper object
+ protected LifecycleSupport support = new LifecycleSupport(this);
+
+ protected JBossCacheManager manager_;
+
+ /**
+ * Create a new Valve.
+ *
+ */
+ public BatchReplicationClusteredSessionValve(AbstractJBossManager manager)
+ {
+ super();
+ manager_ = (JBossCacheManager)manager;
+ }
+
+ /**
+ * Get information about this Valve.
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ /**
+ * Valve-chain handler method.
+ * This method gets called when the request goes through the Valve-chain. Our session replication mechanism replicates the
+ * session after request got through the servlet code.
+ *
+ * @param request The request object associated with this request.
+ * @param response The response object associated with this request.
+ */
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+ // Note: we use specfically the tm in cache.
+ TransactionManager tm = manager_.getCacheService().getTransactionManager();
+ if(tm == null)
+ {
+ throw new RuntimeException("BatchReplicationClusteredSessionValve.invoke(): Obtain null tm");
+ }
+
+ // Before we start a tx, get the session. If this is a failover
+ // situation, this will cause data gravitation, which will occur
+ // thus outside of the scope of the tx we are about to start.
+ // JBossCacheManager will ensure the gravitation is in its own tx
+ request.getSession(false);
+
+ // Start a new transaction, we need transaction so all the replication are sent in batch.
+ try
+ {
+ tm.begin();
+
+ // let the servlet invocation go through
+ getNext().invoke(request, response);
+
+ log_.trace("Ready to commit batch replication for field level granularity");
+
+ tm.commit();
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ tm.rollback();
+ }
+ catch (Exception exn)
+ {
+ log_.error("Caught exception rolling back transaction", exn);
+ }
+
+ // We will need to alert Tomcat of this exception.
+ if (e instanceof IOException)
+ throw (IOException) e;
+ if (e instanceof ServletException)
+ throw (ServletException) e;
+ if (e instanceof RuntimeException)
+ throw (RuntimeException) e;
+ throw new RuntimeException(e);
+ }
+ }
+
+ // Lifecylce-interface
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ support.addLifecycleListener(listener);
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ support.removeLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return support.findLifecycleListeners();
+ }
+
+ public void start() throws LifecycleException
+ {
+ support.fireLifecycleEvent(START_EVENT, this);
+ }
+
+ public void stop() throws LifecycleException
+ {
+ support.fireLifecycleEvent(STOP_EVENT, this);
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/BatchReplicationClusteredSessionValve.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,294 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Map;
+
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.AbstractCacheListener;
+import org.jboss.cache.CacheListener.ModificationType;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.logging.Logger;
+import org.jgroups.View;
+
+
+public class CacheListener extends AbstractCacheListener
+{
+ // Element within an FQN that is JSESSION
+ private static final int JSESSION_FQN_INDEX = 0;
+ // Element within an FQN that is the hostname
+ private static final int HOSTNAME_FQN_INDEX = 1;
+ // ELEMENT within an FQN this is the webapp name
+ private static final int WEBAPP_FQN_INDEX = 2;
+ // Element within an FQN that is the session id
+ private static final int SESSION_ID_FQN_INDEX = 3;
+ // Size of an Fqn that points to the root of a session
+ private static final int SESSION_FQN_SIZE = SESSION_ID_FQN_INDEX + 1;
+ // Element within an FQN that is the root of a Pojo attribute map
+ private static final int POJO_ATTRIBUTE_FQN_INDEX = SESSION_ID_FQN_INDEX + 1;
+ // Element within an FQN that is the root of an individual Pojo attribute
+ private static final int POJO_KEY_FQN_INDEX = POJO_ATTRIBUTE_FQN_INDEX + 1;
+ // Size of an Fqn that points to the root of a session
+ private static final int POJO_KEY_FQN_SIZE = POJO_KEY_FQN_INDEX + 1;
+ // The index of the root of a buddy backup subtree
+ private static final int BUDDY_BACKUP_ROOT_OWNER_INDEX = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN.size();
+ // The size of the root of a buddy backup subtree (including owner)
+ private static final int BUDDY_BACKUP_ROOT_OWNER_SIZE = BUDDY_BACKUP_ROOT_OWNER_INDEX + 1;
+
+ private static Logger log_ = Logger.getLogger(CacheListener.class);
+ private JBossCacheWrapper cacheWrapper_;
+ private JBossCacheManager manager_;
+ private String webapp_;
+ private String hostname_;
+ private boolean fieldBased_;
+ // When trying to ignore unwanted notifications, do we check for local activity first?
+ private boolean disdainLocalActivity_;
+
+ CacheListener(JBossCacheWrapper wrapper, JBossCacheManager manager, String hostname, String webapp)
+ {
+ cacheWrapper_ = wrapper;
+ manager_ = manager;
+ hostname_ = hostname;
+ webapp_ = webapp;
+ ReplicationGranularity granularity = manager_.getReplicationGranularity();
+ fieldBased_ = (granularity == ReplicationGranularity.FIELD);
+ // TODO decide if disdaining local activity is always good for REPL_ASYNC
+ disdainLocalActivity_ = (granularity == ReplicationGranularity.SESSION);; // for now
+ }
+
+ // --------------- TreeCacheListener methods ------------------------------------
+
+ @Override
+ public void nodeRemoved(Fqn fqn, boolean pre, boolean isLocal, Map data)
+ {
+ if (pre || isLocal)
+ return;
+
+ boolean isBuddy = isBuddyFqn(fqn);
+
+ // Potential removal of a Pojo where we need to unregister as an Observer.
+ if (fieldBased_
+ && isFqnPojoKeySized(fqn, isBuddy)
+ && isFqnForOurWebapp(fqn, isBuddy))
+ {
+ String sessId = getIdFromFqn(fqn, isBuddy);
+ String attrKey = getPojoKeyFromFqn(fqn, isBuddy);
+ manager_.processRemoteAttributeRemoval(sessId, attrKey);
+ }
+ else if(isFqnSessionRootSized(fqn, isBuddy)
+ && isFqnForOurWebapp(fqn, isBuddy))
+ {
+ // A session has been invalidated from another node;
+ // need to inform manager
+ String sessId = getIdFromFqn(fqn, isBuddy);
+ manager_.processRemoteInvalidation(sessId);
+ }
+ }
+
+ @Override
+ public void nodeModified(Fqn fqn, boolean pre, boolean isLocal, ModificationType type, Map data)
+ {
+ if (pre || isLocal)
+ return;
+
+// // If checking for local activity has a higher likelihood of
+// // catching unwanted notifications than checking fqn size,
+// // do it first
+// if (disdainLocalActivity_)
+// {
+// if (SessionReplicationContext.isLocallyActive())
+// return;
+// }
+
+ boolean isBuddy = isBuddyFqn(fqn);
+ // We only care if there is a chance this is for a session root
+ if (!isFqnSessionRootSized(fqn, isBuddy))
+ return;
+
+// if (!disdainLocalActivity_)
+// {
+// if (SessionReplicationContext.isLocallyActive())
+// return;
+// }
+
+ // We only care if this is for our webapp
+ if (!isFqnForOurWebapp(fqn, isBuddy))
+ return;
+
+ // Query if we have version value in the distributed cache.
+ // If we have a version value, compare the version and invalidate if necessary.
+ // TODO get the key from the passed in data map!!
+// Integer version = (Integer)cacheWrapper_.get(fqn, JBossCacheService.VERSION_KEY);
+ Integer version = (Integer)data.get(JBossCacheService.VERSION_KEY);
+ if(version != null)
+ {
+ String realId = getIdFromFqn(fqn, isBuddy);
+
+ ClusteredSession session = manager_.findLocalSession(realId);
+ if (session == null)
+ {
+ String owner = isBuddy ? getBuddyOwner(fqn) : null;
+ // Notify the manager that an unloaded session has been updated
+ manager_.unloadedSessionChanged(realId, owner);
+ }
+ else if (session.isNewData(version.intValue()))
+ {
+ // Need to invalidate the loaded session
+ session.setOutdatedVersion(version.intValue());
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("nodeDirty(): session in-memory data is " +
+ "invalidated with id: " + realId + " and version: " +
+ version.intValue());
+ }
+ }
+ else if (!isBuddy)
+ {
+ log_.warn("Possible concurrency problem: Replicated version id " +
+ version + " matches in-memory version for session " + realId);
+ }
+ /*else
+ {
+ We have a local session but got a modification for the buddy tree.
+ This means another node is in the process of taking over the session;
+ we don't worry about it
+ }
+ */
+ }
+ else
+ {
+// log_.warn("No VERSION_KEY attribute found in " + fqn);
+ }
+ }
+
+ @Override
+ public void cacheStarted(CacheSPI cache)
+ {
+ // TODO will need to synchronize this with local sessions
+ }
+
+ @Override
+ public void cacheStopped(CacheSPI cache)
+ {
+ // TODO will need to synchronize this with local sessions
+ }
+
+ @Override
+ public void viewChange(View new_view)
+ {
+ // We don't care for this event.
+ }
+
+ @Override
+ public void nodeActivated(Fqn fqn, boolean pre)
+ {
+ // we don't need to handle node activation notification since the
+ // session manager will notify any servlet API listeners upon
+ // loading the session from the distrubted store.
+ }
+
+ // FIXME why would there be a notification of passivation on another node?
+ @Override
+ public void nodePassivated(Fqn fqn, boolean pre)
+ {
+ if (!pre || SessionReplicationContext.isLocallyActive())
+ return;
+
+ boolean isBuddy = isBuddyFqn(fqn);
+
+ // We only deal with events for the root node of a session,
+ // so skip all others
+ if(isFqnSessionRootSized(fqn, isBuddy)
+ && isFqnForOurWebapp(fqn, isBuddy))
+ {
+ // A session has been passivated on another node;
+ // need to inform the manager
+ String realId = getIdFromFqn(fqn, isBuddy);
+ String owner = isBuddy ? getBuddyOwner(fqn) : null;
+ manager_.processSessionPassivation(realId, owner);
+ }
+
+ }
+
+ private boolean isFqnForOurWebapp(Fqn fqn, boolean isBuddy)
+ {
+ try
+ {
+ if (webapp_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + WEBAPP_FQN_INDEX : WEBAPP_FQN_INDEX))
+ && hostname_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + HOSTNAME_FQN_INDEX : HOSTNAME_FQN_INDEX))
+ && JBossCacheService.SESSION.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + JSESSION_FQN_INDEX : JSESSION_FQN_INDEX)))
+ return true;
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ // can't be ours; too small; just fall through
+ }
+
+ return false;
+ }
+
+ private static boolean isFqnSessionRootSized(Fqn fqn, boolean isBuddy)
+ {
+ return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_FQN_SIZE : SESSION_FQN_SIZE);
+ }
+
+ private static boolean isFqnPojoKeySized(Fqn fqn, boolean isBuddy)
+ {
+ return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_SIZE : POJO_KEY_FQN_SIZE);
+ }
+
+ private static String getIdFromFqn(Fqn fqn, boolean isBuddy)
+ {
+ return (String)fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_ID_FQN_INDEX : SESSION_ID_FQN_INDEX);
+ }
+
+ private static String getPojoKeyFromFqn(Fqn fqn, boolean isBuddy)
+ {
+ return (String) fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_INDEX: POJO_KEY_FQN_INDEX);
+ }
+
+ private static boolean isBuddyFqn(Fqn fqn)
+ {
+ try
+ {
+ return BuddyManager.BUDDY_BACKUP_SUBTREE.equals(fqn.get(0));
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ // Can only happen if fqn is ROOT, and we shouldn't get
+ // notifications for ROOT.
+ // If it does, just means it's not a buddy
+ return false;
+ }
+ }
+
+ /**
+ * Extracts the owner portion of an buddy subtree Fqn.
+ *
+ * @param fqn An Fqn that is a child of the buddy backup root node.
+ */
+ private static String getBuddyOwner(Fqn fqn)
+ {
+ return (String) fqn.get(BUDDY_BACKUP_ROOT_OWNER_INDEX);
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1524 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.beans.PropertyChangeSupport;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Session;
+import org.apache.catalina.session.StandardSession;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.StringManager;
+import org.jboss.logging.Logger;
+
+/**
+ * Abstract base class for session clustering based on StandardSession. Different session
+ * replication strategy can be implemented such as session- or attribute-based ones.
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 58585 $
+ */
+abstract class ClusteredSession
+ extends StandardSession
+ implements Externalizable
+{
+ private static final long serialVersionUID = -758573655613558722L;
+ protected static Logger log = Logger.getLogger(ClusteredSession.class);
+
+ // ----------------------------------------------------- Instance Variables
+ /**
+ * Descriptive information describing this Session implementation.
+ */
+ protected static final String info = "ClusteredSession/1.0";
+
+ /**
+ * Set of attribute names which are not allowed to be replicated/persisted.
+ */
+ protected static final String[] excludedAttributes = {
+ Globals.SUBJECT_ATTR
+ };
+
+ /**
+ * Set containing all members of {@link #excludedAttributes}.
+ */
+ protected static final Set replicationExcludes;
+ static
+ {
+ HashSet set = new HashSet();
+ for (int i = 0; i < excludedAttributes.length; i++)
+ {
+ set.add(excludedAttributes[i]);
+ }
+ replicationExcludes = Collections.unmodifiableSet(set);
+ }
+
+ protected InvalidateSessionPolicy invalidationPolicy;
+
+ /**
+ * If true, means the local in-memory session data contains
+ * changes that have not been published to the distributed cache.
+ *
+ * @deprecated not used
+ */
+ protected transient boolean isSessionModifiedSinceLastSave;
+
+ /**
+ * If true, means the local in-memory session data contains metadata
+ * changes that have not been published to the distributed cache.
+ */
+ protected transient boolean sessionMetadataDirty;
+
+ /**
+ * If true, means the local in-memory session data contains attribute
+ * changes that have not been published to the distributed cache.
+ */
+ protected transient boolean sessionAttributesDirty;
+
+ /**
+ * The last version that was passed to {@link #setOutdatedVersion} or
+ * <code>0</code> if <code>setIsOutdated(false)</code> was subsequently called.
+ */
+ protected transient int outdatedVersion;
+
+ /**
+ * The last time {@link #setIsOutdated setIsOutdated(true)} was called or
+ * <code>0</code> if <code>setIsOutdated(false)</code> was subsequently called.
+ */
+ protected transient long outdatedTime;
+
+ /**
+ * Version number to track cache invalidation. If any new version number is
+ * greater than this one, it means the data it holds is newer than this one.
+ */
+ protected int version;
+
+ /**
+ * The session's id with any jvmRoute removed.
+ */
+ protected transient String realId;
+
+ /**
+ * Whether JK is being used, in which case our realId will
+ * not match our id
+ */
+ private transient boolean useJK;
+
+ /**
+ * Timestamp when we were last replicated.
+ */
+ protected transient long lastReplicated;
+
+ /**
+ * Maximum percentage of the inactive interval this session
+ * should be allowed to go unreplicated if access to the
+ * session doesn't mark it as dirty. Drives the calculation
+ * of maxUnreplicatedInterval.
+ */
+ protected transient int maxUnreplicatedFactor = 80;
+
+ /**
+ * Maximum number of milliseconds this session
+ * should be allowed to go unreplicated if access to the
+ * session doesn't mark it as dirty.
+ */
+ protected transient long maxUnreplicatedInterval;
+
+ /**
+ * Whether any of this session's attributes implement
+ * HttpSessionActivationListener.
+ */
+ protected transient Boolean hasActivationListener;
+
+ /**
+ * Has this session only been accessed once?
+ */
+ protected transient boolean firstAccess;
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm =
+ StringManager.getManager(ClusteredSession.class.getPackage().getName());
+
+ /**
+ * Create a new ClusteredSession.
+ *
+ * @param manager the session's manager
+ *
+ * @deprecated use {@link ClusteredSession(AbstractJBossManager, boolean)}
+ */
+ protected ClusteredSession(AbstractJBossManager manager)
+ {
+ this(manager, false);
+ }
+
+ protected ClusteredSession(AbstractJBossManager manager, boolean useJK)
+ {
+ super(manager);
+ invalidationPolicy = manager.getInvalidateSessionPolicy();
+ this.useJK = useJK;
+ this.firstAccess = true;
+ calcMaxUnreplicatedInterval();
+ }
+
+ /**
+ * Check to see if the session data is still valid. Outdated here means
+ * that the in-memory data is not in sync with one in the data store.
+ *
+ * @return
+ */
+ public boolean isOutdated()
+ {
+ return thisAccessedTime < outdatedTime;
+ }
+
+ /**
+ * Marks this session as outdated or up-to-date vis-a-vis the distributed
+ * cache.
+ *
+ * @param outdated
+ *
+ * @deprecated use {@link #setOutdatedVersion(int)} and {@link #clearOutdated()}
+ */
+ public void setIsOutdated(boolean outdated)
+ {
+ if (outdated)
+ outdatedTime = System.currentTimeMillis();
+ else
+ clearOutdated();
+ }
+
+ public void setOutdatedVersion(int version)
+ {
+ this.outdatedVersion = version;
+ outdatedTime = System.currentTimeMillis();
+ }
+
+ public void clearOutdated()
+ {
+ // Only overwrite the access time if access() hasn't been called
+ // since setOutdatedVersion() was called
+ if (outdatedTime > thisAccessedTime)
+ {
+ lastAccessedTime = thisAccessedTime;
+ thisAccessedTime = outdatedTime;
+ }
+ outdatedTime = 0;
+
+ // Only overwrite the version if the outdated version is greater
+ // Otherwise when we first unmarshal a session that has been
+ // replicated many times, we will reset the version to 0
+ if (outdatedVersion > version)
+ version = outdatedVersion;
+
+ outdatedVersion = 0;
+ }
+
+ public void updateAccessTimeFromOutdatedTime()
+ {
+ if (outdatedTime > thisAccessedTime)
+ {
+ lastAccessedTime = thisAccessedTime;
+ thisAccessedTime = outdatedTime;
+ }
+ outdatedTime = 0;
+ }
+
+ /**
+ * Gets the session id with any appended jvmRoute info removed.
+ *
+ * @see #getUseJK()
+ */
+ public String getRealId()
+ {
+ return realId;
+ }
+
+ private void parseRealId(String sessionId)
+ {
+ String newId = null;
+ if (useJK)
+ newId = Util.getRealId(sessionId);
+ else
+ newId = sessionId;
+
+ // realId is used in a lot of map lookups, so only replace it
+ // if the new id is actually different -- preserve object identity
+ if (!newId.equals(realId))
+ realId = newId;
+ }
+
+ /**
+ * This is called specifically for failover case using mod_jk where the new
+ * session has this node name in there. As a result, it is safe to just
+ * replace the id since the backend store is using the "real" id
+ * without the node name.
+ *
+ * @param id
+ */
+ public void resetIdWithRouteInfo(String id)
+ {
+ this.id = id;
+ parseRealId(id);
+ }
+
+ public boolean getUseJK()
+ {
+ return useJK;
+ }
+
+ /**
+ * Check to see if the input version number is greater than I am. If it is,
+ * it means we will need to invalidate the in-memory cache.
+ * @param version
+ * @return
+ */
+ public boolean isNewData(int version)
+ {
+ return (this.version < version);
+ }
+
+ public int getVersion()
+ {
+ return version;
+ }
+
+ public void setVersion(int version)
+ {
+ this.version = version;
+ }
+
+ /**
+ * There are couple ways to generate this version number.
+ * But we will stick with the simple one of incrementing for now.
+ *
+ * @return the new version
+ */
+ public int incrementVersion()
+ {
+ return version++;
+ }
+
+ /**
+ * Gets the maximum percentage of the <code>maxInactiveInterval</code>
+ * beyond which a session should be replicated upon access even if it
+ * isn't dirty. Used to ensure that even a read-only session gets
+ * replicated before it expires, so that it isn't removed from other
+ * nodes.
+ *
+ * @return an int between 1 and 100, or -1 if replicating on access is
+ * disabled
+ */
+ public int getMaxUnreplicatedFactor()
+ {
+ return maxUnreplicatedFactor;
+ }
+
+ /**
+ * Sets the maximum percentage of the <code>maxInactiveInterval</code>
+ * beyond which a session should be replicated upon access even if it
+ * isn't dirty. Used to ensure that even a read-only session gets
+ * replicated before it expires, so that it isn't removed from other
+ * nodes.
+ *
+ * @param maxUnreplicatedFactor an int between 1 and 100, or -1 to
+ * disable replicating on access
+ *
+ * @throws IllegalArgumentException if the factor isn't -1 or between
+ * 1 and 100
+ */
+ public void setMaxUnreplicatedFactor(int factor)
+ {
+ if ((factor != -1 && factor < 1) || factor > 100)
+ throw new IllegalArgumentException("Invalid factor " + factor +
+ " -- must be between 1 and 100 or -1");
+ this.maxUnreplicatedFactor = factor;
+ calcMaxUnreplicatedInterval();
+ }
+
+
+ /**
+ * Overrides the superclass to calculate
+ * {@link #getMaxUnreplicatedInterval() maxUnreplicatedInterval}.
+ */
+ public void setMaxInactiveInterval(int interval)
+ {
+ super.setMaxInactiveInterval(interval);
+ calcMaxUnreplicatedInterval();
+ sessionMetadataDirty();
+ }
+
+ /**
+ * Gets the time {@link #updateLastReplicated()} was last called, or
+ * <code>0</code> if it has never been called.
+ */
+ public long getLastReplicated()
+ {
+ return lastReplicated;
+ }
+
+ /**
+ * Sets the {@link #getLastReplicated() lastReplicated} field to
+ * the current time.
+ */
+ public void updateLastReplicated()
+ {
+ lastReplicated = System.currentTimeMillis();
+ }
+
+ public long getMaxUnreplicatedInterval()
+ {
+ return maxUnreplicatedInterval;
+ }
+
+ public boolean getExceedsMaxUnreplicatedInterval()
+ {
+ boolean result = false;
+
+ if (maxUnreplicatedInterval > 0) // -1 means ignore; 0 means expire now
+ {
+ result = ((System.currentTimeMillis() - lastReplicated) >= maxUnreplicatedInterval);
+ }
+
+ return result;
+ }
+
+ private void calcMaxUnreplicatedInterval()
+ {
+ if (maxInactiveInterval < 0 || maxUnreplicatedFactor < 0)
+ maxUnreplicatedInterval = -1;
+ else
+ maxUnreplicatedInterval = maxInactiveInterval * maxUnreplicatedFactor / 100;
+ }
+
+ /**
+ * This is called after loading a session to initialize the transient values.
+ *
+ * @param manager
+ */
+ public abstract void initAfterLoad(AbstractJBossManager manager);
+
+ /**
+ * Propogate session to the internal store.
+ */
+ public abstract void processSessionRepl();
+
+ /**
+ * Remove myself from the internal store.
+ */
+ public abstract void removeMyself();
+
+ /**
+ * Remove myself from the <t>local</t> internal store.
+ */
+ public abstract void removeMyselfLocal();
+
+
+ // ----------------------------------------------- Overridden Public Methods
+
+ public void access()
+ {
+ super.access();
+
+ // JBAS-3528. If it's not the first access, make sure
+ // the 'new' flag is correct
+ if (!firstAccess && isNew)
+ {
+ setNew(false);
+ }
+
+ if (invalidationPolicy == InvalidateSessionPolicy.ACCESS)
+ {
+ this.sessionMetadataDirty();
+ }
+ }
+
+
+ public void endAccess()
+ {
+ super.endAccess();
+
+ if (firstAccess)
+ {
+ firstAccess = false;
+ // Tomcat marks the session as non new, but that's not really
+ // accurate per SRV.7.2, as the second request hasn't come in yet
+ // So, we fix that
+ isNew = true;
+ }
+ }
+
+ public Object getAttribute(String name)
+ {
+
+ if (!isValid())
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.getAttribute.ise"));
+
+ return getAttributeInternal(name);
+ }
+
+ public Enumeration getAttributeNames()
+ {
+ if (!isValid())
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.getAttributeNames.ise"));
+
+ return (new Enumerator(getAttributesInternal().keySet(), true));
+ }
+
+ public void setAttribute(String name, Object value)
+ {
+ // Name cannot be null
+ if (name == null)
+ throw new IllegalArgumentException
+ (sm.getString("clusteredSession.setAttribute.namenull"));
+
+ // Null value is the same as removeAttribute()
+ if (value == null)
+ {
+ removeAttribute(name);
+ return;
+ }
+
+ // Validate our current state
+ if (!isValid())
+ {
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.setAttribute.ise"));
+ }
+
+ if (canAttributeBeReplicated(value) == false)
+ {
+ throw new IllegalArgumentException
+ (sm.getString("clusteredSession.setAttribute.iae"));
+ }
+ // Construct an event with the new value
+ HttpSessionBindingEvent event = null;
+
+ // Call the valueBound() method if necessary
+ if (value instanceof HttpSessionBindingListener)
+ {
+ event = new HttpSessionBindingEvent(getSession(), name, value);
+ try
+ {
+ ((HttpSessionBindingListener) value).valueBound(event);
+ }
+ catch (Throwable t)
+ {
+ manager.getContainer().getLogger().error(sm.getString("standardSession.bindingEvent"), t);
+ }
+ }
+
+ // Replace or add this attribute
+ Object unbound = setInternalAttribute(name, value);
+
+ // Call the valueUnbound() method if necessary
+ if ((unbound != null) && (unbound != value) &&
+ (unbound instanceof HttpSessionBindingListener))
+ {
+ try
+ {
+ ((HttpSessionBindingListener) unbound).valueUnbound
+ (new HttpSessionBindingEvent(getSession(), name));
+ }
+ catch (Throwable t)
+ {
+ manager.getContainer().getLogger().error(sm.getString("standardSession.bindingEvent"), t);
+ }
+ }
+
+ // Notify interested application event listeners
+ Context context = (Context) manager.getContainer();
+ Object listeners[] = context.getApplicationEventListeners();
+ if (listeners == null)
+ return;
+ for (int i = 0; i < listeners.length; i++)
+ {
+ if (!(listeners[i] instanceof HttpSessionAttributeListener))
+ continue;
+ HttpSessionAttributeListener listener =
+ (HttpSessionAttributeListener) listeners[i];
+ try
+ {
+ if (unbound != null)
+ {
+ fireContainerEvent(context,
+ "beforeSessionAttributeReplaced",
+ listener);
+ if (event == null)
+ {
+ event = new HttpSessionBindingEvent
+ (getSession(), name, unbound);
+ }
+ listener.attributeReplaced(event);
+ fireContainerEvent(context,
+ "afterSessionAttributeReplaced",
+ listener);
+ }
+ else
+ {
+ fireContainerEvent(context,
+ "beforeSessionAttributeAdded",
+ listener);
+ if (event == null)
+ {
+ event = new HttpSessionBindingEvent
+ (getSession(), name, value);
+ }
+ listener.attributeAdded(event);
+ fireContainerEvent(context,
+ "afterSessionAttributeAdded",
+ listener);
+ }
+ }
+ catch (Throwable t)
+ {
+ try
+ {
+ if (unbound != null)
+ {
+ fireContainerEvent(context,
+ "afterSessionAttributeReplaced",
+ listener);
+ }
+ else
+ {
+ fireContainerEvent(context,
+ "afterSessionAttributeAdded",
+ listener);
+ }
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
+ }
+ }
+ }
+
+
+ /**
+ * Returns whether the attribute's type is one that can be replicated.
+ *
+ * @param attribute the attribute
+ * @return <code>true</code> if <code>attribute</code> is <code>null</code>,
+ * <code>Serializable</code> or an array of primitives.
+ */
+ protected boolean canAttributeBeReplicated(Object attribute)
+ {
+ if (attribute instanceof Serializable || attribute == null)
+ return true;
+ Class clazz = attribute.getClass().getComponentType();
+ return (clazz != null && clazz.isPrimitive());
+ }
+
+ /**
+ * Invalidates this session and unbinds any objects bound to it.
+ * Overridden here to remove across the cluster instead of just expiring.
+ *
+ * @exception IllegalStateException if this method is called on
+ * an invalidated session
+ */
+ public void invalidate()
+ {
+ if (!isValid())
+ throw new IllegalStateException(sm.getString("clusteredSession.invalidate.ise"));
+
+ // Cause this session to expire globally
+ boolean notify = true;
+ boolean localCall = true;
+ boolean localOnly = false;
+ expire(notify, localCall, localOnly);
+ }
+
+
+ /**
+ * Overrides the {@link StandardSession#isValid() superclass method}
+ * to call {@ #isValid(boolean) isValid(true)}.
+ */
+ public boolean isValid()
+ {
+ return isValid(true);
+ }
+
+ /**
+ * Returns whether the current session is still valid, but
+ * only calls {@link #expire(boolean)} for timed-out sessions
+ * if <code>expireIfInvalid</code> is <code>true</code>.
+ *
+ * @param expireIfInvalid <code>true</code> if sessions that have
+ * been timed out should be expired
+ */
+ public boolean isValid(boolean expireIfInvalid)
+ {
+ if (this.expiring)
+ {
+ return true;
+ }
+
+ if (!this.isValid)
+ {
+ return false;
+ }
+
+ if (ACTIVITY_CHECK && accessCount.get() > 0)
+ {
+ return true;
+ }
+
+ if (maxInactiveInterval >= 0)
+ {
+ long timeNow = System.currentTimeMillis();
+ int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);
+ if (timeIdle >= maxInactiveInterval)
+ {
+ if (expireIfInvalid)
+ expire(true);
+ else
+ return false;
+ }
+ }
+
+ return (this.isValid);
+
+ }
+
+ /**
+ * Expires the session, but in such a way that other cluster nodes
+ * are unaware of the expiration.
+ *
+ * @param notify
+ */
+ public void expire(boolean notify)
+ {
+ boolean localCall = true;
+ boolean localOnly = true;
+ expire(notify, localCall, localOnly);
+ }
+
+ /**
+ * Expires the session, notifying listeners and possibly the manager.
+ * <p>
+ * <strong>NOTE:</strong> The manager will only be notified of the expiration
+ * if <code>localCall</code> is <code>true</code>; otherwise it is the
+ * responsibility of the caller to notify the manager that the session is
+ * expired. (In the case of JBossCacheManager, it is the manager itself
+ * that makes such a call, so it of course is aware).
+ * </p>
+ *
+ * @param notify whether servlet spec listeners should be notified
+ * @param localCall <code>true</code> if this call originated due to local
+ * activity (such as a session invalidation in user code
+ * or an expiration by the local background processing
+ * thread); <code>false</code> if the expiration
+ * originated due to some kind of event notification
+ * from the cluster.
+ * @param localOnly <code>true</code> if the expiration should not be
+ * announced to the cluster, <code>false</code> if other
+ * cluster nodes should be made aware of the expiration.
+ * Only meaningful if <code>localCall</code> is
+ * <code>true</code>.
+ */
+ public void expire(boolean notify, boolean localCall, boolean localOnly)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("The session has expired with id: " + id +
+ " -- is it local? " + localOnly);
+ }
+
+ // If another thread is already doing this, stop
+ if (expiring)
+ return;
+
+ synchronized (this)
+ {
+ // If we had a race to this sync block, another thread may
+ // have already completed expiration. If so, don't do it again
+ if (!isValid)
+ return;
+
+ if (manager == null)
+ return;
+
+ expiring = true;
+
+ // Notify interested application event listeners
+ // FIXME - Assumes we call listeners in reverse order
+ Context context = (Context) manager.getContainer();
+ Object listeners[] = context.getApplicationLifecycleListeners();
+ if (notify && (listeners != null))
+ {
+ HttpSessionEvent event =
+ new HttpSessionEvent(getSession());
+ for (int i = 0; i < listeners.length; i++)
+ {
+ int j = (listeners.length - 1) - i;
+ if (!(listeners[j] instanceof HttpSessionListener))
+ continue;
+ HttpSessionListener listener =
+ (HttpSessionListener) listeners[j];
+ try
+ {
+ fireContainerEvent(context,
+ "beforeSessionDestroyed",
+ listener);
+ listener.sessionDestroyed(event);
+ fireContainerEvent(context,
+ "afterSessionDestroyed",
+ listener);
+ }
+ catch (Throwable t)
+ {
+ try
+ {
+ fireContainerEvent(context,
+ "afterSessionDestroyed",
+ listener);
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("standardSession.sessionEvent"), t);
+ }
+ }
+ }
+ accessCount = null;
+
+ // Notify interested session event listeners.
+ if (notify)
+ {
+ fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
+ }
+
+ // JBAS-1360 -- Unbind any objects associated with this session
+ String keys[] = keys();
+ for (int i = 0; i < keys.length; i++)
+ removeAttributeInternal(keys[i], localCall, localOnly, notify);
+
+ // Remove this session from our manager's active sessions
+ removeFromManager(localCall, localOnly);
+
+ // We have completed expire of this session
+ setValid(false);
+ expiring = false;
+ }
+
+ }
+
+ /**
+ * Advise our manager to remove this expired session.
+ *
+ * @param localCall whether this call originated from local activity
+ * or from a remote invalidation. In this default
+ * implementation, this parameter is ignored.
+ * @param localOnly whether the rest of the cluster should be made aware
+ * of the removal
+ */
+ protected void removeFromManager(boolean localCall, boolean localOnly)
+ {
+ if(localOnly)
+ {
+ ((AbstractJBossManager) manager).removeLocal(this);
+ }
+ else
+ {
+ manager.remove(this);
+ }
+ }
+
+ public void passivate()
+ {
+ // Notify interested session event listeners
+ fireSessionEvent(Session.SESSION_PASSIVATED_EVENT, null);
+
+ if (hasActivationListener != Boolean.FALSE)
+ {
+ boolean hasListener = false;
+
+ // Notify ActivationListeners
+ HttpSessionEvent event = null;
+ String keys[] = keys();
+ Map attrs = getAttributesInternal();
+ for (int i = 0; i < keys.length; i++)
+ {
+ Object attribute = attrs.get(keys[i]);
+ if (attribute instanceof HttpSessionActivationListener)
+ {
+ hasListener = true;
+
+ if (event == null)
+ event = new HttpSessionEvent(getSession());
+ try
+ {
+ ((HttpSessionActivationListener)attribute).sessionWillPassivate(event);
+ }
+ catch (Throwable t)
+ {
+ manager.getContainer().getLogger().error
+ (sm.getString("clusteredSession.attributeEvent"), t);
+ }
+ }
+ }
+
+ hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+ }
+ }
+
+ public void activate()
+ {
+ // Notify interested session event listeners
+ fireSessionEvent(Session.SESSION_ACTIVATED_EVENT, null);
+
+ if (hasActivationListener != Boolean.FALSE)
+ {
+ // Notify ActivationListeners
+
+ boolean hasListener = false;
+
+ HttpSessionEvent event = null;
+ String keys[] = keys();
+ Map attrs = getAttributesInternal();
+ for (int i = 0; i < keys.length; i++)
+ {
+ Object attribute = attrs.get(keys[i]);
+ if (attribute instanceof HttpSessionActivationListener)
+ {
+ hasListener = true;
+ if (event == null)
+ event = new HttpSessionEvent(getSession());
+ try
+ {
+ ((HttpSessionActivationListener)attribute).sessionDidActivate(event);
+ }
+ catch (Throwable t)
+ {
+ manager.getContainer().getLogger().error
+ (sm.getString("clusteredSession.attributeEvent"), t);
+ }
+ }
+ }
+
+ hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+ }
+ }
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// public void removeNote(String name)
+// {
+// // FormAuthenticator removes the username and password because
+// // it assumes they are not needed if the Principal is cached,
+// // but they are needed if the session fails over, so ignore
+// // the removal request.
+// // TODO discuss this on Tomcat dev list to see if a better
+// // way of handling this can be found
+// if (Constants.SESS_USERNAME_NOTE.equals(name)
+// || Constants.SESS_PASSWORD_NOTE.equals(name))
+// {
+// if (log.isDebugEnabled())
+// {
+// log.debug("removeNote(): ignoring removal of note " + name);
+// }
+// }
+// else
+// {
+// super.removeNote(name);
+// }
+//
+// }
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// public void setNote(String name, Object value)
+// {
+// super.setNote(name, value);
+//
+// if (Constants.SESS_USERNAME_NOTE.equals(name)
+// || Constants.SESS_PASSWORD_NOTE.equals(name))
+// {
+// sessionIsDirty();
+// }
+// }
+
+ /**
+ * Override the superclass to additionally reset this class' fields.
+ * <p>
+ * <strong>NOTE:</strong> It is not anticipated that this method will be
+ * called on a ClusteredSession, but we are overriding the method to be
+ * thorough.
+ * </p>
+ */
+ public void recycle()
+ {
+ super.recycle();
+
+ // Fields that the superclass isn't clearing
+ listeners.clear();
+ support = new PropertyChangeSupport(this);
+
+ invalidationPolicy = InvalidateSessionPolicy.ACCESS;
+ outdatedTime = 0;
+ outdatedVersion = 0;
+ sessionAttributesDirty = false;
+ sessionMetadataDirty = false;
+ realId = null;
+ useJK = false;
+ version = 0;
+ hasActivationListener = null;
+ lastReplicated = 0;
+ maxUnreplicatedFactor = 80;
+ calcMaxUnreplicatedInterval();
+ }
+
+ /**
+ * Set the creation time for this session. This method is called by the
+ * Manager when an existing Session instance is reused.
+ *
+ * @param time The new creation time
+ */
+ public void setCreationTime(long time)
+ {
+ super.setCreationTime(time);
+ sessionMetadataDirty();
+ }
+
+ /**
+ * Overrides the superclass method to also set the
+ * {@link #getRealId() realId} property.
+ */
+ public void setId(String id)
+ {
+ // Parse the real id first, as super.setId() calls add(),
+ // which depends on having the real id
+ parseRealId(id);
+ super.setId(id);
+ }
+
+ /**
+ * Set the authenticated Principal that is associated with this Session.
+ * This provides an <code>Authenticator</code> with a means to cache a
+ * previously authenticated Principal, and avoid potentially expensive
+ * <code>Realm.authenticate()</code> calls on every request.
+ *
+ * @param principal The new Principal, or <code>null</code> if none
+ */
+ public void setPrincipal(Principal principal)
+ {
+
+ Principal oldPrincipal = this.principal;
+ this.principal = principal;
+ support.firePropertyChange("principal", oldPrincipal, this.principal);
+
+ if ((oldPrincipal != null && !oldPrincipal.equals(principal)) ||
+ (oldPrincipal == null && principal != null))
+ sessionMetadataDirty();
+
+ }
+
+ public void setNew(boolean isNew)
+ {
+ super.setNew(isNew);
+ // Don't replicate metadata just 'cause its the second request
+ // The only effect of this is if someone besides a request
+ // deserializes metadata from the distributed cache, this
+ // field may be out of date.
+ // If a request accesses the session, the access() call will
+ // set isNew=false, so the request will see the correct value
+ // sessionMetadataDirty();
+ }
+
+ public void setValid(boolean isValid)
+ {
+ super.setValid(isValid);
+ sessionMetadataDirty();
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append("id: " +id).append(" lastAccessedTime: " +lastAccessedTime).append(
+ " version: " +version).append(" lastOutdated: " + outdatedTime);
+
+ return buf.toString();
+ }
+
+ // --------------------------------------------------------- Externalizable
+
+ /**
+ * Reads all non-transient state from the ObjectOutput <i>except
+ * the attribute map</i>. Subclasses that wish the attribute map
+ * to be read should override this method and
+ * {@link #writeExternal(ObjectOutput) writeExternal()}.
+ *
+ * <p>
+ * This method is deliberately public so it can be used to reset
+ * the internal state of a session object using serialized
+ * contents replicated from another JVM via JBossCache.
+ * </p>
+ *
+ * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+ */
+ public void readExternal(ObjectInput in)
+ throws IOException, ClassNotFoundException
+ {
+ synchronized (this)
+ {
+ // From StandardSession
+ id = in.readUTF();
+ creationTime = in.readLong();
+ lastAccessedTime = in.readLong();
+ maxInactiveInterval = in.readInt();
+ isNew = in.readBoolean();
+ isValid = in.readBoolean();
+ thisAccessedTime = in.readLong();
+
+ // From ClusteredSession
+
+ invalidationPolicy = InvalidateSessionPolicy.fromInt(in.readInt());
+ version = in.readInt();
+
+ // Get our id without any jvmRoute appended
+ parseRealId(id);
+
+ // We no longer know if we have an activationListener
+ hasActivationListener = null;
+
+ // If the session has been replicated, any subsequent
+ // access cannot be the first.
+ this.firstAccess = false;
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// // Session notes -- for FORM auth apps, allow replicated session
+// // to be used without requiring a new login
+// // We use the superclass set/removeNote calls here to bypass
+// // the custom logic we've added
+// String username = (String) in.readObject();
+// if (username != null)
+// {
+// super.setNote(Constants.SESS_USERNAME_NOTE, username);
+// }
+// else
+// {
+// super.removeNote(Constants.SESS_USERNAME_NOTE);
+// }
+// String password = (String) in.readObject();
+// if (password != null)
+// {
+// super.setNote(Constants.SESS_PASSWORD_NOTE, password);
+// }
+// else
+// {
+// super.removeNote(Constants.SESS_PASSWORD_NOTE);
+// }
+ }
+ }
+
+
+ /**
+ * Writes all non-transient state to the ObjectOutput <i>except
+ * the attribute map</i>. Subclasses that wish the attribute map
+ * to be written should override this method and append it.
+ *
+ * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+ */
+ public void writeExternal(ObjectOutput out)
+ throws IOException
+ {
+ synchronized (this)
+ {
+ // From StandardSession
+ out.writeUTF(id);
+ out.writeLong(creationTime);
+ out.writeLong(lastAccessedTime);
+ out.writeInt(maxInactiveInterval);
+ out.writeBoolean(isNew);
+ out.writeBoolean(isValid);
+ out.writeLong(thisAccessedTime);
+
+ // From ClusteredSession
+ out.writeInt(invalidationPolicy.ordinal());
+ out.writeInt(version);
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// // Session notes -- for FORM auth apps, allow replicated session
+// // to be used without requiring a new login
+// String username = (String) getNote(Constants. SESS_USERNAME_NOTE);
+// log.debug(Constants.SESS_USERNAME_NOTE + " = " + username);
+// out.writeObject(username);
+// String password = (String) getNote(Constants.SESS_PASSWORD_NOTE);
+// log.debug(Constants.SESS_PASSWORD_NOTE + " = " + password);
+// out.writeObject(password);
+ }
+ }
+
+ // ----------------------------------------------------- Protected Methods
+
+ /**
+ * Removes any attribute whose name is found in {@link #excludedAttributes}
+ * from <code>attributes</code> and returns a Map of all such attributes.
+ *
+ * @param attributes source map from which excluded attributes are to be
+ * removed.
+ *
+ * @return Map that contains any attributes removed from
+ * <code>attributes</code>, or <code>null</code> if no attributes
+ * were removed.
+ */
+ protected static Map removeExcludedAttributes(Map attributes)
+ {
+ Map excluded = null;
+ for (int i = 0; i < excludedAttributes.length; i++) {
+ Object attr = attributes.remove(excludedAttributes[i]);
+ if (attr != null)
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("Excluding attribute " + excludedAttributes[i] +
+ " from replication");
+ }
+ if (excluded == null)
+ {
+ excluded = new HashMap();
+ }
+ excluded.put(excludedAttributes[i], attr);
+ }
+ }
+
+ return excluded;
+ }
+
+ /**
+ * Reads all non-transient state from the ObjectOutput <i>except
+ * the attribute map</i>. Subclasses that wish the attribute map
+ * to be read should override this method and
+ * {@link #writeExternal(ObjectOutput) writeExternal()}.
+ *
+ * <p>
+ * This method is deliberately public so it can be used to reset
+ * the internal state of a session object using serialized
+ * contents replicated from another JVM via JBossCache.
+ * </p>
+ *
+ * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+ */
+ protected void update(ClusteredSession replicated)
+ {
+ synchronized (this)
+ {
+ // From StandardSession
+ id = replicated.id;
+ creationTime = replicated.creationTime;
+ lastAccessedTime = replicated.lastAccessedTime;
+ maxInactiveInterval = replicated.maxInactiveInterval;
+ isNew = replicated.isNew;
+ isValid = replicated.isValid;
+ thisAccessedTime = replicated.thisAccessedTime;
+
+ // From ClusteredSession
+ invalidationPolicy = replicated.invalidationPolicy;
+ version = replicated.version;
+
+ // Get our id without any jvmRoute appended
+ parseRealId(id);
+
+ // We no longer know if we have an activationListener
+ hasActivationListener = null;
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// // Session notes -- for FORM auth apps, allow replicated session
+// // to be used without requiring a new login
+// // We use the superclass set/removeNote calls here to bypass
+// // the custom logic we've added
+// String username = (String) in.readObject();
+// if (username != null)
+// {
+// super.setNote(Constants.SESS_USERNAME_NOTE, username);
+// }
+// else
+// {
+// super.removeNote(Constants.SESS_USERNAME_NOTE);
+// }
+// String password = (String) in.readObject();
+// if (password != null)
+// {
+// super.setNote(Constants.SESS_PASSWORD_NOTE, password);
+// }
+// else
+// {
+// super.removeNote(Constants.SESS_PASSWORD_NOTE);
+// }
+ }
+ }
+
+
+ // -------------------------------------- Internal protected method override
+
+ /**
+ * Method inherited from Tomcat. Return zero-length based string if not found.
+ */
+ protected String[] keys()
+ {
+ return ((String[]) getAttributesInternal().keySet().toArray(EMPTY_ARRAY));
+ }
+
+ /**
+ * Called by super.removeAttribute().
+ *
+ * @param name the attribute name
+ * @param notify <code>true</code> if listeners should be notified
+ */
+ protected void removeAttributeInternal(String name, boolean notify)
+ {
+ boolean localCall = true;
+ boolean localOnly = false;
+ removeAttributeInternal(name, localCall, localOnly, notify);
+ }
+
+ /**
+ * Remove the attribute from the local cache and possibly the distributed
+ * cache, plus notify any listeners
+ *
+ * @param name the attribute name
+ * @param localCall <code>true</code> if this call originated from local
+ * activity (e.g. a removeAttribute() in the webapp or a
+ * local session invalidation/expiration),
+ * <code>false</code> if it originated due to an remote
+ * event in the distributed cache.
+ * @param localOnly <code>true</code> if the removal should not be
+ * replicated around the cluster
+ * @param notify <code>true</code> if listeners should be notified
+ */
+ protected void removeAttributeInternal(String name,
+ boolean localCall,
+ boolean localOnly,
+ boolean notify)
+ {
+ // Remove this attribute from our collection
+ Object value = removeAttributeInternal(name, localCall, localOnly);
+
+ // Do we need to do valueUnbound() and attributeRemoved() notification?
+ if (!notify || (value == null))
+ {
+ return;
+ }
+
+ // Call the valueUnbound() method if necessary
+ HttpSessionBindingEvent event = null;
+ if (value instanceof HttpSessionBindingListener)
+ {
+ event = new HttpSessionBindingEvent(getSession(), name, value);
+ ((HttpSessionBindingListener) value).valueUnbound(event);
+ }
+
+ // Notify interested application event listeners
+ Context context = (Context) manager.getContainer();
+ Object listeners[] = context.getApplicationEventListeners();
+ if (listeners == null)
+ return;
+ for (int i = 0; i < listeners.length; i++)
+ {
+ if (!(listeners[i] instanceof HttpSessionAttributeListener))
+ continue;
+ HttpSessionAttributeListener listener =
+ (HttpSessionAttributeListener) listeners[i];
+ try
+ {
+ fireContainerEvent(context,
+ "beforeSessionAttributeRemoved",
+ listener);
+ if (event == null)
+ {
+ event = new HttpSessionBindingEvent
+ (getSession(), name, value);
+ }
+ listener.attributeRemoved(event);
+ fireContainerEvent(context,
+ "afterSessionAttributeRemoved",
+ listener);
+ }
+ catch (Throwable t)
+ {
+ try
+ {
+ fireContainerEvent(context,
+ "afterSessionAttributeRemoved",
+ listener);
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
+ }
+ }
+
+ }
+
+ /**
+ * Exists in this class solely to act as an API-compatible bridge to the
+ * deprecated {@link #removeJBossInternalAttribute(String)}.
+ * JBossCacheClusteredSession subclasses will override this to call their
+ * own methods that make use of localCall and localOnly
+ *
+ * @param name
+ * @param localCall
+ * @param localOnly
+ * @return
+ *
+ * @deprecated will be replaced by removeJBossInternalAttribute(String, boolean, boolean)
+ */
+ protected Object removeAttributeInternal(String name,
+ boolean localCall,
+ boolean localOnly)
+ {
+ return removeJBossInternalAttribute(name);
+ }
+
+ protected Object getAttributeInternal(String name)
+ {
+ return getJBossInternalAttribute(name);
+ }
+
+ protected Map getAttributesInternal()
+ {
+ return getJBossInternalAttributes();
+ }
+
+ protected Object setInternalAttribute(String name, Object value)
+ {
+ if (value instanceof HttpSessionActivationListener)
+ hasActivationListener = Boolean.TRUE;
+
+ return setJBossInternalAttribute(name, value);
+ }
+
+ // ------------------------------------------ JBoss internal abstract method
+
+ protected abstract Object getJBossInternalAttribute(String name);
+
+ /** @deprecated will be replaced by removeJBossInternalAttribute(String, boolean, boolean) */
+ protected abstract Object removeJBossInternalAttribute(String name);
+
+ protected abstract Map getJBossInternalAttributes();
+
+ protected abstract Object setJBossInternalAttribute(String name, Object value);
+
+ // ------------------------------------------------ Session Package Methods
+
+ protected void sessionAttributesDirty()
+ {
+ if (!sessionAttributesDirty && log.isTraceEnabled())
+ log.trace("Marking session attributes dirty" + id);
+
+ sessionAttributesDirty = true;
+ }
+
+ protected boolean getSessionAttributesDirty()
+ {
+ return sessionAttributesDirty;
+ }
+
+ protected void sessionMetadataDirty()
+ {
+ if (!sessionMetadataDirty && !isNew && log.isTraceEnabled())
+ log.trace("Marking session metadata dirty " + id);
+ sessionMetadataDirty = true;
+ }
+
+ protected boolean getSessionMetadataDirty()
+ {
+ return sessionMetadataDirty;
+ }
+
+ /**
+ * Calls {@link #sessionAttributesDirty()} and
+ * {@link #sessionMetadataDirty()}.
+ *
+ * @deprecated use one of the more fine-grained methods.
+ */
+ protected void sessionDirty()
+ {
+ sessionAttributesDirty();
+ sessionMetadataDirty();
+ }
+
+ public boolean isSessionDirty()
+ {
+ return sessionAttributesDirty || sessionMetadataDirty;
+ }
+
+ public boolean getReplicateSessionBody()
+ {
+ return sessionMetadataDirty || getExceedsMaxUnreplicatedInterval();
+ }
+
+ protected boolean isGetDirty(Object attribute)
+ {
+ boolean result = false;
+ switch (invalidationPolicy)
+ {
+ case SET_AND_GET:
+ result = true;
+ break;
+ case SET_AND_NON_PRIMITIVE_GET:
+ result = isMutable(attribute);
+ break;
+ default:
+ // result is false
+ }
+ return result;
+ }
+
+ protected boolean isMutable(Object attribute)
+ {
+ return attribute != null &&
+ !(attribute instanceof String ||
+ attribute instanceof Number ||
+ attribute instanceof Character ||
+ attribute instanceof Boolean);
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,151 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.*;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+
+/**
+ * This Valve detects all sessions that were used in a request. All sessions are given to a snapshot
+ * manager that handles the distribution of modified sessions.
+ * <p/>
+ * TOMCAT 4.1.12 UPDATE: Added findLifecycleListeners() to comply with the latest
+ * Lifecycle interface.
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * @version $Revision: 56542 $
+ */
+public class ClusteredSessionValve extends ValveBase implements Lifecycle
+{
+ // The info string for this Valve
+ private static final String info = "ClusteredSessionValve/1.0";
+
+ // Valve-lifecycle_ helper object
+ private LifecycleSupport support = new LifecycleSupport(this);
+
+ /**
+ * Create a new Valve.
+ */
+ public ClusteredSessionValve()
+ {
+ super();
+ }
+
+ /**
+ * Get information about this Valve.
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ /**
+ * Valve-chain handler method.
+ * This method gets called when the request goes through the Valve-chain. Our session replication mechanism replicates the
+ * session after request got through the servlet code.
+ *
+ * @param request The request object associated with this request.
+ * @param response The response object associated with this request.
+ */
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+ // Initialize the context and store the request and response objects
+ // for any clustering code that has no direct access to these objects
+ SessionReplicationContext.enterWebapp(request, response, true);
+ try
+ {
+
+
+ // let the servlet invocation go through
+ getNext().invoke(request, response);
+ }
+ finally // We replicate no matter what
+ {
+ // --> We are now after the servlet invocation
+ try
+ {
+ SessionReplicationContext ctx = SessionReplicationContext.exitWebapp();
+
+ if (ctx.getSoleSnapshotManager() != null)
+ {
+ ctx.getSoleSnapshotManager().snapshot(ctx.getSoleSession());
+ }
+ else
+ {
+ // Cross-context request touched multiple sesssions;
+ // need to replicate them all
+ Map sessions = ctx.getCrossContextSessions();
+ if (sessions != null && sessions.size() > 0)
+ {
+ for (Iterator iter = sessions.entrySet().iterator(); iter.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ ((SnapshotManager) entry.getValue()).snapshot((ClusteredSession) entry.getKey());
+ }
+ }
+ }
+ }
+ finally
+ {
+ SessionReplicationContext.finishCacheActivity();
+ }
+
+ }
+ }
+
+ // Lifecylce-interface
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ support.addLifecycleListener(listener);
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ support.removeLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return support.findLifecycleListeners();
+ }
+
+ public void start() throws LifecycleException
+ {
+ support.fireLifecycleEvent(START_EVENT, this);
+ }
+
+ public void stop() throws LifecycleException
+ {
+ support.fireLifecycleEvent(STOP_EVENT, this);
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,46 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+
+/**
+ * This exception is thrown when the clustered HTTPSession-service
+ * is not found
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @version $Revision: 56007 $
+ */
+public class ClusteringNotSupportedException extends Exception
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -2365801349845482947L;
+
+ public ClusteringNotSupportedException(String message)
+ {
+ super(message);
+ }
+
+ public ClusteringNotSupportedException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,363 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.aspects.patterns.observable.Observer;
+import org.jboss.aspects.patterns.observable.Subject;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpSessionActivationListener;
+
+/**
+ * <p>
+ * Implementation of a clustered session for JBossCacheManager. The replication granularity
+ * level is field based; that is, we replicate only the dirty field in a POJO that is part of
+ * a session attribute. E.g., once a user do setAttribute("pojo", pojo), pojo will be monitored
+ * automatically for field changes and accessing. It offers couple of advantages:
+ * <ul>
+ * <li>pojo.setName(), for example, will only replicate the name field in the pojo. And thus is more efficient.</li>
+ * <li>If pojo has a complex object graph, we will handle that automtically providing that the
+ * children object is also aspectized.</li>
+ * </ul>
+ * Note that in current version, all the attributes and its associated childre graph objects are
+ * required to be aspectized. That is, you can't simply declare them as Serializable. This is restricted
+ * because of the marshalling/unmarshalling issue.</p>
+ *
+ * <p>We use JBossCache for our internal, replicated data store.
+ * The internal structure is like in JBossCache:
+ * <pre>
+ * /JSESSION
+ * /hostname
+ * /web_app_path (path + session id is unique)
+ * /id Map(id, session)
+ * (VERSION, version)
+ * /ATTRIBUTE Map(can be empty)
+ * /pojo Map(field name, field value) (pojo naming is by field.getName())
+ *
+ * </pre>
+ * <p/>
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 58586 $
+ */
+class FieldBasedClusteredSession
+ extends JBossCacheClusteredSession implements Observer
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8347544395334247623L;
+
+ /**
+ * Descriptive information describing this Session implementation.
+ */
+ protected static final String info = "FieldBasedClusteredSession/1.0";
+
+ protected transient Map attributes_ = Collections.synchronizedMap(new HashMap());
+
+ public FieldBasedClusteredSession(JBossCacheManager manager)
+ {
+ super(manager);
+ }
+
+ // ----------------------------------------------- Overridden Public Methods
+
+
+ /**
+ * Override the superclass to additionally reset this class' fields.
+ * <p>
+ * <strong>NOTE:</strong> It is not anticipated that this method will be
+ * called on a ClusteredSession, but we are overriding the method to be
+ * thorough.
+ * </p>
+ */
+ public void recycle()
+ {
+ super.recycle();
+
+ attributes_.clear();
+ }
+
+ /**
+ * Return a string representation of this object.
+ */
+ public String toString()
+ {
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("FieldBasedClusteredSession[");
+ sb.append(super.toString());
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+ // The superclass version of processSessionRepl is fine; it will remove
+ // the session metadata, and any attribute changes have been picked up
+ // for replication as they were made; no need to do anything here
+// public synchronized void processSessionRepl()
+// {
+// super.processSessionRepl();
+// }
+
+ public void removeMyself()
+ {
+ // This is a shortcut to remove session and it's child attributes.
+ // Note that there is no need to remove attribute first since caller
+ // will do that already.
+ proxy_.removeSession(realId);
+ }
+
+ public void removeMyselfLocal()
+ {
+ // Need to evict attribute first before session to clean up everything.
+ // Note that there is no need to remove attributes first since caller
+ // will do that already.
+ // BRIAN -- the attributes *are* already evicted, but we leave the
+ // removePojosLocal call here in order to evict the ATTRIBUTE node.
+ // Otherwise empty nodes for the session root and child ATTRIBUTE will
+ // remain in the tree and screw up our list of session names.
+ proxy_.removePojosLocal(realId);
+ proxy_.removeSessionLocal(realId);
+ }
+
+ // ------------------------------------------------ JBoss internal abstract method
+
+ /**
+ * Populate the attributes stored in the distributed store to the local
+ * transient map. Add ourself as an Observer to newly found attributes and
+ * remove ourself as an Observer to existing attributes that are no longer
+ * in the distributed store.
+ */
+ protected void populateAttributes()
+ {
+ // Preserve any local attributes that were excluded from replication
+ Map excluded = removeExcludedAttributes(attributes_);
+
+ Set keys = proxy_.getPojoKeys(realId);
+ Set oldKeys = new HashSet(attributes_.keySet());
+
+ // Since we are going to touch each attribute, might as well
+ // check if we have any HttpSessionActivationListener
+ boolean hasListener = false;
+
+ if (keys != null)
+ {
+ oldKeys.removeAll(keys); // only keys that no longer exist are left
+
+ for (Iterator it = keys.iterator(); it.hasNext(); )
+ {
+ String name = (String) it.next();
+
+ Object oldAttrib = null;
+ Object newAttrib = proxy_.getPojo(realId, name);
+ if (newAttrib != null)
+ {
+ oldAttrib = attributes_.put(name, newAttrib);
+
+ if (oldAttrib != newAttrib)
+ {
+ // Need to observe this pojo as well
+ // for any modification events.
+ proxy_.addObserver(this, newAttrib);
+
+ // Stop observing the old pojo
+ proxy_.removeObserver(this, oldAttrib); // null pojo OK :)
+ }
+
+ // Check if we have a listener
+ if (newAttrib instanceof HttpSessionActivationListener)
+ hasListener = true;
+ }
+ else
+ {
+ // This shouldn't happen -- if we had a key, newAttrib s/b not null
+
+ oldAttrib = attributes_.remove(name);
+ // Stop observing this pojo
+ proxy_.removeObserver(this, oldAttrib); // null pojo OK :)
+
+ }
+ }
+ }
+
+ hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+
+ // Cycle through remaining old keys and remove them
+ // and also remove ourself as Observer
+ for (Iterator it = oldKeys.iterator(); it.hasNext(); )
+ {
+ Object oldAttrib = attributes_.remove(it.next());
+ proxy_.removeObserver(this, oldAttrib);
+ }
+
+ // Restore any excluded attributes
+ if (excluded != null)
+ attributes.putAll(excluded);
+ }
+
+ protected Object getJBossInternalAttribute(String name)
+ {
+ // Check the local map first.
+ Object result = attributes_.get(name);
+
+ // NOTE -- we no longer check with the store. Attributes are only
+ // loaded from store during populateAttributes() call at beginning
+ // of request when we notice we are outdated.
+
+ // Do dirty check even if result is null, as w/ SET_AND_GET null
+ // still makes us dirty (ensures timely replication w/o using ACCESS)
+ if (isGetDirty(result))
+ {
+ sessionAttributesDirty();
+ }
+
+ return result;
+ }
+
+ /**
+ * Overrides the superclass to treat classes implementing Subject
+ * as "immutable", since as an Observer we will detect any changes
+ * to those types.
+ */
+ protected boolean isMutable(Object attribute)
+ {
+ boolean pojo = (attribute instanceof Subject);
+ boolean mutable = (!pojo && super.isMutable(attribute));
+ return mutable;
+ }
+
+ protected Object removeJBossInternalAttribute(String name, boolean localCall, boolean localOnly)
+ {
+ // Remove it from the underlying store
+ if (localCall && !replicationExcludes.contains(name))
+ {
+ if (localOnly)
+ proxy_.removePojoLocal(realId, name);
+ else
+ proxy_.removePojo(realId, name);
+
+ sessionAttributesDirty();
+ }
+ Object result = attributes_.remove(name);
+ if(result == null)
+ {
+ log.warn("removeJBossInternalAttribute(): null value to remove with key: "+ name);
+ return null;
+ }
+ proxy_.removeObserver(this, result);
+
+ return result;
+ }
+
+ protected Map getJBossInternalAttributes()
+ {
+ return attributes_;
+ }
+
+ protected Set getJBossInternalKeys()
+ {
+ return attributes_.keySet();
+ }
+
+ /**
+ * Method inherited from Tomcat. Return zero-length based string if not found.
+ */
+ protected String[] keys()
+ {
+ return ((String[]) getJBossInternalKeys().toArray(EMPTY_ARRAY));
+ }
+
+ /**
+ * Overrides the superclass to allow instrumented classes and
+ * non-serializable Collections and Maps.
+ */
+ protected boolean canAttributeBeReplicated(Object attribute)
+ {
+ return (attribute == null || Util.checkPojoType(attribute));
+ }
+
+ /**
+ * This is the hook for setAttribute. Note that in this FieldBasedClusteredSession using aop,
+ * user should not call setAttribute call too often since this will re-connect the attribute with the internal
+ * cache (and this is expensive).
+ * @param key
+ * @param value
+ * @return Object
+ */
+ protected Object setJBossInternalAttribute(String key, Object value)
+ {
+ Object oldVal = null;
+ if (!replicationExcludes.contains(key))
+ {
+ oldVal = proxy_.setPojo(realId, key, value);
+ if(oldVal != null)
+ { // We are done with the old one.
+ proxy_.removeObserver(this, oldVal);
+ }
+
+ if(value != null)
+ {
+ // Special case for Collection classes.
+ if( value instanceof Map || value instanceof Collection)
+ {
+ // We need to obtain the proxy first.
+ value = proxy_.getPojo(realId, key);
+ }
+
+ // Need to use obj since it can return as a proxy.
+ proxy_.addObserver(this, value);
+ }
+
+ // Only mark session dirty if we can replicate the attribute
+ sessionAttributesDirty();
+ }
+
+ // Still need to put it in the map to track locally.
+ oldVal = attributes_.put(key, value);
+
+ return oldVal;
+ }
+
+ /**
+ * Call back handler for the aop Subject/Observer pattern.
+ * We subscribe to the event of field write and mark ourself dirty.
+ *
+ * @param subject the object we are Observing
+ */
+ public void fireChange(Subject subject)
+ {
+ // Currently we don't care who is modified, we will simply mark session is dirty for replication purpose.
+ if(log.isTraceEnabled())
+ {
+ log.trace("fireChange(): subject has changed: " +subject);
+ }
+ sessionAttributesDirty();
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,31 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+/**
+ * Marker interface for use in aop annotation declaration. For example, can use this in pojo
+ * annotation instead of declaring jboss-aop.xml. For a pojo annotation declaration, just declare
+ * this in the pojo class level, and then there is a corresponding jboss-aop.xml that is needed.
+ * @author Ben Wang
+ */
+public interface InstanceOfAopMarker {
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstanceOfAopMarker.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,65 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+
+/**
+ * A concrete implementation of the snapshot manager interface
+ * that does instant replication of a modified session
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * @version $Revision: 56542 $
+ */
+public class InstantSnapshotManager extends SnapshotManager
+{
+ public InstantSnapshotManager(AbstractJBossManager manager, String path)
+ {
+ super(manager, path);
+ }
+
+ /**
+ * Instant replication of the modified session
+ */
+ public void snapshot(ClusteredSession session)
+ {
+ if (session != null)
+ {
+ try
+ {
+ getManager().storeSession(session);
+ }
+ catch (Exception e)
+ {
+ getLog().warn("Failed to replicate session " + session.getIdInternal(), e);
+ }
+ }
+ }
+
+ public void start()
+ {
+ }
+
+ public void stop()
+ {
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InstantSnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,200 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.jboss.logging.Logger;
+
+/**
+ * A snapshot manager that collects all modified sessions over a given
+ * period of time and distributes them en bloc.
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ * @version $Revision: 56542 $
+ */
+public class IntervalSnapshotManager extends SnapshotManager implements Runnable
+{
+ static Logger log = Logger.getLogger(IntervalSnapshotManager.class);
+
+ // the interval in ms
+ private int interval = 1000;
+
+ // the modified sessions
+ private Set sessions = new LinkedHashSet();
+
+ // the distribute thread
+ private Thread thread = null;
+
+ // Is session processing allowed?
+ private boolean processingAllowed = false;
+
+ // has the thread finished?
+ private boolean threadDone = false;
+
+ public IntervalSnapshotManager(AbstractJBossManager manager, String path)
+ {
+ super(manager, path);
+ }
+
+ public IntervalSnapshotManager(AbstractJBossManager manager, String path, int interval)
+ {
+ super(manager, path);
+ this.interval = interval;
+ }
+
+ /**
+ * Store the modified session in a hashmap for the distributor thread
+ */
+ public void snapshot(ClusteredSession session)
+ {
+ try
+ {
+ // Don't hold a ref to the session for a long time
+ synchronized (sessions)
+ {
+ sessions.add(session);
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("Failed to queue session " + session + " for replication", e);
+ }
+ }
+
+ /**
+ * Distribute all modified sessions
+ */
+ protected void processSessions()
+ {
+ ClusteredSession[] toProcess = null;
+ synchronized (sessions)
+ {
+ toProcess = new ClusteredSession[sessions.size()];
+ toProcess = (ClusteredSession[]) sessions.toArray(toProcess);
+ sessions.clear();
+ }
+
+ AbstractJBossManager mgr = getManager();
+ for (int i = 0; i < toProcess.length; i++)
+ {
+ // Confirm we haven't been stopped
+ if (!processingAllowed)
+ break;
+
+ try
+ {
+ mgr.storeSession(toProcess[i]);
+ }
+ catch (Exception e)
+ {
+ getLog().error("Caught exception processing session " + toProcess[i].getRealId(), e);
+ }
+ }
+ }
+
+ /**
+ * Start the snapshot manager
+ */
+ public void start()
+ {
+ processingAllowed = true;
+ startThread();
+ }
+
+ /**
+ * Stop the snapshot manager
+ */
+ public void stop()
+ {
+ processingAllowed = false;
+ stopThread();
+ synchronized (sessions)
+ {
+ sessions.clear();
+ }
+ }
+
+ /**
+ * Start the distributor thread
+ */
+ protected void startThread()
+ {
+ if (thread != null)
+ {
+ return;
+ }
+
+ thread = new Thread(this, "ClusteredSessionDistributor[" + getContextPath() + "]");
+ thread.setDaemon(true);
+ thread.setContextClassLoader(getManager().getContainer().getLoader().getClassLoader());
+ threadDone = false;
+ thread.start();
+ }
+
+ /**
+ * Stop the distributor thread
+ */
+ protected void stopThread()
+ {
+ if (thread == null)
+ {
+ return;
+ }
+ threadDone = true;
+ thread.interrupt();
+ try
+ {
+ thread.join();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ thread = null;
+ }
+
+ /**
+ * Thread-loop
+ */
+ public void run()
+ {
+ while (!threadDone)
+ {
+ try
+ {
+ Thread.sleep(interval);
+ processSessions();
+ }
+ catch (InterruptedException ie)
+ {
+ if (!threadDone)
+ getLog().error("Caught exception processing sessions", ie);
+ }
+ catch (Exception e)
+ {
+ getLog().error("Caught exception processing sessions", e);
+ }
+ }
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/IntervalSnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InvalidateSessionPolicy.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InvalidateSessionPolicy.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InvalidateSessionPolicy.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,51 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.web.tomcat.service.session;
+
+public enum InvalidateSessionPolicy
+{
+ ACCESS, SET_AND_GET, SET_AND_NON_PRIMITIVE_GET, SET;
+
+ public static InvalidateSessionPolicy fromString(String policy)
+ {
+ return (policy == null ? InvalidateSessionPolicy.SET_AND_NON_PRIMITIVE_GET
+ : Enum.valueOf(InvalidateSessionPolicy.class, policy));
+ }
+
+ public static InvalidateSessionPolicy fromInt(int ordinal)
+ {
+ switch (ordinal)
+ {
+ case 2:
+ return SET_AND_NON_PRIMITIVE_GET;
+ case 3:
+ return SET;
+ case 1:
+ return SET_AND_GET;
+ case 0:
+ return ACCESS;
+ default:
+ throw new IllegalArgumentException("Unknown ordinal " + ordinal);
+ }
+ }
+}
\ No newline at end of file
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/InvalidateSessionPolicy.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,962 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.core.ContainerBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.factories.XmlConfigurationParser;
+import org.jboss.cache.pojo.PojoCacheFactory;
+import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper;
+import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean;
+import org.jboss.cache.transaction.BatchModeTransactionManagerLookup;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.mx.util.MBeanServerLocator;
+
+/**
+ * A Tomcat <code>Cluster</code> implementation that uses a JBoss
+ * <code>TreeCache</code> to support intra-cluster session replication.
+ * <p>
+ * This class registers a <code>TreeCache</code> in JMX, making it
+ * available to other users who wish to replicate data within the cluster.
+ * </p>
+ *
+ * @author Brian Stansberry
+ * @version $Revision: 61125 $
+ */
+public class JBossCacheCluster
+ implements JBossCacheClusterMBean, Lifecycle
+{
+ // ------------------------------------------------------- Static Fields
+
+ protected static final String info = "JBossCacheCluster/2.1";
+
+ public static Log log = LogFactory.getLog(JBossCacheCluster.class);
+
+ public static final String DEFAULT_CLUSTER_NAME = "Tomcat-Cluster";
+
+ /** TreeCache's isolation level */
+ public static final String DEFAULT_ISOLATION_LEVEL = "REPEATABLE_READ";
+
+ /** TreeCache's cache mode */
+ public static final String DEFAULT_CACHE_MODE = "REPL_ASYNC";
+
+ /** TreeCache's lock aquisition timeout */
+ public static final long DEFAULT_LOCK_TIMEOUT = 15000;
+
+ /** TransactionManagerLookup implementation that the TreeCache should use. */
+ public static final String DEFAULT_TM_LOOKUP =
+ BatchModeTransactionManagerLookup.class.getName();
+
+ public static final String DEFAULT_CACHE_CONFIG_PATH = "conf/cluster-cache.xml";
+
+ // ------------------------------------------------------- Instance Fields
+
+ /** Parent container of this cluster. */
+ private Container container = null;
+
+ /** Our JMX Server. */
+ private MBeanServer mserver = null;
+
+ /** Name under which we are registered in JMX */
+ private ObjectName objectName = null;
+
+ /** Are we started? */
+ private boolean started = false;
+
+ /** The lifecycle event support for this component. */
+ private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+ /** Our tree cache */
+ private PojoCacheJmxWrapperMBean pojoCache = null;
+
+ /** Name under which our TreeCache is registered in JMX */
+ private String pojoCacheObjectName = "jboss.cache:service=TomcatClusteringCache";
+
+ /** Did we create the tree cache, or was it already registered in JMX? */
+ private boolean pojoCacheLocal = false;
+
+ /** Name of the tree cache's JGroups channel */
+ private String clusterName = null;
+
+ /** File name, URL or String to use to configure JGroups. */
+ private String cacheConfigPath = null;
+
+ /**
+ * Implementation of Manager to instantiate when
+ * createManager() is called.
+ */
+ private String managerClassName = JBossCacheManager.class.getName();
+
+ /** Does the Engine in which we are running use mod_jk? */
+ private boolean useJK = false;
+
+ /** Whether our Managers should use a local cache. */
+ private boolean useLocalCache = false;
+
+ /**
+ * Default replication trigger to assign to our
+ * Managers that haven't had this property set.
+ */
+ private String defaultReplicationTrigger = null;
+
+ /**
+ * Default replication granularity to assign to our Managers
+ * that haven't had this property set.
+ */
+ private String defaultReplicationGranularity = null;
+
+ /**
+ * JBossCacheManager's snapshot mode.
+ */
+ private String snapshotMode = null;
+
+ /**
+ * JBossCacheManager's snapshot interval.
+ */
+ private int snapshotInterval = 0;
+
+ /** Whether we use batch mode replication for field level granularity */
+ private boolean replicationFieldBatchMode;
+
+ // ---------------------------------------------------------- Constructors
+
+ /**
+ * Default constructor.
+ */
+ public JBossCacheCluster()
+ {
+ super();
+ }
+
+ // ------------------------------------------------------------ Properties
+
+ /**
+ * Gets a String representation of the JMX <code>ObjectName</code> under
+ * which our <code>TreeCache</code> is registered.
+ * <p>
+ * If this property is not explicitly set, the <code>TreeCache</code> will
+ * be registered under
+ * @{@link Tomcat6.DEFAULT_CACHE_NAME the default name used in
+ * embedded JBoss/Tomcat}.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public String getCacheObjectName()
+ {
+ return pojoCacheObjectName;
+ }
+
+ /**
+ * Sets the JMX <code>ObjectName</code> under which our
+ * <code>TreeCache</code> is registered, if already created, or under
+ * which it should be registered if this object creates it.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setCacheObjectName(String objectName)
+ {
+ this.pojoCacheObjectName = objectName;
+ }
+
+ /**
+ * Sets the name of the <code>TreeCache</code>'s JGroups channel.
+ * <p>
+ * This property is ignored if a <code>TreeCache</code> is already
+ * registered under the provided
+ * {@link #setCacheObjectName cache object name}.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public void setClusterName(String clusterName)
+ {
+ this.clusterName = clusterName;
+ }
+
+ /**
+ * Gets the filesystem path, which can either be absolute or a path
+ * relative to <code>$CATALINA_BASE</code>, where a
+ * a JBossCache configuration file can be found.
+ *
+ * @return a path, either absolute or relative to
+ * <code>$CATALINA_BASE</code>. Will return
+ * <code>null</code> if no such path was configured.
+ *
+ * @jmx.managed-attribute
+ */
+ public String getCacheConfigPath()
+ {
+ return cacheConfigPath;
+ }
+
+ /**
+ * Sets the filesystem path, which can either be absolute or a path
+ * relative to <code>$CATALINA_BASE</code>, where a
+ * a JBossCache configuration file can be found.
+ * <p>
+ * This property is ignored if a <code>TreeCache</code> is already
+ * registered under the provided
+ * {@link #setCacheObjectName cache object name}.
+ * </p>
+ *
+ * @param cacheConfigPath a path, absolute or relative to
+ * <code>$CATALINA_BASE</code>,
+ * pointing to a JBossCache configuration file.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setCacheConfigPath(String cacheConfigPath)
+ {
+ this.cacheConfigPath = cacheConfigPath;
+ }
+
+ /**
+ * Gets the name of the implementation of Manager to instantiate when
+ * createManager() is called.
+ *
+ * @jmx.managed-attribute
+ */
+ public String getManagerClassName()
+ {
+ return managerClassName;
+ }
+
+ /**
+ * Sets the name of the implementation of Manager to instantiate when
+ * createManager() is called.
+ * <p>
+ * This should be {@link JBossCacheManager} (the default) or a subclass
+ * of it.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public void setManagerClassName(String managerClassName)
+ {
+ this.managerClassName = managerClassName;
+ }
+
+ public void registerManager(Manager arg0)
+ {
+ // TODO tie this into the managerClassName
+ }
+
+ public void removeManager(Manager arg0)
+ {
+ // TODO tie this into the managerClassName
+ }
+
+ /**
+ * Gets whether the <code>Engine</code> in which we are running
+ * uses <code>mod_jk</code>.
+ *
+ * @jmx.managed-attribute
+ */
+ public boolean isUseJK()
+ {
+ return useJK;
+ }
+
+ /**
+ * Sets whether the <code>Engine</code> in which we are running
+ * uses <code>mod_jk</code>.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK = useJK;
+ }
+
+ /**
+ * Gets the <code>JBossCacheManager</code>'s <code>useLocalCache</code>
+ * property.
+ *
+ * @jmx.managed-attribute
+ */
+ public boolean isUseLocalCache()
+ {
+ return useLocalCache;
+ }
+
+ /**
+ * Sets the <code>JBossCacheManager</code>'s <code>useLocalCache</code>
+ * property.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setUseLocalCache(boolean useLocalCache)
+ {
+ this.useLocalCache = useLocalCache;
+ }
+
+ /**
+ * Gets the default granularity of session data replicated across the
+ * cluster; i.e. whether the entire session should be replicated when
+ * replication is triggered, or only modified attributes.
+ * <p>
+ * The value of this property is applied to <code>Manager</code> instances
+ * that did not have an equivalent property explicitly set in
+ * <code>context.xml</code> or <code>server.xml</code>.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public String getDefaultReplicationGranularity()
+ {
+ return defaultReplicationGranularity;
+ }
+
+ /**
+ * Sets the granularity of session data replicated across the cluster.
+ * Valid values are:
+ * <ul>
+ * <li>SESSION</li>
+ * <li>ATTRIBUTE</li>
+ * <li>FIELD</li>
+ * </ul>
+ * @jmx.managed-attribute
+ */
+ public void setDefaultReplicationGranularity(
+ String defaultReplicationGranularity)
+ {
+ this.defaultReplicationGranularity = defaultReplicationGranularity;
+ }
+
+ /**
+ * Gets the type of operations on a <code>HttpSession</code> that
+ * trigger replication.
+ * <p>
+ * The value of this property is applied to <code>Manager</code> instances
+ * that did not have an equivalent property explicitly set in
+ * <code>context.xml</code> or <code>server.xml</code>.
+ * </p>
+ *
+ * @jmx.managed-attribute
+ */
+ public String getDefaultReplicationTrigger()
+ {
+ return defaultReplicationTrigger;
+ }
+
+ /**
+ * Sets the type of operations on a <code>HttpSession</code> that
+ * trigger replication. Valid values are:
+ * <ul>
+ * <li>SET_AND_GET</li>
+ * <li>SET_AND_NON_PRIMITIVE_GET</li>
+ * <li>SET</li>
+ * </ul>
+ *
+ * @jmx.managed-attribute
+ */
+ public void setDefaultReplicationTrigger(String defaultTrigger)
+ {
+ this.defaultReplicationTrigger = defaultTrigger;
+ }
+
+ /**
+ * Gets whether Managers should use batch mode replication.
+ * Only meaningful if replication granularity is set to <code>FIELD</code>.
+ *
+ * @jmx.managed-attribute
+ */
+ public boolean getDefaultReplicationFieldBatchMode()
+ {
+ return replicationFieldBatchMode;
+ }
+
+ /**
+ * Sets whether Managers should use batch mode replication.
+ * Only meaningful if replication granularity is set to <code>FIELD</code>.
+ *
+ * @jmx.managed-attribute
+ */
+ public void setDefaultReplicationFieldBatchMode(boolean replicationFieldBatchMode)
+ {
+ this.replicationFieldBatchMode = replicationFieldBatchMode;
+ }
+
+ /**
+ * Gets when sessions are replicated to the other nodes.
+ * The default value, "instant", synchronously replicates changes
+ * to the other nodes. In this case, the "SnapshotInterval" attribute
+ * is not used.
+ * The "interval" mode, in association with the "SnapshotInterval"
+ * attribute, indicates that Tomcat will only replicate modified
+ * sessions every "SnapshotInterval" miliseconds at most.
+ *
+ * @see #getSnapshotInterval()
+ *
+ * @jmx.managed-attribute
+ */
+ public String getSnapshotMode()
+ {
+ return snapshotMode;
+ }
+
+ /**
+ * Sets when sessions are replicated to the other nodes. Valid values are:
+ * <ul>
+ * <li>instant</li>
+ * <li>interval</li>
+ * </ul>
+ *
+ * @jmx.managed-attribute
+ */
+ public void setSnapshotMode(String snapshotMode)
+ {
+ this.snapshotMode = snapshotMode;
+ }
+
+ /**
+ * Gets how often session changes should be replicated to other nodes.
+ * Only relevant if property {@link #getSnapshotMode() snapshotMode} is
+ * set to <code>interval</code>.
+ *
+ * @return the number of milliseconds between session replications.
+ *
+ * @jmx.managed-attribute
+ */
+ public int getSnapshotInterval()
+ {
+ return snapshotInterval;
+ }
+
+ /**
+ * Sets how often session changes should be replicated to other nodes.
+ *
+ * @param snapshotInterval the number of milliseconds between
+ * session replications.
+ * @jmx.managed-attribute
+ */
+ public void setSnapshotInterval(int snapshotInterval)
+ {
+ this.snapshotInterval = snapshotInterval;
+ }
+
+ // ---------------------------------------------------------------- Cluster
+
+ /**
+ * Gets the name of the <code>TreeCache</code>'s JGroups channel.
+ *
+ * @see org.apache.catalina.Cluster#getClusterName()
+ */
+ public String getClusterName()
+ {
+ return clusterName;
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Cluster#getContainer()
+ */
+ public Container getContainer()
+ {
+ return container;
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Cluster#setContainer()
+ */
+ public void setContainer(Container container)
+ {
+ this.container = container;
+ }
+
+ /**
+ * @see org.apache.catalina.Cluster#getInfo()
+ *
+ * @jmx.managed-attribute access="read-only"
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ /**
+ * @see org.apache.catalina.Cluster#createManager(java.lang.String)
+ */
+ public Manager createManager(String name)
+ {
+ if (log.isDebugEnabled())
+ log.debug("Creating ClusterManager for context " + name
+ + " using class " + getManagerClassName());
+ Manager manager = null;
+ try
+ {
+ manager = (Manager) getClass().getClassLoader().loadClass(
+ getManagerClassName()).newInstance();
+ }
+ catch (Exception x)
+ {
+ log.error("Unable to load class for replication manager", x);
+ manager = new JBossCacheManager();
+ }
+ finally
+ {
+ manager.setDistributable(true);
+ }
+
+ if (manager instanceof JBossCacheManager)
+ {
+ configureManager((JBossCacheManager) manager);
+ }
+
+ return manager;
+ }
+
+ /**
+ * Does nothing; tracking the status of other members of the cluster is
+ * provided by the JGroups layer.
+ *
+ * @see org.apache.catalina.Cluster#backgroundProcess()
+ */
+ public void backgroundProcess()
+ {
+ ; // no-op
+ }
+
+ // --------------------------------------------- Deprecated Cluster Methods
+
+ /**
+ * Returns <code>null</code>; method is deprecated.
+ *
+ * @return <code>null</code>, always.
+ *
+ * @see org.apache.catalina.Cluster#getProtocol()
+ */
+ public String getProtocol()
+ {
+ return null;
+ }
+
+ /**
+ * Does nothing; method is deprecated.
+ *
+ * @see org.apache.catalina.Cluster#setProtocol(java.lang.String)
+ */
+ public void setProtocol(String protocol)
+ {
+ ; // no-op
+ }
+
+ /**
+ * Does nothing; method is deprecated.
+ *
+ * @see org.apache.catalina.Cluster#startContext(java.lang.String)
+ */
+ public void startContext(String contextPath) throws IOException
+ {
+ ; // no-op
+ }
+
+ /**
+ * Does nothing; method is deprecated.
+ *
+ * @see org.apache.catalina.Cluster#installContext(java.lang.String, java.net.URL)
+ */
+ public void installContext(String contextPath, URL war)
+ {
+ ; // no-op
+ }
+
+ /**
+ * Does nothing; method is deprecated.
+ *
+ * @see org.apache.catalina.Cluster#stop(java.lang.String)
+ */
+ public void stop(String contextPath) throws IOException
+ {
+ ; // no-op
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * Sets the cluster-wide properties of a <code>Manager</code> to
+ * match those of this cluster. Does not override
+ * <code>Manager</code>-specific properties with cluster-wide defaults
+ * if the <code>Manager</code>-specfic properties have already been set.
+ */
+ public void configureManager(JBossCacheManager manager)
+ {
+ manager.setSnapshotMode(snapshotMode);
+ manager.setSnapshotInterval(snapshotInterval);
+ manager.setUseJK(useJK);
+ manager.setUseLocalCache(useLocalCache);
+ manager.setCacheObjectNameString(pojoCacheObjectName);
+
+ // Only set replication attributes if they were not
+ // already set via a <Manager> element in an XML config file
+
+ if (manager.getReplicationGranularityString() == null)
+ {
+ manager.setReplicationGranularityString(defaultReplicationGranularity);
+ }
+
+ if (manager.getReplicationTriggerString() == null)
+ {
+ manager.setReplicationTriggerString(defaultReplicationTrigger);
+ }
+
+ if (manager.isReplicationFieldBatchMode() == null)
+ {
+ manager.setReplicationFieldBatchMode(replicationFieldBatchMode);
+ }
+ }
+
+ // --------------------------------------------------------------- Lifecyle
+
+ /**
+ * Finds or creates a {@link TreeCache}; if created, starts the
+ * cache and registers it with our JMX server.
+ *
+ * @see org.apache.catalina.Lifecycle#start()
+ */
+ public void start() throws LifecycleException
+ {
+ if (started)
+ {
+ throw new LifecycleException("Cluster already started");
+ }
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, this);
+
+ try
+ {
+ // Tell the JBoss MBeanServerLocator utility
+ // that Tomcat's MBean server is 'jboss'
+ MBeanServerLocator.setJBoss(getMBeanServer());
+
+ // Initialize the tree cache
+ PojoCacheJmxWrapperMBean cache = getTreeCache();
+
+ registerMBeans();
+
+ if (pojoCacheLocal)
+ {
+ cache.create();
+ cache.start();
+ }
+
+ started = true;
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_START_EVENT, this);
+
+ }
+ catch (LifecycleException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ log.error("Unable to start cluster.", e);
+ throw new LifecycleException(e);
+ }
+ }
+
+ /**
+ * If this object created its own {@link TreeCache}, stops it
+ * and unregisters it with JMX.
+ *
+ * @see org.apache.catalina.Lifecycle#stop()
+ */
+ public void stop() throws LifecycleException
+ {
+ if (!started)
+ {
+ throw new IllegalStateException("Cluster not started");
+ }
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+
+ if (pojoCacheLocal)
+ {
+ pojoCache.stop();
+ pojoCache.destroy();
+ }
+
+ started = false;
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+
+ unregisterMBeans();
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Lifecycle#addLifecycleListener()
+ */
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.addLifecycleListener(listener);
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Lifecycle#findLifecycleListeners()
+ */
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return lifecycle.findLifecycleListeners();
+ }
+
+ /* (non-javadoc)
+ * @see org.apache.catalina.Lifecycle#removeLifecycleListener()
+ */
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.removeLifecycleListener(listener);
+ }
+
+ // -------------------------------------------------------- Private Methods
+
+ /**
+ * Gets our TreeCache, either from a local reference or the JMX
+ * server. If one is not found, creates and configures it.
+ */
+ private PojoCacheJmxWrapperMBean getTreeCache() throws Exception
+ {
+ if (pojoCache == null) {
+
+ MBeanServer server = getMBeanServer();
+ ObjectName objName = new ObjectName(pojoCacheObjectName);
+ if (server.isRegistered(objName))
+ {
+ // Get a proxy to the existing TreeCache
+ pojoCache = (PojoCacheJmxWrapperMBean)
+ MBeanProxyExt.create(PojoCacheJmxWrapperMBean.class, objName);
+ }
+ else
+ {
+ // See if there is an XML descriptor file to configure the cache
+ InputStream configIS = getCacheConfigStream();
+
+ Configuration config = null;
+
+ if (configIS != null)
+ {
+ XmlConfigurationParser parser = new XmlConfigurationParser();
+ config = parser.parseStream(configIS);
+ try
+ {
+ configIS.close();
+ }
+ catch (IOException io)
+ {
+ // ignore
+ }
+
+ if (clusterName != null)
+ {
+ // Override the XML config with the name provided in
+ // server.xml. Method setClusterName is specified in the
+ // Cluster interface, otherwise we would not do this
+ config.setClusterName(clusterName);
+ }
+ }
+ else
+ {
+ // User did not try to configure the cache.
+ // Configure it using defaults. Only exception
+ // is the clusterName, which user can specify in server.xml.
+ config = new Configuration();
+ String channelName = (clusterName == null) ? DEFAULT_CLUSTER_NAME
+ : clusterName;
+ config.setClusterName(channelName);
+ config.setIsolationLevel(DEFAULT_ISOLATION_LEVEL);
+ config.setCacheMode(DEFAULT_CACHE_MODE);
+ config.setLockAcquisitionTimeout(DEFAULT_LOCK_TIMEOUT);
+ config.setTransactionManagerLookupClass(DEFAULT_TM_LOOKUP);
+ }
+
+ pojoCache = new PojoCacheJmxWrapper(PojoCacheFactory.createCache(config, false));
+
+ pojoCacheLocal = true;
+ }
+ }
+ return pojoCache;
+ }
+
+
+
+ private InputStream getCacheConfigStream() throws FileNotFoundException
+ {
+ boolean useDefault = (this.cacheConfigPath == null);
+ String path = (useDefault) ? DEFAULT_CACHE_CONFIG_PATH : cacheConfigPath;
+ // See if clusterProperties points to a file relative
+ // to $CATALINA_BASE
+ File file = new File(path);
+ if (!file.isAbsolute())
+ {
+ file = new File(System.getProperty("catalina.base"), path);
+ }
+
+ try
+ {
+ return new FileInputStream(file);
+ }
+ catch (FileNotFoundException fnf)
+ {
+ if (useDefault)
+ {
+ // Not a problem, just means user did not try to
+ // configure the cache. Return null and let the cache
+ // be configured from defaults.
+ return null;
+ }
+ else
+ {
+ // User provided config was invalid; throw the exception
+ log.error("No tree cache config file found at " +
+ file.getAbsolutePath());
+ throw fnf;
+ }
+ }
+ }
+
+ /**
+ * Registers this object and the tree cache (if we created it) with JMX.
+ */
+ private void registerMBeans()
+ {
+ try
+ {
+ MBeanServer server = getMBeanServer();
+
+ String domain;
+ if (container instanceof ContainerBase)
+ {
+ domain = ((ContainerBase) container).getDomain();
+ }
+ else
+ {
+ domain = server.getDefaultDomain();
+ }
+
+ String name = ":type=Cluster";
+ if (container instanceof Host) {
+ name += ",host=" + container.getName();
+ }
+ else if (container instanceof Engine)
+ {
+ name += ",engine=" + container.getName();
+ }
+
+ ObjectName clusterName = new ObjectName(domain + name);
+
+ if (server.isRegistered(clusterName))
+ {
+ log.warn("MBean " + clusterName + " already registered");
+ }
+ else
+ {
+ this.objectName = clusterName;
+ server.registerMBean(this, objectName);
+ }
+
+ if (pojoCacheLocal)
+ {
+ // Register the treeCache
+ ObjectName treeCacheName = new ObjectName(pojoCacheObjectName);
+ server.registerMBean(getTreeCache(), treeCacheName);
+ }
+
+ }
+ catch (Exception ex)
+ {
+ log.error(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Unregisters this object and the tree cache (if we created it) with JMX.
+ */
+ private void unregisterMBeans()
+ {
+ if (mserver != null)
+ {
+ try
+ {
+ if (objectName != null) {
+ mserver.unregisterMBean(objectName);
+ }
+ if (pojoCacheLocal)
+ {
+ mserver.unregisterMBean(new ObjectName(pojoCacheObjectName));
+ }
+ }
+ catch (Exception e)
+ {
+ log.error(e);
+ }
+ }
+ }
+
+ /**
+ * Get the current Catalina MBean Server.
+ *
+ * @return
+ * @throws Exception
+ */
+ private MBeanServer getMBeanServer() throws Exception
+ {
+ if (mserver == null)
+ {
+ ArrayList servers = MBeanServerFactory.findMBeanServer(null);
+ if (servers.size() > 0)
+ {
+ mserver = (MBeanServer) servers.get(0);
+ }
+ else
+ {
+ mserver = MBeanServerFactory.createMBeanServer();
+ }
+ }
+ return mserver;
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,80 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.LifecycleException;
+
+/**
+ * The MBean interface for the JBossCacheCluster.
+ *
+ * @see org.jboss.web.tomcat.service.JBossCacheCluster
+ *
+ * @author Brian Stansberry
+ * @version $Revision: 45726 $
+ */
+public interface JBossCacheClusterMBean extends Cluster
+{
+ public abstract boolean isUseJK();
+
+ public abstract void setUseJK(boolean useJK);
+
+ public abstract boolean isUseLocalCache();
+
+ public abstract void setUseLocalCache(boolean useLocalCache);
+
+ public abstract String getManagerClassName();
+
+ public abstract void setManagerClassName(String managerClassName);
+
+ public abstract String getDefaultReplicationGranularity();
+
+ public abstract void setDefaultReplicationGranularity(String defaultGran);
+
+ public abstract String getDefaultReplicationTrigger();
+
+ public abstract void setDefaultReplicationTrigger(String defaultTrigger);
+
+ public boolean getDefaultReplicationFieldBatchMode();
+
+ public void setDefaultReplicationFieldBatchMode(boolean replicationFieldBatchMode);
+
+ public abstract int getSnapshotInterval();
+
+ public abstract void setSnapshotInterval(int snapshotInterval);
+
+ public abstract String getSnapshotMode();
+
+ public abstract void setSnapshotMode(String snapshotMode);
+
+ public abstract String getCacheObjectName();
+
+ public abstract void setCacheObjectName(String objectName);
+
+ public abstract void start() throws LifecycleException;
+
+ /**
+ * Does nothing.
+ */
+ public abstract void stop() throws LifecycleException;
+
+}
\ No newline at end of file
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusterMBean.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,160 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+
+package org.jboss.web.tomcat.service.session;
+
+/**
+ * Common superclass of ClusteredSession types that use JBossCache
+ * as their distributed cache.
+ *
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 56542 $
+ */
+public abstract class JBossCacheClusteredSession extends ClusteredSession
+{
+ /**
+ * Our proxy to the cache.
+ */
+ protected transient JBossCacheService proxy_;
+
+ /**
+ * Create a new JBossCacheClusteredSession.
+ *
+ * @param manager
+ * @param useJK
+ */
+ public JBossCacheClusteredSession(JBossCacheManager manager)
+ {
+ super(manager, manager.getUseJK());
+ establishProxy();
+ }
+
+ /**
+ * Initialize fields marked as transient after loading this session
+ * from the distributed store
+ *
+ * @param manager the manager for this session
+ */
+ public void initAfterLoad(AbstractJBossManager manager)
+ {
+ // Our manager and proxy may have been lost if we were recycled,
+ // so reestablish them
+ setManager(manager);
+ establishProxy();
+
+ // Since attribute map may be transient, we may need to populate it
+ // from the underlying store.
+ populateAttributes();
+
+ // Notify all attributes of type HttpSessionActivationListener (SRV 7.7.2)
+ this.activate();
+
+ // We are no longer outdated vis a vis distributed cache
+ clearOutdated();
+ }
+
+ /**
+ * Gets a reference to the JBossCacheService.
+ */
+ protected void establishProxy()
+ {
+ if (proxy_ == null)
+ {
+ proxy_ = ((JBossCacheManager) manager).getCacheService();
+
+ // still null???
+ if (proxy_ == null)
+ {
+ throw new RuntimeException("JBossCacheClusteredSession: Cache service is null.");
+ }
+ }
+ }
+
+ protected abstract void populateAttributes();
+
+ /**
+ * Override the superclass to additionally reset this class' fields.
+ * <p>
+ * <strong>NOTE:</strong> It is not anticipated that this method will be
+ * called on a ClusteredSession, but we are overriding the method to be
+ * thorough.
+ * </p>
+ */
+ public void recycle()
+ {
+ super.recycle();
+
+ proxy_ = null;
+ }
+
+ /**
+ * Increment our version and place ourself in the cache.
+ */
+ public synchronized void processSessionRepl()
+ {
+ // Replicate the session.
+ if (log.isTraceEnabled())
+ {
+ log.trace("processSessionRepl(): session is dirty. Will increment " +
+ "version from: " + getVersion() + " and replicate.");
+ }
+ this.incrementVersion();
+ proxy_.putSession(realId, this);
+
+ sessionAttributesDirty = false;
+ sessionMetadataDirty = false;
+
+ updateLastReplicated();
+ }
+
+ /**
+ * Overrides the superclass impl by doing nothing if <code>localCall</code>
+ * is <code>false</code>. The JBossCacheManager will already be aware of
+ * a remote invalidation and will handle removal itself.
+ */
+ protected void removeFromManager(boolean localCall, boolean localOnly)
+ {
+ if (localCall)
+ {
+ super.removeFromManager(localCall, localOnly);
+ }
+ }
+
+
+ protected Object removeAttributeInternal(String name, boolean localCall, boolean localOnly)
+ {
+ return removeJBossInternalAttribute(name, localCall, localOnly);
+ }
+
+ protected Object removeJBossInternalAttribute(String name)
+ {
+ throw new UnsupportedOperationException("removeJBossInternalAttribute(String) " +
+ "is not supported by JBossCacheClusteredSession; use " +
+ "removeJBossInternalAttribute(String, boolean, boolean");
+ }
+
+ protected abstract Object removeJBossInternalAttribute(String name,
+ boolean localCall,
+ boolean localOnly);
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1923 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+import javax.transaction.RollbackException;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.ContainerBase;
+import org.jboss.cache.CacheException;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.mx.util.MBeanServerLocator;
+
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implementation of a clustered session manager for
+ * catalina using JBossCache replication.
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ * @author Hany Mesha
+ * @version $Revision: 58587 $
+ */
+public class JBossCacheManager
+ extends JBossManager
+ implements JBossCacheManagerMBean
+{
+
+ /**
+ * Informational name for this Catalina component
+ */
+ static final String info_ = "JBossCacheManager/1.0";
+
+ // -- Class attributes ---------------------------------
+
+ /**
+ * The transaction manager.
+ */
+ private TransactionManager tm;
+
+ /**
+ * Proxy-object for the JBossCacheService
+ */
+ private JBossCacheService proxy_;
+
+ /**
+ * Id/timestamp of sessions in cache that we haven't loaded
+ */
+ private Map unloadedSessions_ = new ConcurrentHashMap();
+
+ /** Our TreeCache's ObjectName */
+ private String cacheObjectNameString_ = "jboss.cache:service=TomcatClusteringCache";
+
+ /**
+ * If set to true, will add a JvmRouteFilter to the request.
+ */
+ private boolean useJK_ = false;
+
+ /** Are we running embedded in JBoss? */
+ private boolean embedded_ = false;
+
+ /** Our JMX Server */
+ private MBeanServer mserver_ = null;
+
+ /** Our ClusteredSessionValve's snapshot mode. */
+ private String snapshotMode_ = null;
+
+ /** Our ClusteredSessionValve's snapshot interval. */
+ private int snapshotInterval_ = 0;
+
+ /** String form of invalidateSessionPolicy_ */
+ private String replTriggerString_ = null;
+
+ /** String form of replGranularityString_ */
+ private String replGranularityString_ = null;
+
+ /**
+ * Whether we use batch mode replication for field level granularity.
+ * We store this in a Boolean rather than a primitive so JBossCacheCluster
+ * can determine if this was set via a <Manager> element.
+ */
+ private Boolean replicationFieldBatchMode_;
+
+ /** Class loader for this web app. */
+ private ClassLoader tcl_;
+
+ /**
+ * The snapshot manager we are using.
+ */
+ private SnapshotManager snapshotManager_;
+
+ // ---------------------------------------------------------- Constructors
+
+ public JBossCacheManager()
+ {
+ super();
+ }
+
+ /**
+ * Initializes this Manager when running in embedded mode.
+ * <p>
+ * <strong>NOTE:</strong> This method should not be called when
+ * running unembedded.
+ * </p>
+ */
+ public void init(String name, WebMetaData webMetaData,
+ boolean useJK, boolean useLocalCache)
+ throws ClusteringNotSupportedException
+ {
+ super.init(name, webMetaData, useJK, useLocalCache);
+ this.useJK_ = useJK;
+ this.replicationFieldBatchMode_ =
+ webMetaData.getReplicationFieldBatchMode() ? Boolean.TRUE : Boolean.FALSE;
+
+ proxy_ = new JBossCacheService(cacheObjectNameString_);
+
+ // Confirm our replication granularity is compatible with the cache
+ // Throws ISE if not
+ validateFieldMarshalling();
+
+ embedded_ = true;
+ }
+
+ // ------------------------------------------------------------- Properties
+
+ /**
+ * Gets the <code>JBossCacheService</code> through which we interact
+ * with the <code>TreeCache</code>.
+ */
+ public JBossCacheService getCacheService()
+ {
+ return proxy_;
+ }
+
+ /**
+ * Gets a String representation of the JMX <code>ObjectName</code> under
+ * which our <code>TreeCache</code> is registered.
+ */
+ public String getCacheObjectNameString()
+ {
+ return cacheObjectNameString_;
+ }
+
+ /**
+ * Sets the JMX <code>ObjectName</code> under which our
+ * <code>TreeCache</code> is registered.
+ */
+ public void setCacheObjectNameString(String treeCacheObjectName)
+ {
+ this.cacheObjectNameString_ = treeCacheObjectName;
+ }
+
+ /**
+ * Gets when sessions are replicated to the other nodes.
+ * The default value, "instant", synchronously replicates changes
+ * to the other nodes. In this case, the "SnapshotInterval" attribute
+ * is not used.
+ * The "interval" mode, in association with the "SnapshotInterval"
+ * attribute, indicates that Tomcat will only replicate modified
+ * sessions every "SnapshotInterval" miliseconds at most.
+ *
+ * @see #getSnapshotInterval()
+ */
+ public String getSnapshotMode()
+ {
+ return snapshotMode_;
+ }
+
+ /**
+ * Sets when sessions are replicated to the other nodes. Valid values are:
+ * <ul>
+ * <li>instant</li>
+ * <li>interval</li>
+ * </ul>
+ */
+ public void setSnapshotMode(String snapshotMode)
+ {
+ this.snapshotMode_ = snapshotMode;
+ }
+
+ /**
+ * Gets how often session changes should be replicated to other nodes.
+ * Only relevant if property {@link #getSnapshotMode() snapshotMode} is
+ * set to <code>interval</code>.
+ *
+ * @return the number of milliseconds between session replications.
+ */
+ public int getSnapshotInterval()
+ {
+ return snapshotInterval_;
+ }
+
+ /**
+ * Sets how often session changes should be replicated to other nodes.
+ *
+ * @param snapshotInterval the number of milliseconds between
+ * session replications.
+ */
+ public void setSnapshotInterval(int snapshotInterval)
+ {
+ this.snapshotInterval_ = snapshotInterval;
+ }
+
+ /**
+ * Gets whether the <code>Engine</code> in which we are running
+ * uses <code>mod_jk</code>.
+ */
+ public boolean getUseJK()
+ {
+ return useJK_;
+ }
+
+ /**
+ * Sets whether the <code>Engine</code> in which we are running
+ * uses <code>mod_jk</code>.
+ */
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK_ = useJK;
+ }
+
+ /**
+ * Returns the replication granularity expressed as an int.
+ *
+ * @see WebMetaData#REPLICATION_GRANULARITY_ATTRIBUTE
+ * @see WebMetaData#REPLICATION_GRANULARITY_FIELD
+ * @see WebMetaData#REPLICATION_GRANULARITY_SESSION
+ */
+ public ReplicationGranularity getReplicationGranularity()
+ {
+ return replicationGranularity_;
+ }
+
+ /**
+ * Gets the granularity of session data replicated across the
+ * cluster; i.e. whether the entire session should be replicated when
+ * replication is triggered, only modified attributes, or only
+ * modified fields of attributes.
+ */
+ public String getReplicationGranularityString()
+ {
+ // Only lazy-set this if we are started;
+ // otherwise screws up standalone TC integration!!
+ if (started_ && this.replGranularityString_ == null)
+ {
+ this.replGranularityString_ = replicationGranularity_.toString();
+ }
+ return replGranularityString_;
+ }
+
+ /**
+ * Sets the granularity of session data replicated across the cluster.
+ * Valid values are:
+ * <ul>
+ * <li>SESSION</li>
+ * <li>ATTRIBUTE</li>
+ * <li>FIELD</li>
+ * </ul>
+ */
+ public void setReplicationGranularityString(String granularity)
+ {
+ this.replGranularityString_ = granularity;
+ }
+
+ /**
+ * Gets the type of operations on a <code>HttpSession</code> that
+ * trigger replication.
+ */
+ public String getReplicationTriggerString()
+ {
+ // Only lazy-set this if we are started;
+ // otherwise screws up standalone TC integration!!
+ if (started_ && this.replTriggerString_ == null)
+ {
+ this.replTriggerString_ = invalidateSessionPolicy_.toString();
+ }
+ return this.replTriggerString_;
+ }
+
+ /**
+ * Sets the type of operations on a <code>HttpSession</code> that
+ * trigger replication. Valid values are:
+ * <ul>
+ * <li>SET_AND_GET</li>
+ * <li>SET_AND_NON_PRIMITIVE_GET</li>
+ * <li>SET</li>
+ * </ul>
+ */
+ public void setReplicationTriggerString(String trigger)
+ {
+ this.replTriggerString_ = trigger;
+ }
+
+ /**
+ * Gets whether, if replication granularity is set to <code>FIELD</code>,
+ * replication should be done in batch mode. Ignored if field-level
+ * granularity is not used.
+ */
+ public Boolean isReplicationFieldBatchMode()
+ {
+ return replicationFieldBatchMode_;
+ }
+
+ /**
+ * Sets whether, if replication granularity is set to <code>FIELD</code>,
+ * replication should be done in batch mode. Ignored if field-level
+ * granularity is not used.
+ */
+ public void setReplicationFieldBatchMode(boolean replicationFieldBatchMode)
+ {
+ this.replicationFieldBatchMode_ = Boolean.valueOf(replicationFieldBatchMode);
+ }
+
+ public void setUseLocalCache(boolean useLocalCache)
+ {
+ this.useLocalCache_ = useLocalCache;
+ }
+
+ // JBossCacheManagerMBean-methods -------------------------------------
+
+ public void expireSession(String sessionId)
+ {
+ Session session = findSession(sessionId);
+ if (session != null)
+ session.expire();
+ }
+
+ public String getLastAccessedTime(String sessionId)
+ {
+ Session session = findSession(sessionId);
+ if(session == null) {
+ log_.debug("getLastAccessedTime(): Session " + sessionId +
+ " not found");
+ return "";
+ }
+ return new Date(session.getLastAccessedTime()).toString();
+ }
+
+ public Object getSessionAttribute(String sessionId, String key)
+ {
+ ClusteredSession session = (ClusteredSession) findSession(sessionId);
+ return (session == null) ? null : session.getAttribute(key);
+ }
+
+ public String getSessionAttributeString(String sessionId, String key)
+ {
+ Object attr = getSessionAttribute(sessionId, key);
+ return (attr == null) ? null : attr.toString();
+ }
+
+ public String listLocalSessionIds()
+ {
+ return reportSessionIds(sessions_.keySet());
+ }
+
+ public String listSessionIds()
+ {
+ Set ids = new HashSet(sessions_.keySet());
+ ids.addAll(unloadedSessions_.keySet());
+ return reportSessionIds(ids);
+ }
+
+ private String reportSessionIds(Set ids)
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean added = false;
+ for (Iterator it = ids.iterator(); it.hasNext(); )
+ {
+ if (added)
+ {
+ sb.append(',');
+ }
+ else
+ {
+ added = true;
+ }
+
+ sb.append(it.next());
+ }
+ return sb.toString();
+ }
+
+ // Manager-methods -------------------------------------
+
+ /**
+ * Start this Manager
+ *
+ * @throws org.apache.catalina.LifecycleException
+ *
+ */
+ public void start() throws LifecycleException
+ {
+ if (embedded_)
+ {
+ startEmbedded();
+ }
+ else
+ {
+ startUnembedded();
+ }
+ }
+
+ public void stop() throws LifecycleException
+ {
+ if (!started_)
+ {
+ throw new IllegalStateException("Manager not started");
+ }
+
+ log_.debug("Stopping");
+
+ resetStats();
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+
+ clearSessions();
+
+ // Don't leak the classloader
+ tcl_ = null;
+
+ proxy_.stop();
+ tm = null;
+
+ snapshotManager_.stop();
+
+ started_ = false;
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+
+ try
+ {
+ unregisterMBeans();
+ }
+ catch (Exception e)
+ {
+ log_.error("Could not unregister ManagerMBean from MBeanServer", e);
+ }
+ }
+
+ /**
+ * Clear the underlying cache store and also pojo that has the observers.
+ */
+ protected void clearSessions()
+ {
+ // First, the sessions we have actively loaded
+ ClusteredSession[] sessions = findLocalSessions();
+ for(int i=0; i < sessions.length; i++)
+ {
+ ClusteredSession ses = sessions[i];
+ // JBCLUSTER-15
+ // if session passivation is enabled, passivate sessions instead of expiring them which means
+ // they'll be available to the manager for activation after a restart.
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("clearSessions(): clear session by expiring or passivating: " + ses);
+ }
+ boolean notify = true;
+ boolean localCall = true;
+ boolean localOnly = true;
+ try
+ {
+ if(isPassivationEnabled() && ses.isValid())
+ {
+
+ processSessionPassivation(ses.getRealId(), this.getContainer().getParent().getName());
+ }
+ else
+ {
+ ses.expire(notify, localCall, localOnly);
+ }
+
+ }
+ catch (Throwable t)
+ {
+ log_.warn("clearSessions(): Caught exception expiring or passivating session " +
+ ses.getIdInternal(), t);
+ }
+ finally
+ {
+ // Guard against leaking memory if anything is holding a
+ // ref to the session by clearing its internal state
+ ses.recycle();
+ }
+ }
+
+ if(!isPassivationEnabled())
+ {
+// Next, the local copy of the distributed cache
+ Map unloaded = new HashMap(unloadedSessions_);
+ Set keys = unloaded.keySet();
+ for (Iterator it = keys.iterator(); it.hasNext(); )
+ {
+ String realId = (String) it.next();
+ proxy_.removeSessionLocal(realId);
+ unloadedSessions_.remove(realId);
+ }
+ }
+ }
+
+ /**
+ * Create a new session with a generated id.
+ */
+ public Session createSession()
+ {
+ return createSession(null);
+ }
+
+ /**
+ * Create a new session.
+ *
+ * @param sessionId the id to use, or <code>null</code> if we should
+ * generate a new id
+ *
+ * @return the session
+ *
+ * @throws IllegalStateException if the current number of active sessions
+ * exceeds the maximum number allowed
+ */
+ public Session createSession(String sessionId)
+ {
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("createSession: active sessions = " + activeCounter_ +
+ " , session map size = " + sessions_.size() +
+ " and max allowed sessions = " + maxActive_);
+ }
+ // We check here for maxActive instead of in add(). add() gets called
+ // when we load an already existing session from the distributed cache
+ // (e.g. in a failover) and we don't want to fail in that situation.
+
+ // JBCLUSTER-15
+ // first check if passivation is enabled and reached the max allowed sessions,
+ /// then try to expire/passivate sessions to free memory
+ if(maxActive_ != -1 && sessions_.size() >= maxActive_ && isPassivationEnabled())
+ {
+ processExpires();
+ }
+ // maxActive_ -1 is unlimited
+ if (maxActive_ != -1 && sessions_.size() >= maxActive_)
+ {
+ // Exceeds limit. We need to reject it.
+ rejectedCounter_++;
+ // Catalina api does not specify what happens
+ // but we will throw a runtime exception for now.
+ String msgEnd = (sessionId == null) ? "" : " id " + sessionId;
+ throw new IllegalStateException("JBossCacheManager.createSession(): number of " +
+ "active sessions exceeds the maximum limit: " +
+ maxActive_ + " when trying to create session" + msgEnd);
+ }
+
+ ClusteredSession session = createEmptyClusteredSession();
+
+ session.setNew(true);
+ session.setCreationTime(System.currentTimeMillis());
+ session.setMaxInactiveInterval(this.maxInactiveInterval_);
+ session.setValid(true);
+
+ if (sessionId == null)
+ {
+ sessionId = this.getNextId();
+
+ // We are using mod_jk for load balancing. Append the JvmRoute.
+ if (useJK_)
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("createSession(): useJK is true. Will append JvmRoute: " + this.getJvmRoute());
+ }
+ sessionId += "." + this.getJvmRoute();
+ }
+ }
+
+ session.setId(sessionId); // Setting the id leads to a call to add()
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Created a ClusteredSession with id: " + sessionId);
+ }
+
+ createdCounter_++;
+ // JBCLUSTER-15 - if we have created a session it must be handled by this manager
+ // therefore we must increment the active counter
+ activeCounter_++;
+
+ // Add this session to the set of those potentially needing replication
+ SessionReplicationContext.bindSession(session, snapshotManager_);
+
+ return session;
+ }
+
+ public boolean storeSession(Session baseSession)
+ {
+ boolean stored = false;
+ if(baseSession != null && started_)
+ {
+ ClusteredSession session = (ClusteredSession) baseSession;
+
+ synchronized (session)
+ {
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("check to see if needs to store and replicate " +
+ "session with id " + session.getIdInternal());
+ }
+
+ if (session.isValid() &&
+ (session.isSessionDirty() || session.getExceedsMaxUnreplicatedInterval()))
+ {
+ String realId = session.getRealId();
+
+ // Notify all session attributes that they get serialized (SRV 7.7.2)
+ long begin = System.currentTimeMillis();
+ session.passivate();
+ long elapsed = System.currentTimeMillis() - begin;
+ stats_.updatePassivationStats(realId, elapsed);
+
+ // Do the actual replication
+ begin = System.currentTimeMillis();
+ processSessionRepl(session);
+ elapsed = System.currentTimeMillis() - begin;
+ stored = true;
+ stats_.updateReplicationStats(realId, elapsed);
+ }
+ else if (log_.isTraceEnabled())
+ {
+ log_.trace("Session " + session.getIdInternal() +
+ " did not require replication.");
+ }
+ }
+ }
+
+ return stored;
+ }
+
+ public void add(Session session)
+ {
+ if (session == null)
+ return;
+
+ if (!(session instanceof ClusteredSession))
+ {
+ throw new IllegalArgumentException("You can only add instances of " +
+ "type ClusteredSession to this Manager. Session class name: " +
+ session.getClass().getName());
+ }
+
+// add((ClusteredSession) session, true);
+ add((ClusteredSession) session, false);
+ }
+
+ /**
+ * Adds the given session to the collection of those being managed by this
+ * Manager.
+ *
+ * @param session the session. Cannot be <code>null</code>.
+ * @param replicate whether the session should be replicated
+ *
+ * @throws NullPointerException if <code>session</code> is <code>null</code>.
+ */
+ private void add(ClusteredSession session, boolean replicate)
+ {
+ if (!session.isValid())
+ {
+ log_.error("Cannot add session with id=" + session.getIdInternal() +
+ " because it is invalid");
+ return;
+ }
+
+ String realId = session.getRealId();
+ Object existing = sessions_.put(realId, session);
+ unloadedSessions_.remove(realId);
+
+ if (!session.equals(existing))
+ {
+ if (replicate)
+ {
+ storeSession(session);
+ }
+
+ activeCounter_++;
+ if (activeCounter_ > maxActiveCounter_)
+ maxActiveCounter_++;
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Session with id=" + session.getIdInternal() + " added. " +
+ "Current active sessions " + activeCounter_);
+ }
+ }
+ }
+
+ // Satisfy the Manager interface. Internally we use
+ // createEmptyClusteredSession to avoid a cast
+ public Session createEmptySession()
+ {
+ return createEmptyClusteredSession();
+ }
+
+ private ClusteredSession createEmptyClusteredSession()
+ {
+ log_.debug("Creating an empty ClusteredSession");
+
+ ClusteredSession session = null;
+ switch (replicationGranularity_)
+ {
+ case ATTRIBUTE:
+ session = new AttributeBasedClusteredSession(this);
+ break;
+ case FIELD:
+ session = new FieldBasedClusteredSession(this);
+ break;
+ default:
+ session = new SessionBasedClusteredSession(this);
+ break;
+ }
+ return session;
+ }
+
+ /**
+ * Attempts to find the session in the collection of those being managed
+ * locally, and if not found there, in the distributed cache of sessions.
+ * <p>
+ * If a session is found in the distributed cache, it is added to the
+ * collection of those being managed locally.
+ * </p>
+ *
+ * @param id the session id, which may include an appended jvmRoute
+ *
+ * @return the session, or <code>null</code> if no such session could
+ * be found
+ */
+ public Session findSession(String id)
+ {
+ String realId = getRealId(id);
+ // Find it from the local store first
+ ClusteredSession session = findLocalSession(realId);
+
+ // If we didn't find it locally, only check the distributed cache
+ // if we haven't previously handled this session id on this request.
+ // If we handled it previously but it's no longer local, that means
+ // it's been invalidated. If we request an invalidated session from
+ // the distributed cache, it will be missing from the local cache but
+ // may still exist on other nodes (i.e. if the invalidation hasn't
+ // replicated yet because we are running in a tx). With buddy replication,
+ // asking the local cache for the session will cause the out-of-date
+ // session from the other nodes to be gravitated, thus resuscitating
+ // the session.
+ if (session == null
+ && !SessionReplicationContext.isSessionBoundAndExpired(realId, snapshotManager_))
+ {
+ if (log_.isTraceEnabled())
+ log_.trace("Checking for session " + realId + " in the distributed cache");
+
+ session = loadSession(realId);
+ if (session != null)
+ {
+ add(session);
+ // TODO should we advise of a new session?
+ //tellNew();
+ }
+ }
+ else if (session != null && session.isOutdated())
+ {
+ if (log_.isTraceEnabled())
+ log_.trace("Updating session " + realId + " from the distributed cache");
+
+ // Need to update it from the cache
+ loadSession(realId);
+ }
+
+ if (session != null)
+ {
+ // Add this session to the set of those potentially needing replication
+ SessionReplicationContext.bindSession(session, snapshotManager_);
+ }
+
+ return session;
+ }
+
+ /**
+ * Return the sessions. Note that this will return not only the local
+ * in-memory sessions, but also any sessions that are in the distributed
+ * cache but have not previously been accessed on this server. Invoking
+ * this method will bring all such sessions into local memory and can
+ * potentially be quite expensive.
+ *
+ * <p>
+ * Note also that when sessions are loaded from the distributed cache, no
+ * check is made as to whether the number of local sessions will thereafter
+ * exceed the maximum number allowed on this server.
+ * </p>
+ *
+ * @return an array of all the sessions
+ */
+ public Session[] findSessions()
+ {
+ // Need to load all the unloaded sessions
+ if(unloadedSessions_.size() > 0)
+ {
+ // Make a thread-safe copy of the new id list to work with
+ Set ids = new HashSet(unloadedSessions_.keySet());
+
+ if(log_.isDebugEnabled()) {
+ log_.debug("findSessions: loading sessions from distributed cache: " + ids);
+ }
+
+ for(Iterator it = ids.iterator(); it.hasNext();) {
+ loadSession((String) it.next());
+ }
+ }
+
+ // All sessions are now "local" so just return the local sessions
+ return findLocalSessions();
+ }
+
+ /**
+ * Returns all the sessions that are being actively managed by this manager.
+ * This includes those that were created on this server, those that were
+ * brought into local management by a call to
+ * {@link #findLocalSession(String)} as well as all sessions brought into
+ * local management by a call to {@link #findSessions()}.
+ */
+ public ClusteredSession[] findLocalSessions()
+ {
+ Collection coll = sessions_.values();
+ ClusteredSession[] sess = new ClusteredSession[coll.size()];
+ sess = (ClusteredSession[]) coll.toArray(sess);
+ return sess;
+ }
+
+ /**
+ * Returns the given session if it is being actively managed by this manager.
+ * An actively managed session is on that was either created on this server,
+ * brought into local management by a call to
+ * {@link #findLocalSession(String)} or brought into local management by a
+ * call to {@link #findSessions()}.
+ *
+ * @param realId the session id, with any trailing jvmRoute removed.
+ *
+ * @see #getRealId(String)
+ */
+ public ClusteredSession findLocalSession(String realId)
+ {
+ return (ClusteredSession) sessions_.get(realId);
+ }
+
+ /**
+ * Removes the session from this Manager's collection of actively managed
+ * sessions. Also removes the session from the distributed cache, both
+ * on this server and on all other server to which this one replicates.
+ */
+ public void remove(Session session)
+ {
+ ClusteredSession clusterSess = (ClusteredSession) session;
+ synchronized (clusterSess)
+ {
+ String realId = clusterSess.getRealId();
+ if (realId == null)
+ return;
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Removing session from store with id: " + realId);
+ }
+
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ clusterSess.removeMyself();
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+
+ // We don't want to replicate this session at the end
+ // of the request; the removal process took care of that
+ SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
+
+ sessions_.remove(realId);
+ stats_.removeStats(realId);
+ activeCounter_--;
+ }
+ }
+ }
+
+ /**
+ * Removes the session from this Manager's collection of actively managed
+ * sessions. Also removes the session from this server's copy of the
+ * distributed cache (but does not remove it from other servers'
+ * distributed cache).
+ */
+ public void removeLocal(Session session)
+ {
+ ClusteredSession clusterSess = (ClusteredSession) session;
+ synchronized (clusterSess)
+ {
+ String realId = clusterSess.getRealId();
+ if (realId == null) return;
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Removing session from local store with id: " + realId);
+ }
+
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ clusterSess.removeMyselfLocal();
+ }
+ finally
+ {
+ SessionReplicationContext.finishCacheActivity();
+
+ // We don't want to replicate this session at the end
+ // of the request; the removal process took care of that
+ SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
+
+ sessions_.remove(realId);
+ stats_.removeStats(realId);
+
+ // Update counters.
+ // It's a bit ad-hoc to do it here. But since we currently call
+ // this when session expires ...
+ expiredCounter_++;
+ activeCounter_--;
+ }
+ }
+ }
+
+ /**
+ * Loads a session from the distributed store. If an existing session with
+ * the id is already under local management, that session's internal state
+ * will be updated from the distributed store. Otherwise a new session
+ * will be created and added to the collection of those sessions under
+ * local management.
+ *
+ * @param realId id of the session-id with any jvmRoute removed
+ *
+ * @return the session or <code>null</code> if the session cannot be found
+ * in the distributed store
+ */
+ protected ClusteredSession loadSession(String realId)
+ {
+ if (realId == null)
+ {
+ return null;
+ }
+
+ long begin = System.currentTimeMillis();
+ boolean mustAdd = false;
+ ClusteredSession session = (ClusteredSession) sessions_.get(realId);
+
+ if (session == null)
+ {
+ // JBCLUSTER-15
+ // We need to check for maxActive first before attempting to create a new session
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("createSession: active sessions = " + activeCounter_ +
+ " , session map size = " + sessions_.size() +
+ " and max allowed sessions = " + maxActive_);
+ }
+ // first check if passivation is enabled and reached the max allowed sessions,
+ /// then try to expire/passivate sessions to free memory
+ if(maxActive_ != -1 && sessions_.size() >= maxActive_ && isPassivationEnabled())
+ {
+ processExpires();
+ }
+ // maxActive_ -1 is unlimited
+ if (maxActive_ != -1 && sessions_.size() >= maxActive_)
+ {
+ // Exceeds limit. We need to reject it.
+ rejectedCounter_++;
+ // Catalina api does not specify what happens
+ // but we will throw a runtime exception for now.
+ String msgEnd = (realId == null) ? "" : " id " + realId;
+ throw new IllegalStateException("JBossCacheManager.createSession(): number of " +
+ "active sessions exceeds the maximum limit: " +
+ maxActive_ + " when trying to load session" + msgEnd);
+ }
+
+ // This is either the first time we've seen this session on this
+ // server, or we previously expired it and have since gotten
+ // a replication message from another server
+ mustAdd = true;
+ session = createEmptyClusteredSession();
+ }
+
+ synchronized (session)
+ {
+ boolean doTx = false;
+ try
+ {
+ // We need transaction so any data gravitation replication
+ // is sent in batch.
+ // Don't do anything if there is already transaction context
+ // associated with this thread.
+ if(tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ // Ignore cache notifications we may generate for this
+ // session if data gravitation occurs.
+ SessionReplicationContext.startCacheActivity();
+
+ session = proxy_.loadSession(realId, session);
+ }
+ catch (Exception ex)
+ {
+ try
+ {
+// if(doTx)
+ // Let's set it no matter what.
+ tm.setRollbackOnly();
+ }
+ catch (Exception exn)
+ {
+ log_.error("Caught exception rolling back transaction", exn);
+ }
+ // We will need to alert Tomcat of this exception.
+ if (ex instanceof RuntimeException)
+ throw (RuntimeException) ex;
+
+ throw new RuntimeException("loadSession(): failed to load session " +
+ realId, ex);
+ }
+ finally
+ {
+ try {
+ if(doTx)
+ endTransaction(realId);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ if (session != null)
+ {
+ // Need to initialize.
+ session.initAfterLoad(this);
+ if (mustAdd)
+ add(session, false); // don't replicate
+ long elapsed = System.currentTimeMillis() - begin;
+ stats_.updateLoadStats(realId, elapsed);
+
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("loadSession(): id= " + realId + ", session=" + session);
+ }
+ }
+ else if (log_.isDebugEnabled())
+ {
+ log_.debug("loadSession(): session " + realId +
+ " not found in distributed cache");
+ }
+ }
+
+ return session;
+ }
+
+ /**
+ * Places the current session contents in the distributed cache and
+ * replicates them to the cluster
+ *
+ * @param session the session. Cannot be <code>null</code>.
+ */
+ protected void processSessionRepl(ClusteredSession session)
+ {
+ // If we are using SESSION granularity, we don't want to initiate a TX
+ // for a single put
+ boolean notSession = (replicationGranularity_ != ReplicationGranularity.SESSION);
+ boolean doTx = false;
+ try
+ {
+ // We need transaction so all the replication are sent in batch.
+ // Don't do anything if there is already transaction context
+ // associated with this thread.
+ if(notSession && tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ // Tell the proxy to ignore cache notifications we are about
+ // to generate for this session. We have to do this
+ // at this level because we don't want to resume handling
+ // notifications until any compensating changes resulting
+ // from a tx rollback are done.
+ SessionReplicationContext.startCacheActivity();
+
+ session.processSessionRepl();
+ }
+ catch (Exception ex)
+ {
+ log_.debug("processSessionRepl(): failed with exception", ex);
+
+ try
+ {
+ //if(doTx)
+ // Let's setRollbackOnly no matter what.
+ // (except if there's no tx due to SESSION (JBAS-3840))
+ if (notSession)
+ tm.setRollbackOnly();
+ }
+ catch (Exception exn)
+ {
+ log_.error("Caught exception rolling back transaction", exn);
+ }
+
+ // We will need to alert Tomcat of this exception.
+ if (ex instanceof RuntimeException)
+ throw (RuntimeException) ex;
+
+ throw new RuntimeException("JBossCacheManager.processSessionRepl(): " +
+ "failed to replicate session.", ex);
+ }
+ finally
+ {
+ try {
+ if(doTx)
+ endTransaction(session.getId());
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+ }
+
+ protected void endTransaction(String id)
+ {
+ if (tm == null)
+ {
+ log_.warn("JBossCacheManager.endTransaction(): tm is null for id: " +id);
+ return;
+ }
+
+
+ try
+ {
+ if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
+ {
+ tm.commit();
+ }
+ else
+ {
+ log_.info("JBossCacheManager.endTransaction(): rolling back tx for id: " +id);
+ tm.rollback();
+ }
+ }
+ catch (RollbackException re)
+ {
+ // Do nothing here since cache may rollback automatically.
+ log_.warn("JBossCacheManager.endTransaction(): rolling back transaction with exception: " +re);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheManager.endTransaction(): Exception for id: " +id, e);
+ }
+ }
+
+ /**
+ * Gets the classloader of the webapp we are managing.
+ */
+ protected ClassLoader getWebappClassLoader()
+ {
+ return tcl_;
+ }
+
+ /**
+ * Goes through all sessions and look if they have expired.
+ * Note this overrides the method in JBossManager.
+ */
+ protected void processExpires()
+ {
+ if (maxInactiveInterval_ < 0)
+ {
+ return;
+ }
+
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("processExpires():max active sessions = " + maxActive_);
+ log_.trace("processExpires(): passivation mode = " + isPassivationEnabled());
+ log_.trace("processExpires(): Looking for sessions that have expired ...");
+ }
+ try
+ {
+ // First, handle the sessions we are actively managing
+ Session sessions[] = findLocalSessions();
+ for (int i = 0; i < sessions.length; ++i)
+ {
+ try
+ {
+ ClusteredSession session = (ClusteredSession) sessions[i];
+ if(session == null)
+ {
+ log_.warn("processExpires(): processing null session at index " +i);
+ continue;
+ }
+
+ // JBAS-2403. Check for outdated sessions where we think
+ // the local copy has timed out. If found, refresh the
+ // session from the cache in case that might change the timeout
+ if (session.isOutdated() && !(session.isValid(false)))
+ {
+ // JBAS-2792 don't assign the result of loadSession to session
+ // just update the object from the cache or fall through if
+ // the session has been removed from the cache
+ loadSession(session.getRealId());
+ }
+
+ // Do a normal invalidation check that will expire any
+ // sessions that have timed out
+ // DON'T SYNCHRONIZE on session here -- isValid() and
+ // expire() are meant to be multi-threaded and synchronize
+ // properly internally; synchronizing externally can lead
+ // to deadlocks!!
+ if (!session.isValid()) continue;
+
+ // JBCLUSTER-15
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("processExpires(): Checking passivation for session " + session.getId());
+ }
+ // now that we have valid session, see if we need to
+ // passivate it based on the configurable passivation min and max Idle time
+ if (isPassivationEnabled())
+ {
+ long timeNow = System.currentTimeMillis();
+ int timeIdle = (int) ((timeNow - session.getLastAccessedTimeInternal()) / 1000L);
+ // if maxIdle time configured, means that we need to passivate sessions that have
+ // exceeded the max allowed idle time
+ if (passivationMaxIdleTime_ >= 0 && timeIdle > passivationMaxIdleTime_)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("JBossCacheManager.processExpires() passivating session " + session.getRealId());
+ }
+ processSessionPassivation(session.getRealId(), this.getContainer().getParent().getName());
+ }
+ // If the session didn't exceed the passivationMaxIdleTime_, See
+ // if the number of sessions managed by this manager greater than the max allowed
+ // active sessions, passivate the session if it exceed passivationMinIdleTime_
+ else if (maxActive_ > 0 && passivationMinIdleTime_ > 0 && sessions_.size()> maxActive_)
+ {
+ if(timeIdle > passivationMinIdleTime_)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.debug("JBossCacheManager.processExpires() passivating session " + session.getRealId());
+ }
+ processSessionPassivation(session.getRealId(), this.getContainer().getParent().getName());
+ }
+ }
+ }
+
+ }
+ catch (Exception ex)
+ {
+ log_.error("processExpires(): failed expiring " +
+ sessions[i].getIdInternal() + " with exception: " +
+ ex, ex);
+ }
+ }
+
+ // Next, handle any unloaded sessions that are stale
+
+ long now = System.currentTimeMillis();
+ Map unloaded = new HashMap(unloadedSessions_);
+ Set entries = unloaded.entrySet();
+ for (Iterator it = entries.iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) it.next();
+ OwnedSessionUpdate osu = (OwnedSessionUpdate) entry.getValue();
+ int elapsed = (int) ((now - osu.updateTime) / 1000L);
+ if (elapsed >= maxInactiveInterval_)
+ {
+ String realId = (String) entry.getKey();
+ try
+ {
+ proxy_.removeSessionLocal(realId, osu.owner);
+ unloadedSessions_.remove(realId);
+ }
+
+ // JBClUSTER-15
+ // we don't need to worry about session passivation here, since the
+ // method processSessionPassivation() takes care of unloadedSessions_ map
+ // when it receives a notification of passivation event happened in the
+ // distributed store from the CacheListener
+ catch (Exception ex)
+ {
+ log_.error("processExpire(): failed removing unloaded session " +
+ realId + " with exception: " +
+ ex, ex);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ log_.error("processExpires: failed with exception: " + ex, ex);
+ }
+ }
+
+ public void processRemoteAttributeRemoval(String realId, String attrKey)
+ {
+
+ ClusteredSession session = findLocalSession(realId);
+ if (session != null)
+ {
+ boolean localCall = false; // call is due to remote event
+ boolean localOnly = true; // don't call back into cache
+ boolean notify = false; // SRV.10.7 gives us leeway
+ // not to notify listeners,
+ // which is safer
+
+ // Ensure the correct TCL is in place
+ ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ Thread.currentThread().setContextClassLoader(tcl_);
+ synchronized (session)
+ {
+ session.removeAttributeInternal(attrKey, localCall, localOnly, notify);
+ }
+ if (log_.isTraceEnabled())
+ log_.trace("processRemoteAttributeRemoval: removed attribute " +
+ attrKey + " from " + realId);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(prevTcl);
+ }
+ }
+ }
+
+ public void processRemoteInvalidation(String realId)
+ {
+ // Remove the session from our local map
+ ClusteredSession session = (ClusteredSession) sessions_.remove(realId);
+ if (session == null)
+ {
+ // We weren't managing the session anyway. But remove it
+ // from the list of cached sessions we haven't loaded
+ if (unloadedSessions_.remove(realId) != null)
+ {
+ if (log_.isTraceEnabled())
+ log_.trace("Removed entry for session " + realId + " from unloaded session map");
+ }
+ }
+ else
+ {
+ // Expire the session
+ // DON'T SYNCHRONIZE ON SESSION HERE -- isValid() and
+ // expire() are meant to be multi-threaded and synchronize
+ // properly internally; synchronizing externally can lead
+ // to deadlocks!!
+ boolean notify = false; // Don't notify listeners. SRV.10.7
+ // allows this, and sending notifications
+ // leads to all sorts of issues; e.g.
+ // circular calls with ClusteredSSO
+ boolean localCall = false; // this call originated from the cache;
+ // we have already removed session
+ boolean localOnly = true; // Don't pass attr removals to cache
+
+ // Ensure the correct TCL is in place
+ ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ Thread.currentThread().setContextClassLoader(tcl_);
+ session.expire(notify, localCall, localOnly);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(prevTcl);
+ }
+
+ // Remove any stats for this session
+ stats_.removeStats(realId);
+
+ // Update counter.
+ activeCounter_--;
+ }
+ }
+
+ public void processSessionPassivation(String realId, String dataOwner)
+ {
+ // get the session from the local map
+ ClusteredSession session = findLocalSession(realId);
+ // only remove actively managed session and add to the unloaded sessions
+ // if it's already unloaded session (session == null) don't do anything,
+ // the evict notification will tell the server that has the session to remove it.
+ if (session != null)
+ {
+ synchronized (session)
+ {
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("Passivating session with id: " + realId);
+ }
+
+ try {
+ // Tell the proxy to ignore cache notifications we are about
+ // to generate for this session.
+ SessionReplicationContext.startCacheActivity();
+ session.passivate();
+ proxy_.evictSession(realId);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+
+ Object obj = unloadedSessions_.put(realId,
+ new OwnedSessionUpdate(dataOwner, session.getLastAccessedTime()));
+ if (log_.isTraceEnabled())
+ {
+ if (obj == null)
+ {
+ log_.trace("New session " + realId + " added to unloaded session map");
+ }
+ else
+ {
+ log_.trace("Updated timestamp for unloaded session " + realId);
+ }
+ }
+ sessions_.remove(realId);
+ stats_.removeStats(realId);
+ }
+ activeCounter_--;
+ }
+ }
+
+ /**
+ * Gets the session id with any jvmRoute removed.
+ *
+ * @param id a session id with or without an appended jvmRoute.
+ * Cannot be <code>null</code>.
+ */
+ protected String getRealId(String id)
+ {
+ return (useJK_ ? Util.getRealId(id) : id);
+ }
+
+ /**
+ * Callback from the CacheListener to notify us that a session
+ * we haven't loaded has been changed.
+ *
+ * @param realId the session id, without any trailing jvmRoute
+ * @param dataOwner the owner of the session. Can be <code>null</code> if
+ * the owner is unknown.
+ */
+ protected void unloadedSessionChanged(String realId, String dataOwner)
+ {
+ Object obj = unloadedSessions_.put(realId,
+ new OwnedSessionUpdate(dataOwner, System.currentTimeMillis()));
+ if (log_.isTraceEnabled())
+ {
+ if (obj == null)
+ {
+ log_.trace("New session " + realId + " added to unloaded session map");
+ }
+ else
+ {
+ log_.trace("Updated timestamp for unloaded session " + realId);
+ }
+ }
+ }
+
+ /**
+ * Returns true if the passivation mode is set to true in JBoss-web.xml and JBoss Cache passivation
+ * has been enabled with proper configured cache loader. Otherwise, it returns false
+ *
+ * @return
+ */
+ protected boolean isPassivationEnabled()
+ {
+ return (passivationMode_ && proxy_.isCachePassivationEnabled());
+ }
+ // ---------------------------------------------------- Lifecyle Unembedded
+
+ /**
+ * Start this Manager when running embedded in JBoss AS.
+ *
+ * @throws org.apache.catalina.LifecycleException
+ */
+ private void startEmbedded() throws LifecycleException
+ {
+ super.start();
+
+ // Start the JBossCacheService
+ // Will need to pass the classloader that is associated with this
+ //web app so de-serialization will work correctly.
+ tcl_ = super.getContainer().getLoader().getClassLoader();
+
+ proxy_.start(tcl_, this);
+
+ tm = proxy_.getTransactionManager();
+ if(tm == null)
+ {
+ throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
+ }
+
+ try
+ {
+ initializeUnloadedSessions();
+
+ // Setup our SnapshotManager
+ initSnapshotManager();
+
+ // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
+ installValves();
+
+ log_.debug("start(): JBossCacheService started");
+ }
+ catch (Exception e)
+ {
+ log_.error("Unable to start manager.", e);
+ throw new LifecycleException(e);
+ }
+ }
+
+ // ----------------------------------------------- Lifecyle When Unembedded
+
+ /**
+ * Start this Manager when running in standalone Tomcat.
+ */
+ private void startUnembedded() throws LifecycleException
+ {
+ if (started_)
+ {
+ return;
+ }
+
+ log_.info("Manager is about to start");
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(BEFORE_START_EVENT, this);
+
+ if (snapshotMode_ == null)
+ {
+ // We were not instantiated by a JBossCacheCluster, so we need to
+ // find one and let it configure our cluster-wide properties
+ try
+ {
+ JBossCacheCluster cluster = (JBossCacheCluster) container_.getCluster();
+ cluster.configureManager(this);
+ }
+ catch (ClassCastException e)
+ {
+ String msg = "Cluster is not an instance of JBossCacheCluster";
+ log_.error(msg, e);
+ throw new LifecycleException(msg, e);
+ }
+ }
+
+ // Validate attributes
+
+ try
+ {
+ this.invalidateSessionPolicy_ = InvalidateSessionPolicy.fromString(replTriggerString_);
+ }
+ catch (IllegalArgumentException iae)
+ {
+ throw new LifecycleException("replication-trigger value set to a " +
+ "non-valid value: '" +
+ replTriggerString_ +
+ "' (should be ['SET_AND_GET', " +
+ "'SET_AND_NON_PRIMITIVE_GET', 'SET'])");
+ }
+
+ try
+ {
+ }
+ catch (IllegalArgumentException iae)
+ {
+ throw new LifecycleException("replication-granularity value set to " +
+ "a non-valid value: '" +
+ replGranularityString_ +
+ "' (should be ['SESSION', " +
+ "'ATTRIBUTE' or 'FIELD'])");
+ }
+
+ // Create the JBossCacheService
+ try
+ {
+ proxy_ = new JBossCacheService(cacheObjectNameString_);
+
+ // Confirm our replication granularity is compatible with the cache
+ // Throws ISE if not
+ validateFieldMarshalling();
+
+ // We need to pass the classloader that is associated with this
+ // web app so de-serialization will work correctly.
+ tcl_ = container_.getLoader().getClassLoader();
+ proxy_.start(tcl_, this);
+ }
+ catch (Throwable t)
+ {
+ String str = "Problem starting JBossCacheService for Tomcat clustering";
+ log_.error(str, t);
+ throw new LifecycleException(str, t);
+ }
+
+ tm = proxy_.getTransactionManager();
+ if(tm == null)
+ {
+ throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
+ }
+
+ try
+ {
+ initializeUnloadedSessions();
+
+ // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
+ installValves();
+
+ started_ = true;
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(AFTER_START_EVENT, this);
+
+ log_.debug("start(): JBossCacheService started");
+ }
+ catch (Exception e)
+ {
+ log_.error("Unable to start manager.", e);
+ throw new LifecycleException(e);
+ }
+
+ try
+ {
+ registerMBeans();
+ }
+ catch (Exception e)
+ {
+ log_.error("Could not register ManagerMBean with MBeanServer", e);
+ }
+ }
+
+ /**
+ * Register this Manager with JMX.
+ */
+ private void registerMBeans()
+ {
+ try
+ {
+ MBeanServer server = getMBeanServer();
+
+ String domain;
+ if (container_ instanceof ContainerBase)
+ {
+ domain = ((ContainerBase) container_).getDomain();
+ }
+ else
+ {
+ domain = server.getDefaultDomain();
+ }
+ String hostName = ((Host) container_.getParent()).getName();
+ hostName = (hostName == null) ? "localhost" : hostName;
+ ObjectName clusterName = new ObjectName(domain
+ + ":service=ClusterManager,WebModule=//" + hostName
+ + ((Context) container_).getPath());
+
+ if (server.isRegistered(clusterName))
+ {
+ log_.warn("MBean " + clusterName + " already registered");
+ return;
+ }
+
+ objectName_ = clusterName;
+ server.registerMBean(this, clusterName);
+
+ }
+ catch (Exception ex)
+ {
+ log_.error(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Unregister this Manager from the JMX server.
+ */
+ private void unregisterMBeans()
+ {
+ if (mserver_ != null)
+ {
+ try
+ {
+ mserver_.unregisterMBean(objectName_);
+ }
+ catch (Exception e)
+ {
+ log_.error(e);
+ }
+ }
+ }
+
+ /**
+ * Get the current MBean Server.
+ *
+ * @return
+ * @throws Exception
+ */
+ private MBeanServer getMBeanServer() throws Exception
+ {
+ if (mserver_ == null)
+ {
+ mserver_ = MBeanServerLocator.locateJBoss();
+ }
+ return (mserver_);
+ }
+
+ /**
+ * Gets the ids of all sessions in the distributed cache and adds
+ * them to the unloaded sessions map, with the current time as the
+ * last replication time. This means these sessions may not be
+ * evicted from the cache for a period well beyond when they would
+ * normally expire, but this is a necessary tradeoff to avoid
+ * deserializing them all to check their lastAccessedTime.
+ */
+ private void initializeUnloadedSessions() throws CacheException
+ {
+ Map sessions = proxy_.getSessionIds();
+ if (sessions != null)
+ {
+ long now = System.currentTimeMillis();
+ for (Iterator it = sessions.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Entry) it.next();
+ unloadedSessions_.put(entry.getKey(),
+ new OwnedSessionUpdate((String) entry.getValue(), now));
+ }
+ }
+ }
+
+ /**
+ * Instantiate a SnapshotManager and ClusteredSessionValve and add
+ * the valve to our parent Context's pipeline.
+ * Add a JvmRouteValve and BatchReplicationClusteredSessionValve if needed.
+ *
+ */
+ private void installValves()
+ {
+ if (useJK_)
+ {
+ log_.info("We are using mod_jk(2) for load-balancing. " +
+ "Will add JvmRouteValve.");
+
+ installContextValve(new JvmRouteValve(this));
+ }
+
+ // Add batch replication valve if needed.
+ // TODO -- should we add this even if not FIELD in case a cross-context
+ // call traverses a field-based webapp?
+ if (replicationGranularity_ == ReplicationGranularity.FIELD &&
+ Boolean.TRUE.equals(replicationFieldBatchMode_))
+ {
+ Valve batchValve = new BatchReplicationClusteredSessionValve(this);
+ log_.debug("Adding BatchReplicationClusteredSessionValve for batch replication.");
+ installContextValve(batchValve);
+ }
+
+ // Add clustered session valve
+ ClusteredSessionValve valve = new ClusteredSessionValve();
+ installContextValve(valve);
+ }
+
+ /**
+ * Create and start a snapshot manager.
+ */
+ private void initSnapshotManager()
+ {
+ String ctxPath = ((Context) container_).getPath();
+ if ("instant".equals(snapshotMode_)
+ || replicationGranularity_== ReplicationGranularity.FIELD)
+ {
+ snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
+ }
+ else if ("interval".equals(snapshotMode_))
+ {
+ snapshotManager_ = new IntervalSnapshotManager(this, ctxPath, snapshotInterval_);
+ }
+ else
+ {
+ log_.error("Snapshot mode must be 'instant' or 'interval' - " +
+ "using 'instant'");
+ snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
+ }
+
+ snapshotManager_.start();
+ }
+
+ private void installContextValve(Valve valve)
+ {
+ boolean installed = false;
+
+ // In embedded mode, install the valve via JMX to be consistent
+ // with the way the overall context is created in TomcatDeployer.
+ // We can't do this in unembedded mode because we are called
+ // before our Context is registered with the MBean server
+ if (embedded_ && getContextObjectName() != null) {
+ try
+ {
+ getMBeanServer().invoke(getContextObjectName(), "addValve",
+ new Object[]{valve},
+ new String[]{"org.apache.catalina.Valve"});
+ installed = true;
+ }
+ catch (Exception e)
+ {
+ // JBAS-2422. If the context is restarted via JMX, the above
+ // JMX call will fail as the context will not be registered
+ // when it's made. So we catch the exception and fall back
+ // to adding the valve directly.
+ // TODO consider skipping adding via JMX and just do it directly
+ log_.debug("Caught exception installing valve to Context", e);
+ }
+ }
+
+ if (!installed)
+ {
+ // If possible install via the ContainerBase.addValve() API.
+ if (container_ instanceof ContainerBase)
+ {
+ ((ContainerBase) container_).addValve(valve);
+ }
+ else
+ {
+ // No choice; have to add it to the context's pipeline
+ container_.getPipeline().addValve(valve);
+ }
+ }
+ }
+
+ /**
+ * If we are using FIELD granularity, checks that the TreeCache
+ * supports marshalling.
+ *
+ * @throws IllegalStateException if not
+ */
+ private void validateFieldMarshalling()
+ {
+ if (replicationGranularity_ == ReplicationGranularity.FIELD
+ && !proxy_.isMarshallingAvailable())
+ {
+ // BES 16/8/2006 -- throw ISE, not ClusteringNotSupportedException, as a
+ // misconfig should be treated differently from the absence of clustering
+ // services
+ throw new IllegalStateException("replication-granularity value is set to " +
+ "'FIELD' but is not supported by the cache service configuration. " +
+ "Must set 'UseRegionBasedMarshalling' to 'true' in the " +
+ "jboss-web-cluster.sar jboss-service.xml");
+ }
+ }
+
+ private ObjectName getContextObjectName()
+ {
+ String oname = container_.getObjectName();
+ try
+ {
+ return (oname == null) ? null : new ObjectName(oname);
+ }
+ catch (MalformedObjectNameException e)
+ {
+ log_.warn("Error creating object name from string " + oname, e);
+ return null;
+ }
+ }
+
+
+ private class OwnedSessionUpdate
+ {
+ String owner;
+ long updateTime;
+
+ OwnedSessionUpdate(String owner, long updateTime)
+ {
+ this.owner = owner;
+ this.updateTime = updateTime;
+ }
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,128 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+public interface JBossCacheManagerMBean extends JBossManagerMBean
+{
+ /**
+ * Gets the value of the attribute with the given key from the given
+ * session. If the session is in the distributed store but hasn't been
+ * loaded on this node, invoking this method will cause it to be loaded.
+ *
+ * @param sessionId the id of the session
+ * @param key the attribute key
+ * @return the value, or <code>null</code> if the session or
+ * key does not exist.
+ */
+ Object getSessionAttribute(String sessionId, String key);
+
+ /**
+ * Same as <code>getSessionAttribute(sessionId, key).toString()</code>.
+ *
+ */
+ String getSessionAttributeString(String sessionId, String key);
+
+ /**
+ * Expires the given session. If the session is in the distributed store
+ * but hasn't been loaded on this node, invoking this method will cause it
+ * to be loaded.
+ *
+ * @param sessionId the id of the session
+ */
+ void expireSession(String sessionId);
+
+ /**
+ * Gets the last time the given session was accessed on this node.
+ * Information about sessions stored in the distributed store but never
+ * accessed on this node will not be made available.
+ *
+ * @param sessionId
+ * @return the last accessed time, or <code>null</code> if the session
+ * has expired or has never been accessed on this node.
+ */
+ String getLastAccessedTime(String sessionId);
+
+ /**
+ * Gets the JMX ObjectName of the distributed session cache as a string.
+ */
+ String getCacheObjectNameString();
+
+ /**
+ * Gets the replication granularity.
+ *
+ * @return SESSION, ATTRIBUTE or FIELD, or <code>null</code> if this
+ * has not yet been configured.
+ */
+ String getReplicationGranularityString();
+
+ /**
+ * Gets the replication trigger.
+ *
+ * @return SET, SET_AND_GET, SET_AND_NON_PRIMITIVE_GET or <code>null</code>
+ * if this has not yet been configured.
+ */
+ String getReplicationTriggerString();
+
+ /**
+ * Gets whether batching of field granularity changes will be done. Only
+ * relevant with replication granularity FIELD.
+ *
+ * @return <code>true</code> if per-request batching will be done,
+ * <code>false</code> if not, <code>null</code> if not configured
+ */
+ Boolean isReplicationFieldBatchMode();
+
+ /**
+ * Gets whether JK is being used and special handling of a jvmRoute
+ * portion of session ids is needed.
+ */
+ boolean getUseJK();
+
+ /**
+ * Gets the snapshot mode.
+ *
+ * @return "instant" or "interval"
+ */
+ String getSnapshotMode();
+
+ /**
+ * Gets the number of milliseconds between replications if "interval" mode
+ * is used.
+ */
+ int getSnapshotInterval();
+
+ /**
+ * Lists all session ids known to this manager, including those in the
+ * distributed store that have not been accessed on this node.
+ *
+ * @return a comma-separated list of session ids
+ */
+ String listSessionIds();
+
+ /**
+ * Lists all session ids known to this manager, excluding those in the
+ * distributed store that have not been accessed on this node.
+ *
+ * @return a comma-separated list of session ids
+ */
+ String listLocalSessionIds();
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1146 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.WeakHashMap;
+
+import javax.management.ObjectName;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.Context;
+import org.jboss.aspects.patterns.observable.Observer;
+import org.jboss.aspects.patterns.observable.Subject;
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
+import org.jboss.cache.Region;
+import org.jboss.cache.pojo.PojoCache;
+import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.config.CacheLoaderConfig;
+import org.jboss.cache.transaction.BatchModeTransactionManager;
+import org.jboss.invocation.MarshalledValue;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.serial.io.MarshalledObject;
+
+/**
+ * A wrapper class to JBossCache. This is currently needed to handle various operations such as
+ * <ul>
+ * <li>Using MarshalledValue to replace Serializable used inside different web app class loader context.</li>
+ * <li>Stripping out any id string after ".". This is to handle the JK failover properly with
+ * Tomcat JvmRoute.</li>
+ * <li>Cache exception retry.</li>
+ * <li>Helper APIS.</li>
+ * </ul>
+ */
+public class JBossCacheService
+{
+ protected static Logger log_ = Logger.getLogger(JBossCacheService.class);
+ public static final String BUDDY_BACKUP = BuddyManager.BUDDY_BACKUP_SUBTREE;
+ public static final Fqn BUDDY_BACKUP_FQN = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN;
+ public static final String SESSION = "JSESSION";
+ public static final String ATTRIBUTE = "ATTRIBUTE";
+ // Needed for cache invalidation
+ static final String VERSION_KEY = "VERSION";
+ static final String FQN_DELIMITER = "/";
+
+ private PojoCache pojoCache_;
+ private Cache plainCache_;
+ private ObjectName cacheServiceName_;
+
+ // name of webapp's virtual host(JBAS-2194).
+ // Idea is host_name + web_app_path + session id is a unique combo.
+ private String hostName_;
+ // web app path (JBAS-1367 and JBAS-2194).
+ // Idea is host_name + web_app_path + session id is a unique combo.
+ private String webAppPath_;
+ private TransactionManager tm;
+
+ private JBossCacheManager manager_;
+ private CacheListener cacheListener_;
+ private JBossCacheWrapper cacheWrapper_;
+
+ // Do we have to marshall attributes ourself or can we let
+ // the TreeCache do it?
+ private boolean useTreeCacheMarshalling_ = false;
+
+ private WeakHashMap typeMap = new WeakHashMap();
+
+ public JBossCacheService(String treeCacheObjectName) throws ClusteringNotSupportedException
+ {
+ // Find JBossCacheService
+ try
+ {
+ cacheServiceName_ = new ObjectName(treeCacheObjectName);
+ // Get the pojo cache
+ PojoCacheJmxWrapperMBean mbean = (PojoCacheJmxWrapperMBean) MBeanProxyExt.create(PojoCacheJmxWrapperMBean.class,
+ cacheServiceName_);
+ if (mbean != null)
+ {
+ pojoCache_ = mbean.getPojoCache();
+ }
+ }
+ catch (Throwable t)
+ {
+
+ String str = "Could not access TreeCache service " +
+ (cacheServiceName_ == null ? "<null>" : cacheServiceName_.toString()) +
+ " for Tomcat clustering";
+ log_.debug(str);
+ throw new ClusteringNotSupportedException(str, t);
+ }
+
+ if (pojoCache_ == null)
+ {
+ String str = "Could not access TreeCache service " +
+ (cacheServiceName_ == null ? "<null>" : cacheServiceName_.toString()) +
+ " for Tomcat clustering";
+ log_.debug(str);
+ throw new ClusteringNotSupportedException(str);
+ }
+
+ plainCache_ = pojoCache_.getCache();
+ cacheWrapper_ = new JBossCacheWrapper(pojoCache_);
+
+ useTreeCacheMarshalling_ = plainCache_.getConfiguration().isUseRegionBasedMarshalling();
+ }
+
+ public void start(ClassLoader tcl, JBossCacheManager manager)
+ {
+ manager_ = manager;
+
+ Context webapp = (Context) manager_.getContainer();
+ String path = webapp.getName();
+ if( path.length() == 0 || path.equals("/")) {
+ // If this is root.
+ webAppPath_ = "ROOT";
+ } else if ( path.startsWith("/") ) {
+ webAppPath_ = path.substring(1);
+ } else {
+ webAppPath_ = path;
+ }
+ // JBAS-3941 -- context path can be multi-level, but we don't
+ // want that turning into a multilevel Fqn, so escape it
+ // Use '?' which is illegal in a context path
+ webAppPath_ = webAppPath_.replace('/', '?');
+ log_.debug("Old and new web app path are: " +path + ", " +webAppPath_);
+
+ String host = webapp.getParent().getName();
+ if( host == null || host.length() == 0) {
+ hostName_ = "localhost";
+ }else {
+ hostName_ = host;
+ }
+ log_.debug("Old and new virtual host name are: " + host + ", " + hostName_);
+
+
+ // Listen for cache changes
+ cacheListener_ = new CacheListener(cacheWrapper_, manager_, hostName_, webAppPath_);
+ plainCache_.addCacheListener(cacheListener_);
+
+ // register the tcl and bring over the state for the webapp
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+ Fqn pathFqn = new Fqn( objs );
+ try {
+ if(useTreeCacheMarshalling_)
+ {
+ log_.debug("UseMarshalling is true. We will register the fqn: " +
+ pathFqn + " with class loader" +tcl +
+ " and activate the webapp's Region");
+ Region region = plainCache_.getRegion(pathFqn, true);
+ region.registerContextClassLoader(tcl);
+ region.activate();
+ }
+ } catch (Exception ex)
+ {
+ throw new RuntimeException("Can't register class loader", ex);
+ }
+
+ // We require the cache tm to be BatchModeTransactionManager now.
+ tm = plainCache_.getConfiguration().getRuntimeConfig().getTransactionManager();
+ if( ! (tm instanceof BatchModeTransactionManager) )
+ {
+ throw new RuntimeException("JBossCacheService.start(): JBossCacheAop transaction manager is not of type BatchModeTransactionManager." +
+ " Please check the jboss-web-cluster-service.xml TransactionManagerClassLookup field.");
+ }
+
+ if(isCachePassivationEnabled())
+ {
+ log_.debug("JBossCache passivation is enabled");
+ }
+ else
+ {
+ log_.debug("JBossCache passivation is disabled");
+ }
+ }
+
+ public void stop()
+ {
+ plainCache_.removeCacheListener(cacheListener_);
+
+ // Construct the fqn
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+ Fqn pathFqn = new Fqn( objs );
+
+ if(useTreeCacheMarshalling_)
+ {
+ log_.debug("UseMarshalling is true. We will inactivate the fqn: " +
+ pathFqn + " and un-register its classloader");
+
+ try {
+ Region region = plainCache_.getRegion(pathFqn, false);
+ if (region != null)
+ {
+ region.deactivate();
+ region.unregisterContextClassLoader();
+ }
+ }
+ catch (Exception e)
+ {
+ log_.error("Exception during inactivation of webapp region " + pathFqn +
+ " or un-registration of its class loader", e);
+ }
+ }
+
+ // remove session data
+ cacheWrapper_.removeLocalSubtree(pathFqn);
+ }
+
+ /**
+ * Get specfically the BatchModeTransactionManager.
+ */
+ public TransactionManager getTransactionManager()
+ {
+ return tm;
+ }
+
+ /**
+ * Gets whether TreeCache-based marshalling is available
+ */
+ public boolean isMarshallingAvailable()
+ {
+ return useTreeCacheMarshalling_;
+ }
+
+ /**
+ * Loads any serialized data in the cache into the given session
+ * using its <code>readExternal</code> method.
+ *
+ * @return the session passed as <code>toLoad</code>, or
+ * <code>null</code> if the cache had no data stored
+ * under the given session id.
+ */
+ public ClusteredSession loadSession(String realId, ClusteredSession toLoad)
+ {
+ Fqn fqn = getSessionFqn(realId);
+ Object sessionData = cacheWrapper_.get(fqn, realId, true);
+
+ if (sessionData == null) {
+ // Requested session is no longer in the cache; return null
+ return null;
+ }
+
+ boolean firstLoad = (toLoad.getVersion() == 0);
+
+// if (useTreeCacheMarshalling_)
+// {
+// toLoad.update((ClusteredSession) sessionData);
+// }
+// else
+// {
+ byte[] sessionBytes = (byte[]) sessionData;
+
+ // Swap in/out the webapp classloader so we can deserialize
+ // attributes whose classes are only available to the webapp
+ ClassLoader prevTCL = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(manager_.getWebappClassLoader());
+ try
+ {
+ // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
+ // ByteArrayInputStream bais = new ByteArrayInputStream(sessionBytes);
+ // Use MarshalledValueInputStream instead of superclass ObjectInputStream
+ // or else there are problems finding classes with scoped loaders
+ // MarshalledValueInputStream input = new MarshalledValueInputStream(bais);
+ ObjectInputStream input = SessionSerializationFactory.createObjectInputStream(sessionBytes);
+ toLoad.readExternal(input);
+ input.close();
+ }
+ catch (Exception e)
+ {
+ log_.error("loadSession(): id: " + realId + "exception occurred during serialization: " +e);
+ return null;
+ }
+ finally {
+ Thread.currentThread().setContextClassLoader(prevTCL);
+ }
+// }
+
+ // The internal version of the serialized session may be less than the
+ // real one due to not replicating metadata. If our listener hasn't
+ // been keeping the outdatedVersion of the session up to date because
+ // the session has never been loaded into the JBCManager cache, we
+ // need to fix the version
+ if (firstLoad)
+ {
+ Integer ver = (Integer) cacheWrapper_.get(fqn, VERSION_KEY);
+ if (ver != null)
+ toLoad.setVersion(ver.intValue());
+ }
+
+ return toLoad;
+ }
+
+ public void putSession(String realId, ClusteredSession session)
+ {
+ Fqn fqn = getSessionFqn(realId);
+
+ if (session.getReplicateSessionBody())
+ {
+ Map map = new HashMap();
+// if (useTreeCacheMarshalling_)
+// map.put(realId, session);
+// else
+ map.put(realId, externalizeSession(session));
+ // Put in (VERSION_KEY, version) after the real put for cache invalidation
+ map.put(VERSION_KEY, new Integer(session.getVersion()));
+ cacheWrapper_.put(fqn, map);
+ }
+ else
+ {
+ // Invalidate the remote caches
+ cacheWrapper_.put(fqn, VERSION_KEY, new Integer(session.getVersion()));
+ }
+ }
+
+ public void removeSession(String realId)
+ {
+ Fqn fqn = getSessionFqn(realId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Remove session from distributed store. Fqn: " + fqn);
+ }
+ //Object obj = getUnMarshalledValue(cacheWrapper_.remove(fqn, realId));
+ cacheWrapper_.remove(fqn, realId);
+ // This needs to go after object removal to support correct cache invalidation.
+// _remove(fqn, VERSION_KEY);
+ // Let just remove the whole thing (including the fqn)
+ cacheWrapper_.remove(fqn);
+ //return obj;
+ }
+
+ public void removeSessionLocal(String realId)
+ {
+ Fqn fqn = getSessionFqn(realId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Remove session from my own distributed store only. Fqn: " + fqn);
+ }
+ cacheWrapper_.removeLocalSubtree(fqn);
+ }
+
+ public void removeSessionLocal(String realId, String dataOwner)
+ {
+ if (dataOwner == null)
+ {
+ removeSessionLocal(realId);
+ }
+ else
+ {
+ Fqn fqn = getSessionFqn(realId, dataOwner);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Remove session from my own distributed store only. Fqn: " + fqn);
+ }
+ cacheWrapper_.removeLocalSubtree(fqn);
+ }
+ }
+
+
+ public void evictSession(String realId)
+ {
+ Fqn fqn = getSessionFqn(realId);
+ if(log_.isDebugEnabled())
+ {
+ log_.debug("evictSession(): evicting session from my distributed store. Fqn: " + fqn);
+ }
+ cacheWrapper_.evictSubtree(fqn);
+
+ }
+
+ public boolean exists(String realId)
+ {
+ Fqn fqn = getSessionFqn(realId);
+ return plainCache_.getRoot().hasChild(fqn);
+ }
+
+ public Object getAttribute(String realId, String key)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ return getUnMarshalledValue(cacheWrapper_.get(fqn, key));
+ }
+
+ public void putAttribute(String realId, String key, Object value)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ cacheWrapper_.put(fqn, key, getMarshalledValue(value));
+ }
+
+ public void putAttribute(String realId, Map map)
+ {
+ // Duplicate the map with marshalled values
+ Map marshalled = new HashMap(map.size());
+ Set entries = map.entrySet();
+ for (Iterator it = entries.iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) it.next();
+ marshalled.put(entry.getKey(), getMarshalledValue(entry.getValue()));
+ }
+
+ Fqn fqn = getAttributeFqn(realId);
+ cacheWrapper_.put(fqn, marshalled);
+
+ }
+
+ public void removeAttributes(String realId)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ cacheWrapper_.remove(fqn);
+ }
+
+ public Object removeAttribute(String realId, String key)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("Remove attribute from distributed store. Fqn: " + fqn + " key: " + key);
+ }
+ return getUnMarshalledValue(cacheWrapper_.remove(fqn, key));
+ }
+
+ public void removeAttributesLocal(String realId)
+ {
+ Fqn fqn = getAttributeFqn(realId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Remove attributes from my own distributed store only. Fqn: " + fqn);
+ }
+ cacheWrapper_.removeLocal(fqn);
+ }
+
+ /**
+ * Obtain the keys associated with this fqn. Note that it is not the fqn children.
+ *
+ */
+ public Set getAttributeKeys(String realId)
+ {
+ Set keys = null;
+ Fqn fqn = getAttributeFqn(realId);
+ try
+ {
+ Node node = plainCache_.getRoot().getChild(fqn);
+ if (node != null)
+ keys = node.getKeys();
+ }
+ catch (CacheException e)
+ {
+ log_.error("getAttributeKeys(): Exception getting keys for session " + realId, e);
+ }
+
+ return keys;
+ }
+
+ /**
+ * Return all attributes associated with this session id.
+ *
+ * TODO use Node.getData() and just copy the map
+ *
+ * @param realId the session id with any jvmRoute removed
+ * @return the attributes, or any empty Map if none are found.
+ */
+ public Map getAttributes(String realId)
+ {
+ if (realId == null || realId.length() == 0) return new HashMap();
+
+ Map map = new HashMap();
+ Set set = getAttributeKeys(realId);
+ if(set != null)
+ {
+ for (Iterator it = set.iterator(); it.hasNext();)
+ {
+ String key = (String) it.next();
+ Object value = getAttribute(realId, key);
+ map.put(key, value);
+ }
+ }
+ return map;
+ }
+
+ /**
+ * Gets the ids of all sessions in the underlying cache.
+ *
+ * @return Set containing all of the session ids of sessions in the cache
+ * (with any jvmRoute removed) or <code>null</code> if there
+ * are no sessions in the cache.
+ */
+ public Map getSessionIds() throws CacheException
+ {
+ Map result = new HashMap();
+
+ Node bbRoot = plainCache_.getRoot().getChild(BUDDY_BACKUP_FQN);
+ if (bbRoot != null)
+ {
+ Set owners = bbRoot.getChildren();
+ if (owners != null)
+ {
+ for (Iterator it = owners.iterator(); it.hasNext();)
+ {
+ Node owner = (Node) it.next();
+ Node webRoot = owner.getChild(getWebappFqn());
+ if (webRoot != null)
+ {
+ Set ids = webRoot.getChildrenNames();
+ storeSessionOwners(ids, owner, result);
+ }
+ }
+ }
+ }
+
+ storeSessionOwners(getChildrenNames(getWebappFqn()), null, result);
+
+ return result;
+ }
+
+ private Set getChildrenNames(Fqn fqn)
+ {
+ Node node = plainCache_.getRoot().getChild(fqn);
+ return (node == null ? null : node.getChildrenNames());
+ }
+
+ private void storeSessionOwners(Set ids, Object owner, Map map)
+ {
+ if (ids != null)
+ {
+ for (Iterator it = ids.iterator(); it.hasNext();)
+ {
+ map.put(it.next(), owner);
+ }
+ }
+ }
+
+ /**
+ * store the pojo instance in the cache. Note that this is for the aop cache.
+ * THe pojo needs to be "aspectized".
+ *
+ * @param realId the session id with any jvmRoute removed
+ * @param key the attribute key
+ * @param pojo
+ */
+ public Object setPojo(String realId, String key, Object pojo)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("setPojo(): session id: " + realId + " key: " + key +
+ " object: " + pojo.toString());
+ }
+ // Construct the fqn.
+ Fqn fqn = getFieldFqn(realId, key);
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ return pojoCache_.attach(fqn.toString(), pojo);
+ } catch (CacheException e) {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache setPojo ... ", e);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ /**
+ * Remove pojo from the underlying cache store.
+ * @param realId the session id with any jvmRoute removed
+ * @param key the attribute key
+ * @return pojo that just removed. Null if there none.
+ */
+ public Object removePojo(String realId, String key)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("removePojo(): session id: " +realId + " key: " +key);
+ }
+ // Construct the fqn.
+ Fqn fqn = getFieldFqn(realId, key);
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ return pojoCache_.detach(fqn.toString());
+ } catch (CacheException e) {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache removePojo ... ", e);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ /**
+ * Remove all the pojos from the underlying cache store locally
+ * without replication.
+ *
+ * @param realId the session id with any jvmRoute removed
+ */
+ public void removePojosLocal(String realId)
+ {
+ if(log_.isDebugEnabled())
+ {
+ log_.debug("removePojoLocal(): session id: " +realId);
+ }
+ // Construct the fqn.
+ Fqn fqn = getAttributeFqn(realId);
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ cacheWrapper_.removeLocalSubtree(fqn);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ /**
+ * Remove all the pojos from the underlying cache store locally
+ * without replication.
+ *
+ * @param realId the session id with any jvmRoute removed
+ */
+ public void removePojoLocal(String realId, String key)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("removePojoLocal(): session id: " + realId + " key: " +key);
+ }
+ // Construct the fqn.
+ Fqn fqn = getFieldFqn(realId, key);
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ cacheWrapper_.removeLocalSubtree(fqn);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
+ }
+
+ public Set getPojoKeys(String realId)
+ {
+ Set keys = null;
+ Fqn fqn = getAttributeFqn(realId);
+ try
+ {
+ keys = getChildrenNames(fqn);
+ }
+ catch (CacheException e)
+ {
+ log_.error("getPojoKeys(): Exception getting keys for session " + realId, e);
+ }
+
+ return keys;
+ }
+
+
+ /**
+ *
+ * @param realId the session id with any jvmRoute removed
+ * @param key the attribute key
+ * @return Pojo that is associated with the attribute
+ */
+ public Object getPojo(String realId, String key)
+ {
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("getPojo(): session id: " +realId + " key: " +key);
+ }
+ // Construct the fqn.
+ Fqn fqn = getFieldFqn(realId, key);
+
+ try
+ {
+ return pojoCache_.find(fqn.toString());
+ }
+ catch (CacheException e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache getPojo ... ", e);
+ }
+ }
+
+ /**
+ * Recursively adds session as observer to the pojo graph. Assumes the
+ * whole object graph has Subject "introduction" declared. If a portion
+ * of the graph isn't a Subject, the recursion does not continue below
+ * that part of the graph.
+ *
+ * @param session the session
+ * @param pojo the pojo. Can be <code>null</code>.
+ */
+ public void addObserver(Observer session, Object pojo)
+ {
+ addObserver(session, pojo, new HashSet());
+ }
+
+ private void addObserver(Observer session, Object pojo, Set processed)
+ {
+ if ( pojo instanceof Collection )
+ {
+ Collection col = (Collection)pojo;
+ for (Iterator i = col.iterator(); i.hasNext();) {
+ // If not a managed pojo, will return anyway
+ addObserver(session, i.next(), processed);
+ }
+ }
+ else if (pojo instanceof Map)
+ {
+ for (Iterator i = ((Map)pojo).entrySet().iterator(); i.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+
+ // Walk thru key and value
+ addObserver(session, entry.getKey(), processed);
+ addObserver(session, entry.getValue(), processed);
+ }
+ }
+
+ if(! (pojo instanceof Subject) )
+ {
+ return; // No need to add observer since it is primitive.
+ }
+
+ Subject subject = (Subject)pojo;
+ subject.addObserver(session);
+
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("addObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
+ }
+
+ // Examine each field of the type and its superclasses to see if
+ // we need to add the observer to the pojo held by that field
+ // Traverse recursively
+
+ // First identify and cache the names of all the class'
+ // non-immediate fields
+ Class type = pojo.getClass();
+ Set complexFields = (Set) typeMap.get(type);
+ if (complexFields == null)
+ {
+ complexFields = Util.parseComplexFields(type);
+ typeMap.put(type, complexFields);
+ }
+
+ if (complexFields.size() == 0)
+ return;
+
+ // Store a ref to the pojo to avoid cyclic additions
+ processed.add(pojo);
+
+ for (Iterator iter = complexFields.iterator(); iter.hasNext();)
+ {
+ String fieldName = (String) iter.next();
+ Class curType = type;
+ while (curType != null)
+ {
+ try
+ {
+ Field field = curType.getDeclaredField(fieldName);
+ boolean accessible = field.isAccessible();
+ Object value = null;
+ try
+ {
+ field.setAccessible(true);
+
+ value=field.get(pojo);
+ // Continue recursively unless we've already handled this value
+ if (value != null && !processed.contains(value))
+ addObserver(session, value, processed);
+ break;
+ }
+ catch(IllegalAccessException e)
+ {
+ throw new RuntimeException("field access failed", e);
+ }
+ finally
+ {
+ field.setAccessible(accessible);
+ }
+ }
+ catch (NoSuchFieldException e)
+ {
+ // Check if the field is declared in a superclass
+ curType = curType.getSuperclass();
+ if (curType == null)
+ throw new RuntimeException("Field " + fieldName +
+ " does not exist", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Recursively removes session as observer to the pojo graph. Assumes the
+ * whole object graph has Subject "introduction" declared. If a portion
+ * of the graph isn't a Subject, the recursion does not continue below
+ * that part of the graph.
+ *
+ * @param session the session
+ * @param pojo the pojo to stop observing. Can be <code>null</code>.
+ */
+ public void removeObserver(Observer session, Object pojo)
+ {
+ removeObserver(session, pojo, new HashSet());
+ }
+
+ private void removeObserver(Observer session, Object pojo, Set stack)
+ {
+ if ( pojo instanceof Collection )
+ {
+ Collection col = (Collection)pojo;
+ for (Iterator i = col.iterator(); i.hasNext();) {
+ Object obj = i.next();
+ // If not a managed pojo, will return anyway
+ removeObserver(session, obj, stack);
+ }
+
+ return;
+ }
+ else if (pojo instanceof Map)
+ {
+ Map map = (Map)pojo;
+ for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+ Object key = i.next();
+ Object value = map.get(key);
+
+ // Walk thru key and value
+ removeObserver(session, key, stack);
+ removeObserver(session, value, stack);
+ }
+
+ return;
+ }
+ // BRIAN 3/14 changed this from checking Advised to checking Subject
+ // since that is what we cast to below
+ if(! (pojo instanceof Subject) )
+ {
+ return; // No need to add observer since it is primitive.
+ }
+
+ Subject subject = (Subject)pojo;
+ subject.removeObserver(session);
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("removeObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
+ }
+
+ // Examine each field of the type and its superclasses to see if
+ // we need to remove the observer from the pojo held by that field
+ // Traverse recursively
+
+ // First identify and cache the names of all the class'
+ // non-immediate fields
+ Class type = pojo.getClass();
+ Set complexFields = (Set) typeMap.get(type);
+ if (complexFields == null)
+ {
+ complexFields = Util.parseComplexFields(type);
+ typeMap.put(type, complexFields);
+ }
+
+ if (complexFields.size() == 0)
+ return;
+
+ // Store a ref to the pojo to avoid cyclic removals
+ stack.add(pojo);
+
+ for (Iterator iter = complexFields.iterator(); iter.hasNext();)
+ {
+ String fieldName = (String) iter.next();
+ Class curType = type;
+ while (curType != null)
+ {
+ try
+ {
+ Field field = curType.getDeclaredField(fieldName);
+ boolean accessible = field.isAccessible();
+ Object value = null;
+ try
+ {
+ field.setAccessible(true);
+
+ value=field.get(pojo);
+ // Continue recursively unless we've already handled this value
+ if (value != null && !stack.contains(value))
+ removeObserver(session, value, stack);
+ break;
+ }
+ catch(IllegalAccessException e)
+ {
+ throw new RuntimeException("field access failed", e);
+ }
+ finally
+ {
+ field.setAccessible(accessible);
+ }
+ }
+ catch (NoSuchFieldException e)
+ {
+ // Check if the field is declared in a superclass
+ curType = curType.getSuperclass();
+ if (curType == null)
+ throw new RuntimeException("Field " + fieldName +
+ " does not exist", e);
+ }
+ }
+ }
+ }
+
+ public boolean isCachePassivationEnabled()
+ {
+ CacheLoaderConfig clc = plainCache_.getConfiguration().getCacheLoaderConfig();
+ if(clc != null)
+ {
+ return (clc.isPassivation() && !clc.isShared());
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ private Fqn getFieldFqn(String id, String key)
+ {
+ // /SESSION/id/ATTR/key
+ // Guard against string with delimiter.
+ List list = new ArrayList(6);
+ list.add(SESSION);
+ list.add(hostName_);
+ list.add(webAppPath_);
+ list.add(id);
+ list.add(ATTRIBUTE);
+ breakKeys(key, list);
+ return new Fqn(list);
+ }
+
+ private void breakKeys(String key, List list)
+ {
+ StringTokenizer token = new StringTokenizer(key, FQN_DELIMITER);
+ while(token.hasMoreTokens())
+ {
+ list.add(token.nextToken());
+ }
+ }
+
+ private Fqn getWebappFqn()
+ {
+ // /SESSION/hostname/webAppPath
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+ return new Fqn(objs);
+ }
+
+ private Fqn getWebappFqn(Object dataOwner)
+ {
+ if (dataOwner == null)
+ return getWebappFqn();
+
+ // /SESSION/hostname/webAppPath
+ Object[] objs = new Object[]{BUDDY_BACKUP, dataOwner, SESSION, hostName_, webAppPath_};
+ return new Fqn(objs);
+ }
+
+ private Fqn getSessionFqn(String id)
+ {
+ // /SESSION/hostname/webAppPath/id
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_, id};
+ return new Fqn(objs);
+ }
+
+ private Fqn getSessionFqn(String id, String dataOwner)
+ {
+ // /_BUDDY_BACKUP_/dataOwner/SESSION/hostname/webAppPath/id
+ Object[] objs = new Object[]{BUDDY_BACKUP, dataOwner, SESSION, hostName_, webAppPath_, id};
+ return new Fqn(objs);
+ }
+
+ private Fqn getAttributeFqn(String id)
+ {
+ // /SESSION/hostName/webAppPath/id/ATTR
+ Object[] objs = new Object[]{SESSION, hostName_, webAppPath_, id, ATTRIBUTE};
+ return new Fqn(objs);
+ }
+
+ private Object getMarshalledValue(Object value)
+ {
+ // JBAS-2920. For now, continue using MarshalledValue, as
+ // it allows lazy deserialization of the attribute on remote nodes
+ // For Branch_4_0 this is what we have to do anyway for backwards
+ // compatibility. For HEAD we'll follow suit for now.
+ // TODO consider only using MV for complex objects (i.e. not primitives)
+ // and Strings longer than X.
+
+// if (useTreeCacheMarshalling_)
+// {
+// return value;
+// }
+// else
+// {
+ try
+ {
+ // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
+ // to allow for switching between JBossSerialization and JavaSerialization using
+ // system property -D=session.serialization.jboss=true / false
+ // MarshalledValue mv = new MarshalledValue(value);
+ if (SessionSerializationFactory.useJBossSerialization())
+ {
+ MarshalledObject mo = SessionSerializationFactory.createMarshalledObject(value);
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("JBoss Marshalled Object to size ");
+ }
+ return mo;
+
+ }
+ else
+ {
+ MarshalledValue mv = SessionSerializationFactory.createMarshalledValue(value);
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("marshalled object to size " + mv.size() + " bytes");
+ }
+ return mv;
+ }
+
+ }
+ catch (IOException e)
+ {
+ log_.error("IOException occurred marshalling value ", e);
+ return null;
+ }
+// }
+ }
+
+ private Object getUnMarshalledValue(Object mv)
+ {
+ // JBAS-2920. For now, continue using MarshalledValue, as
+ // it allows lazy deserialization of the attribute on remote nodes
+ // For Branch_4_0 this is what we have to do anyway for backwards
+ // compatibility. For HEAD we'll follow suit for now.
+// if (useTreeCacheMarshalling_)
+// {
+// return mv;
+// }
+// else
+// {
+ if (mv == null) return null;
+ // Swap in/out the tcl for this web app. Needed only for un marshalling.
+ ClassLoader prevTCL = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(manager_.getWebappClassLoader());
+ try
+ {
+ // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
+ // to allow for switching between JBossSerialization and JavaSerialization using
+ // system property -D=session.serialization.jboss=true / false
+
+ if (SessionSerializationFactory.useJBossSerialization())
+ {
+ return ((MarshalledObject)mv).get();
+ }
+ else
+ {
+ return ((MarshalledValue) mv).get();
+ }
+ }
+ catch (IOException e)
+ {
+ log_.error("IOException occurred unmarshalling value ", e);
+ return null;
+ }
+ catch (ClassNotFoundException e)
+ {
+ log_.error("ClassNotFoundException occurred unmarshalling value ", e);
+ return null;
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(prevTCL);
+ }
+// }
+ }
+
+ private byte[] externalizeSession(ClusteredSession session)
+ {
+ try
+ {
+ // Write the contents of session to a byte array and store that
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ // Use MarshalledValueOutputStream instead of superclass ObjectOutputStream
+ // or else there are problems finding classes with scoped loaders
+
+ // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
+ // to allow for switching between JBossSerialization and JavaSerialization using
+ // system property -D=session.serialization.jboss=true / false
+
+ // MarshalledValueOutputStream oos = new MarshalledValueOutputStream(baos);
+
+ ObjectOutputStream oos = SessionSerializationFactory.createObjectOutputStream(baos);
+ session.writeExternal(oos);
+ oos.close(); // flushes bytes to baos
+
+ byte[] bytes = baos.toByteArray();
+
+ if (log_.isTraceEnabled())
+ {
+ log_.trace("Serialized Object to size " + bytes.length + " bytes");
+ }
+
+ return bytes;
+ }
+ catch (Exception e)
+ {
+ log_.error("externalizeSession(): exception occurred externalizing session " + session, e);
+ return null;
+ }
+
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,381 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.Node;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.lock.TimeoutException;
+import org.jboss.cache.pojo.PojoCache;
+
+public class JBossCacheWrapper
+{
+ static final Option GRAVITATE_OPTION = new Option();
+ static final Option LOCAL_OPTION = new Option();
+ static
+ {
+ GRAVITATE_OPTION.setForceDataGravitation(true);
+ LOCAL_OPTION.setCacheModeLocal(true);
+ }
+
+ private static final int RETRY = 3;
+ private PojoCache pojoCache_;
+ private Cache plainCache_;
+
+ JBossCacheWrapper(PojoCache cache)
+ {
+ pojoCache_ = cache;
+ plainCache_ = pojoCache_.getCache();
+ }
+
+ /**
+ * Wrapper to embed retyr logic.
+ *
+ * @param fqn
+ * @param id
+ * @return
+ */
+ Object get(Fqn fqn, String id)
+ {
+ return get(fqn, id, false);
+ }
+
+ /**
+ * Wrapper to embed retyr logic.
+ *
+ * @param fqn
+ * @param id
+ * @return
+ */
+ Object get(Fqn fqn, String id, boolean gravitate)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ InvocationContext ctx = null;
+ Option existing = null;
+ try
+ {
+
+ Object value = null;
+ if (gravitate)
+ {
+ ctx = plainCache_.getInvocationContext();
+ existing = ctx.getOptionOverrides();
+ ctx.setOptionOverrides(GRAVITATE_OPTION);
+ value = plainCache_.get(fqn, id);
+ }
+ else
+ {
+ value = plainCache_.get(fqn, id);
+ }
+ return value;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ if (e instanceof RuntimeException)
+ throw (RuntimeException) e;
+ throw new RuntimeException("JBossCacheService: exception occurred in cache get ... ", e);
+ }
+ finally
+ {
+ if (ctx != null)
+ ctx.setOptionOverrides(existing);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache get after retry ... ", ex);
+ }
+
+ /**
+ * Wrapper to embed retry logic.
+ *
+ * @param fqn
+ * @param id
+ * @param value
+ * @return
+ */
+ void put(Fqn fqn, String id, Object value)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ plainCache_.put(fqn, id, value);
+ return;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache put ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache put after retry ... ", ex);
+ }
+
+
+ /**
+ * Wrapper to embed retry logic.
+ *
+ * @param fqn
+ * @param map
+ */
+ void put(Fqn fqn, Map map)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ plainCache_.put(fqn, map);
+ return;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache put ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache put after retry ... ", ex);
+ }
+
+ /**
+ * Wrapper to embed retyr logic.
+ *
+ * @param fqn
+ * @param id
+ * @return
+ */
+ Object remove(Fqn fqn, String id)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ return plainCache_.remove(fqn, id);
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache remove ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache remove after retry ... ", ex);
+ }
+
+ /**
+ * Wrapper to embed retry logic.
+ *
+ * @param fqn
+ */
+ void remove(Fqn fqn)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ plainCache_.removeNode(fqn);
+ return;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache remove ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache remove after retry ... ", ex);
+ }
+
+ /**
+ * Wrapper to embed retry logic.
+ *
+ * @param fqn
+ */
+ void removeLocal(Fqn fqn)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ InvocationContext ctx = plainCache_.getInvocationContext();
+ Option existing = ctx.getOptionOverrides();
+ try
+ {
+ ctx.setOptionOverrides(LOCAL_OPTION);
+ plainCache_.removeNode(fqn);
+ return;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache removeLocal ... ", e);
+ }
+ finally
+ {
+ ctx.setOptionOverrides(existing);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache removeLocal after retry ... ", ex);
+ }
+
+ /**
+ * Wrapper to embed retry logic.
+ *
+ * @param fqn
+ */
+ void evict(Fqn fqn)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ plainCache_.evict(fqn, false);
+ return;
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache evict ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache evict after retry ... ", ex);
+ }
+
+ void evictSubtree(Fqn fqn)
+ {
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+// // Evict the node itself first, since if it stores a Pojo
+// // that will do everything
+// plainCache_.evict(fqn);
+//
+// // next do a depth first removal; this ensure all nodes
+// // are removed, not just their data map
+// Set children = plainCache_.getChildrenNames(fqn);
+// if (children != null)
+// {
+// for (Iterator it = children.iterator(); it.hasNext(); )
+// {
+// Fqn child = new Fqn(fqn, it.next());
+// plainCache_.evict(child);
+// }
+//
+// plainCache_.evict(fqn);
+// }
+// return;
+ plainCache_.evict(fqn, true);
+
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache evict ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache evictSubtree after retry ... ", ex);
+ }
+
+ void removeLocalSubtree(Fqn fqn)
+ {
+ // First remove the node itself. If it is the root of an AOP
+ // object tree, this will do everything
+ removeLocal(fqn);
+
+ // Next, clear any children
+ Exception ex = null;
+ for (int i = 0; i < RETRY; i++)
+ {
+ try
+ {
+ // Evict the node itself first, since if it stores a Pojo
+ // that will do everything
+ removeLocal(fqn);
+
+ // next do a depth first removal; this ensures all nodes
+ // are removed, not just their data map
+ Node base = plainCache_.getRoot().getChild(fqn);
+ if (base != null)
+ {
+ Set children = base.getChildren();
+ if (children != null)
+ {
+ for (Iterator it = children.iterator(); it.hasNext(); )
+ {
+ Node child = (Node) it.next();
+ removeLocalSubtree(child.getFqn());
+ }
+
+ removeLocal(fqn);
+ }
+ }
+ return;
+
+ }
+ catch (TimeoutException e)
+ {
+ ex = e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheService: exception occurred in cache removeLocal ... ", e);
+ }
+ }
+ throw new RuntimeException("JBossCacheService: exception occurred in cache removeLocalSubtree after retry ... ", ex);
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,950 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Session;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.WebMetaData;
+import org.jboss.metadata.web.ReplicationConfig;
+import org.jboss.metadata.web.PassivationConfig;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.web.tomcat.statistics.ReplicationStatistics;
+
+
+/**
+ * Base abstract implementation of Tomcat manager without the concept of
+ * session operations, e.g., add, remove, etc.
+ *
+ * @author Ben Wang
+ * @author Hany Mesha
+ * @version $Revision: 60686 $
+ */
+public abstract class JBossManager
+ implements AbstractJBossManager, Lifecycle,
+ JBossManagerMBean, PropertyChangeListener
+{
+ // -- Constants ----------------------------------------
+ /**
+ * Informational name for this Catalina component
+ */
+ private static final String info_ = "JBossManager/1.0";
+
+ // -- Class attributes ---------------------------------
+ protected ReplicationStatistics stats_ = new ReplicationStatistics();
+
+ /**
+ * Policy to determine if a session is dirty
+ */
+ protected InvalidateSessionPolicy invalidateSessionPolicy_ = InvalidateSessionPolicy.SET_AND_NON_PRIMITIVE_GET;
+ /**
+ * Replication granulairty.
+ */
+ protected ReplicationGranularity replicationGranularity_ = ReplicationGranularity.SESSION;
+
+ /**
+ * Session passivation flag set in jboss-web.xml by the user.
+ * If true, then the session passivation is enabled for this web application,
+ * otherwise, it's disabled
+ */
+ protected boolean passivationMode_ = false;
+
+ /**
+ * Min time (seconds) the session must be idle since lastAccesstime before it's eligable for passivation
+ * This overrides maxActive_, to prevent thrashing if the there are lots of active sessions.
+ * Setting to -1 means it's ignored
+ */
+ protected int passivationMinIdleTime_ = -1;
+
+ /**
+ * Max time (seconds) the session must be idle since lastAccesstime before it's eligable for passivation
+ * This overrides maxActive_, to prevent thrashing if the there are lots of active sessions.
+ * Setting to -1 means session should not be forced out.
+ */
+ protected int passivationMaxIdleTime_ = -1;
+
+ /**
+ * The lifecycle_ event support for this component.
+ */
+ protected LifecycleSupport lifecycle_ = new LifecycleSupport(this);
+ /**
+ * Has this component been started_ yet?
+ */
+ protected boolean started_ = false;
+ /**
+ * The objectname this Manager is associated with
+ */
+ protected ObjectName objectName_;
+ /**
+ * The Log-object for this class
+ */
+ protected Logger log_ = Logger.getLogger(this.getClass().getName());
+ /**
+ * The Container with which this Manager is associated.
+ */
+ protected Container container_;
+ /**
+ /**
+ * The distributable flag for Sessions created by this Manager. If this
+ * flag is set to <code>true</code>, any user attributes added to a
+ * session controlled by this Manager must be Serializable.
+ */
+ protected boolean distributable_ = true;
+ /**
+ * The default maximum inactive interval for Sessions created by
+ * this Manager.
+ */
+ protected int maxInactiveInterval_ = 60;
+ /**
+ * The session id length of Sessions created by this Manager.
+ */
+ protected int sessionIdLength_ = 16;
+
+ // Maximum of ative sessions allowed. -1 is unlimited.
+ protected int maxActive_ = -1;
+
+ // Number of sessions created by this manager
+ protected int createdCounter_ = 0;
+
+ // number of rejected sessions because the number active sessions exceeds maxActive
+ protected int rejectedCounter_ = 0;
+
+ // Number of active sessions
+ protected int activeCounter_ = 0;
+
+ // Maximum number of active sessions seen so far
+ protected int maxActiveCounter_ = 0;
+
+ // number of expired session ids. Not sure what exactly does it mean in our clustered case.
+ protected int expiredCounter_ = 0;
+
+ protected long timeSinceLastReset_ = 0;
+
+ // Cumulative time spent in backgroundProcess
+ protected long processingTime_ = 0;
+
+ /**
+ * Map<String,ClusteredSession>. Store the local sessions.
+ */
+ protected final Map sessions_ = new ConcurrentHashMap();
+
+ /**
+ * If set to true, it will not replicate the access time stamp unless attributes are dirty.
+ */
+ protected boolean useLocalCache_ = false;
+
+ /**
+ * The property change support for this component.
+ */
+ protected PropertyChangeSupport support_ = new PropertyChangeSupport(this);
+
+ protected SessionIDGenerator sessionIDGenerator_;
+
+ protected String jvmRoute_;
+
+ // TODO Need a string manager to handle exception localization
+
+ public JBossManager()
+ {
+ sessionIDGenerator_ = SessionIDGenerator.getInstance();
+
+ }
+
+ public void init(String name, WebMetaData webMetaData, boolean useJK, boolean useLocalCache)
+ throws ClusteringNotSupportedException
+ {
+ ReplicationConfig rpc = webMetaData.getReplicationConfig();
+ if (rpc != null)
+ {
+ replicationGranularity_ = ReplicationGranularity.fromString(rpc.getGranularity());
+ invalidateSessionPolicy_ = InvalidateSessionPolicy.fromString(rpc.getTrigger());
+ }
+ maxActive_ = webMetaData.getMaxActiveSessionsAllowed();
+ PassivationConfig pConfig = webMetaData.getPassivationConfig();
+ if (pConfig != null)
+ {
+ passivationMode_ = Boolean.valueOf(pConfig.getUseSessionPassivation()).booleanValue();
+ if (passivationMode_)
+ {
+ passivationMinIdleTime_ = Integer.valueOf(pConfig.getPassivationMinIdleTime()).intValue();
+ passivationMaxIdleTime_ = Integer.valueOf(pConfig.getPassivationMaxIdleTime()).intValue();
+ }
+ }
+
+ useLocalCache_ = useLocalCache;
+ log_.debug("init(): replicationGranularity_ is " + replicationGranularity_ +
+ " and invalidateSessionPolicy is " + invalidateSessionPolicy_ +
+ " and maxActiveSessions allowed is " + maxActive_ +
+ " and passivationMode is " + passivationMode_);
+
+ try
+ {
+ // Give this manager a name
+ objectName_ = new ObjectName("jboss.web:service=ClusterManager,WebModule=" + name);
+ }
+ catch (Throwable e)
+ {
+ log_.error("Could not create ObjectName", e);
+ throw new ClusteringNotSupportedException(e.toString());
+ }
+ }
+
+ public InvalidateSessionPolicy getInvalidateSessionPolicy()
+ {
+ return this.invalidateSessionPolicy_;
+ }
+
+ /**
+ * Retrieve the enclosing Engine for this Manager.
+ *
+ * @return an Engine object (or null).
+ */
+ public Engine getEngine()
+ {
+ Engine e = null;
+ for (Container c = getContainer(); e == null && c != null; c = c.getParent())
+ {
+ if (c != null && c instanceof Engine)
+ {
+ e = (Engine) c;
+ }
+ }
+ return e;
+ }
+
+ /**
+ * Retrieve the JvmRoute for the enclosing Engine.
+ *
+ * @return the JvmRoute or null.
+ */
+ public String getJvmRoute()
+ {
+ if (jvmRoute_ == null)
+ {
+ Engine e = getEngine();
+ jvmRoute_= (e == null ? null : e.getJvmRoute());
+ }
+ return jvmRoute_;
+ }
+
+ /**
+ * Get a new session-id from the distributed store
+ *
+ * @return new session-id
+ */
+ protected String getNextId()
+ {
+ return sessionIDGenerator_.getSessionId();
+ }
+
+ /**
+ * Gets the JMX <code>ObjectName</code> under
+ * which our <code>TreeCache</code> is registered.
+ */
+ public ObjectName getObjectName()
+ {
+ return objectName_;
+ }
+
+ public boolean isUseLocalCache()
+ {
+ return useLocalCache_;
+ }
+
+ /**
+ * Sets a new cookie for the given session id and response
+ *
+ * @param sessionId The session id
+ */
+ public void setSessionCookie(String sessionId)
+ {
+ HttpServletResponse response = SessionReplicationContext.getOriginalResponse();
+ setNewSessionCookie(sessionId, response);
+ }
+
+ public void setNewSessionCookie(String sessionId, HttpServletResponse response)
+ {
+ if (response != null)
+ {
+ Context context = (Context) container_;
+ Connector connector = ((Response)response).getConnector();
+ if (context.getCookies())
+ {
+ // set a new session cookie
+ Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME, sessionId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Setting cookie with session id:" + sessionId + " & name:" + Globals.SESSION_COOKIE_NAME);
+ }
+
+ String contextPath = null;
+ if (!connector.getEmptySessionPath() && (context != null)) {
+ contextPath = context.getEncodedPath();
+ }
+
+ if ((contextPath != null) && (contextPath.length() > 0)) {
+ newCookie.setPath(contextPath);
+ } else {
+ newCookie.setPath("/");
+ }
+
+ if (connector.getSecure()) {
+ newCookie.setSecure(true);
+ }
+
+ response.addCookie(newCookie);
+ }
+ }
+ }
+
+ // JBossManagerMBean-methods -------------------------------------
+
+ // A better property name for the MBean API
+ public int getMaxActiveAllowed()
+ {
+ return getMaxActive();
+ }
+
+ // A better property name for the MBean API
+ public void setMaxActiveAllowed(int maxActive)
+ {
+ setMaxActive(maxActive);
+ }
+
+ public long getMaxActiveSessionCount()
+ {
+ return this.maxActiveCounter_;
+ }
+
+ public ReplicationStatistics getReplicationStatistics()
+ {
+ return stats_;
+ }
+
+ public void resetStats()
+ {
+ stats_.resetStats();
+ activeCounter_ = 0;
+ maxActiveCounter_ = 0;
+ rejectedCounter_ = 0;
+ createdCounter_ = 0;
+ expiredCounter_ = 0;
+ processingTime_ = 0;
+ timeSinceLastReset_ = System.currentTimeMillis();
+ }
+
+ public long timeInSecondsSinceLastReset()
+ {
+ return (System.currentTimeMillis() - timeSinceLastReset_) / (1000L);
+ }
+
+ public long getActiveSessionCount()
+ {
+ return getActiveSessions();
+ }
+
+ public long getCreatedSessionCount()
+ {
+ return createdCounter_;
+ }
+
+ public long getExpiredSessionCount()
+ {
+ return expiredCounter_;
+ }
+
+ public long getRejectedSessionCount()
+ {
+ return rejectedCounter_;
+ }
+
+ public int getSessionMaxAliveTime()
+ {
+ return 0;
+ }
+
+ public void setSessionMaxAliveTime(int sessionMaxAliveTime)
+ {
+ }
+
+ public int getSessionAverageAliveTime()
+ {
+ return 0;
+ }
+
+ public void setSessionAverageAliveTime(int sessionAverageAliveTime)
+ {
+ }
+
+ public String reportReplicationStatistics()
+ {
+ StringBuffer tmp = new StringBuffer();
+ HashMap copy = new HashMap(stats_.getStats());
+ Iterator iter = copy.entrySet().iterator();
+ tmp.append("<table><tr>");
+ tmp.append("<th>sessionID</th>");
+ tmp.append("<th>replicationCount</th>");
+ tmp.append("<th>minPassivationTime</th>");
+ tmp.append("<th>maxPassivationTime</th>");
+ tmp.append("<th>totalPassivationTime</th>");
+ tmp.append("<th>minReplicationTime</th>");
+ tmp.append("<th>maxReplicationTime</th>");
+ tmp.append("<th>totalReplicationlTime</th>");
+ tmp.append("<th>loadCount</th>");
+ tmp.append("<th>minLoadTime</th>");
+ tmp.append("<th>maxLoadTime</th>");
+ tmp.append("<th>totalLoadTime</th>");
+ while (iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ ReplicationStatistics.TimeStatistic stat = (ReplicationStatistics.TimeStatistic) entry.getValue();
+ if (stat != null)
+ {
+ tmp.append("<tr><td>");
+ tmp.append(entry.getKey());
+ tmp.append("</td><td>");
+ tmp.append(stat.replicationCount);
+ tmp.append("</td><td>");
+ tmp.append(stat.minPassivationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.maxPassivationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.totalPassivationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.minReplicationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.maxReplicationTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.totalReplicationlTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.loadCount);
+ tmp.append("</td><td>");
+ tmp.append(stat.minLoadTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.maxLoadTime);
+ tmp.append("</td><td>");
+ tmp.append(stat.totalLoadlTime);
+ tmp.append("</td></tr>");
+ }
+ }
+ tmp.append("</table>");
+ copy.clear();
+ return tmp.toString();
+
+ }
+
+ public String reportReplicationStatisticsCSV()
+ {
+ StringBuffer tmp = createCSVHeader();
+ HashMap copy = new HashMap(stats_.getStats());
+ Iterator iter = copy.entrySet().iterator();
+ while (iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ ReplicationStatistics.TimeStatistic stat = (ReplicationStatistics.TimeStatistic) entry.getValue();
+ if (stat != null)
+ {
+ tmp.append("\n");
+ tmp.append(entry.getKey());
+ tmp.append(",");
+ tmp.append(stat.replicationCount);
+ tmp.append(",");
+ tmp.append(stat.minPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.maxPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.totalPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.minReplicationTime);
+ tmp.append(",");
+ tmp.append(stat.maxReplicationTime);
+ tmp.append(",");
+ tmp.append(stat.totalReplicationlTime);
+ tmp.append(",");
+ tmp.append(stat.loadCount);
+ tmp.append(",");
+ tmp.append(stat.minLoadTime);
+ tmp.append(",");
+ tmp.append(stat.maxLoadTime);
+ tmp.append(",");
+ tmp.append(stat.totalLoadlTime);
+ }
+ }
+ copy.clear();
+ return tmp.toString();
+
+ }
+
+ public String reportReplicationStatisticsCSV(String sessionId)
+ {
+ StringBuffer tmp = createCSVHeader();
+ Map stats = stats_.getStats();
+ ReplicationStatistics.TimeStatistic stat =
+ (ReplicationStatistics.TimeStatistic) stats.get(sessionId);
+ if (stat != null)
+ {
+ tmp.append("\n");
+ tmp.append(sessionId);
+ tmp.append(",");
+ tmp.append(stat.replicationCount);
+ tmp.append(",");
+ tmp.append(stat.minPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.maxPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.totalPassivationTime);
+ tmp.append(",");
+ tmp.append(stat.minReplicationTime);
+ tmp.append(",");
+ tmp.append(stat.maxReplicationTime);
+ tmp.append(",");
+ tmp.append(stat.totalReplicationlTime);
+ tmp.append(",");
+ tmp.append(stat.loadCount);
+ tmp.append(",");
+ tmp.append(stat.minLoadTime);
+ tmp.append(",");
+ tmp.append(stat.maxLoadTime);
+ tmp.append(",");
+ tmp.append(stat.totalLoadlTime);
+ }
+ return tmp.toString();
+ }
+
+ private StringBuffer createCSVHeader()
+ {
+ StringBuffer tmp = new StringBuffer();
+ tmp.append("sessionID,");
+ tmp.append("replicationCount,");
+ tmp.append("minPassivationTime,");
+ tmp.append("maxPassivationTime,");
+ tmp.append("totalPassivationTime,");
+ tmp.append("minReplicationTime,");
+ tmp.append("maxReplicationTime,");
+ tmp.append("totalReplicationlTime,");
+ tmp.append("loadCount,");
+ tmp.append("minLoadTime,");
+ tmp.append("maxLoadTime,");
+ tmp.append("totalLoadTime");
+
+ return tmp;
+ }
+
+ // Lifecycle-methods -------------------------------------
+
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle_.addLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return lifecycle_.findLifecycleListeners();
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle_.removeLifecycleListener(listener);
+ }
+
+ /**
+ * Start this Manager
+ *
+ * @throws org.apache.catalina.LifecycleException
+ *
+ */
+ public void start() throws LifecycleException
+ {
+ startManager();
+ }
+
+ /**
+ * Stop this Manager
+ *
+ * @throws org.apache.catalina.LifecycleException
+ *
+ */
+ public void stop() throws LifecycleException
+ {
+ resetStats();
+ stopManager();
+ }
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after <code>configure()</code>,
+ * and before any of the public methods of the component are utilized.
+ *
+ * @throws IllegalStateException if this component has already been
+ * started_
+ * @throws org.apache.catalina.LifecycleException
+ * if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ protected void startManager() throws LifecycleException
+ {
+ log_.info("Starting JBossManager");
+
+ // Validate and update our current component state
+ if (started_)
+ throw new LifecycleException
+ ("JBossManager alreadyStarted");
+ lifecycle_.fireLifecycleEvent(START_EVENT, null);
+ started_ = true;
+
+ // register ClusterManagerMBean to the MBeanServer
+ try
+ {
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ server.registerMBean(this, objectName_);
+ }
+ catch (Exception e)
+ {
+ log_.error("Could not register ClusterManagerMBean to MBeanServer", e);
+ }
+ }
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component.
+ *
+ * @throws IllegalStateException if this component has not been started_
+ * @throws org.apache.catalina.LifecycleException
+ * if this component detects a fatal error
+ * that needs to be reported
+ */
+ protected void stopManager() throws LifecycleException
+ {
+ log_.info("Stopping JBossManager");
+
+ // Validate and update our current component state
+ if (!started_)
+ throw new LifecycleException
+ ("JBossManager notStarted");
+ lifecycle_.fireLifecycleEvent(STOP_EVENT, null);
+ started_ = false;
+
+ // unregister ClusterManagerMBean from the MBeanServer
+ try
+ {
+ MBeanServer server = MBeanServerLocator.locateJBoss();
+ server.unregisterMBean(objectName_);
+ }
+ catch (Exception e)
+ {
+ log_.error("Could not unregister ClusterManagerMBean from MBeanServer", e);
+ }
+ }
+
+ // Manager-methods -------------------------------------
+ public Container getContainer()
+ {
+ return container_;
+ }
+
+ public void setContainer(Container container)
+ {
+
+ // De-register from the old Container (if any)
+ if ((this.container_ != null) && (this.container_ instanceof Context))
+ this.container_.removePropertyChangeListener(this);
+
+ // Default processing provided by our superclass
+ this.container_ = container;
+
+ // Register with the new Container (if any)
+ if ((this.container_ != null) && (this.container_ instanceof Context))
+ {
+ setMaxInactiveInterval
+ (((Context) this.container_).getSessionTimeout() * 60);
+ this.container_.addPropertyChangeListener(this);
+ }
+ }
+
+ public boolean getDistributable()
+ {
+ return distributable_;
+ }
+
+ public void setDistributable(boolean distributable)
+ {
+ this.distributable_ = distributable;
+ }
+
+ public String getInfo()
+ {
+ return info_;
+ }
+
+ public int getMaxInactiveInterval()
+ {
+ return maxInactiveInterval_;
+ }
+
+ public void setMaxInactiveInterval(int interval)
+ {
+ this.maxInactiveInterval_ = interval;
+ }
+
+ public int getSessionIdLength()
+ {
+ return sessionIdLength_;
+ }
+
+ public void setSessionIdLength(int idLength)
+ {
+ this.sessionIdLength_ = idLength;
+ }
+
+ public int getSessionCounter()
+ {
+ return createdCounter_;
+ }
+
+ public void setSessionCounter(int sessionCounter)
+ {
+ this.createdCounter_ = sessionCounter;
+ }
+
+ public int getMaxActive()
+ {
+ return maxActive_;
+ }
+
+ public void setMaxActive(int maxActive)
+ {
+ this.maxActive_ = maxActive;
+ }
+
+ public int getExpiredSessions()
+ {
+ return expiredCounter_;
+ }
+
+ public void setExpiredSessions(int expiredSessions)
+ {
+ this.expiredCounter_ = expiredSessions;
+ }
+
+ public int getRejectedSessions()
+ {
+ return rejectedCounter_;
+ }
+
+ public void setRejectedSessions(int rejectedSessions)
+ {
+ this.rejectedCounter_ = rejectedSessions;
+ }
+
+ public long getProcessingTime()
+ {
+ return this.processingTime_;
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ support_.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Remove the active session locally from the manager without replicating to the cluster. This can be
+ * useful when the session is exipred, for example, where there is not need to propagate the expiration.
+ *
+ * @param session
+ */
+ public abstract void removeLocal(Session session);
+
+ /**
+ * Store the modified session.
+ *
+ * @param session
+ */
+ public abstract boolean storeSession(Session session);
+
+ public int getActiveSessions()
+ {
+ return activeCounter_;
+ }
+
+/*
+ public void add(Session session)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Session createEmptySession()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Session createSession()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Session findSession(String id) throws IOException
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Session[] findSessions()
+ {
+ return new Session[0]; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void remove(Session session)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+*/
+
+ public void load() throws ClassNotFoundException, IOException
+ {
+ // TODO. Implement persistence layer.
+ throw new RuntimeException("JBossManager.load(): Method not implemented.");
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ support_.removePropertyChangeListener(listener);
+ }
+
+ public void unload() throws IOException
+ {
+ // TODO. Implement persistence layer.
+ throw new RuntimeException("JBossManager.load(): Method not implemented.");
+ }
+
+
+ public void backgroundProcess()
+ {
+ // Called from Catalina StandardEngine for every 60 seconds.
+
+ long start = System.currentTimeMillis();
+
+ processExpires();
+
+ long elapsed = System.currentTimeMillis() - start;
+
+ processingTime_ += elapsed;
+ }
+
+ /**
+ * Go through all sessions and look if they have expired
+ */
+ protected void processExpires()
+ {
+ // What's the time?
+// long timeNow = System.currentTimeMillis();
+
+ // Get all sessions
+ Session sessions[] = findSessions();
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("Looking for sessions that have expired ...");
+ }
+
+ for (int i = 0; i < sessions.length; ++i)
+ {
+ ClusteredSession session = (ClusteredSession) sessions[i];
+
+ // We only look at valid sessions. This will remove session if not valid already.
+ if (!session.isValid())
+ {
+ continue;
+ }
+
+ /* I don't think it is right to check idle time based on lastAccessedTime since it may
+ // remove some request that is currently in progress!!!
+ // How long are they allowed to be idle?
+ int maxInactiveInterval = session.getMaxInactiveInterval();
+
+ // Negative values = never expire
+ if( maxInactiveInterval < 0 )
+ {
+ continue;
+ }
+
+ // How long has this session been idle?
+ int timeIdle =
+ (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
+
+ // Too long?
+ if( timeIdle >= maxInactiveInterval )
+ {
+ try
+ {
+ log_.debug("Session with id = " + session.getId() + " has expired on local node");
+ remove(session);
+ }
+ catch(Throwable t)
+ {
+ log_.error("Problems while expiring session with id = " + session.getId(), t);
+ }
+ }
+ */
+ }
+ }
+
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // TODO Need to handle it here.
+ }
+
+ /**
+ * Find in-memory sessions, if any.
+ * @return local session found. Sessions of size 0, if not found.
+ */
+ abstract public ClusteredSession[] findLocalSessions();
+
+ /**
+ * Find in-memory sessions, if any.
+ * @param realId the Session id without JvmRoute tag.
+ * @return local session found. Null if not found.
+ */
+ abstract public ClusteredSession findLocalSession(String realId);
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,159 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.web.tomcat.statistics.ReplicationStatistics;
+
+/**
+ * The MBean-interface for the JBossManager
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 45726 $
+ */
+public interface JBossManagerMBean
+{
+ /**
+ * Gets the replication statistics for the sessions managed
+ * by this manager.
+ *
+ * @return the statistics
+ */
+ ReplicationStatistics getReplicationStatistics();
+
+ /**
+ * Resets all statistics.
+ */
+ void resetStats();
+
+ /**
+ * Gets the elapsed time since this manager was instantiated or the
+ * last call to resetStats()
+ */
+ long timeInSecondsSinceLastReset();
+
+ /**
+ * Gets the number of sessions active on this node. Does not include
+ * replicated sessions that have not been accessed on this node.
+ */
+ long getActiveSessionCount();
+
+ /**
+ * Gets the number of times session creation has failed because the
+ * number of active sessions exceeds
+ * {@link #getMaxActiveAllowed() maxActiveAllowed}
+ */
+ long getRejectedSessionCount();
+
+ /**
+ * Gets the number of sessions created on this node. Does not include
+ * sessions initially created on other nodes, even if those sessions
+ * were accessed on this node.
+ */
+ long getCreatedSessionCount();
+
+ /**
+ * Gets the number of sessions that have been expired on this node.
+ */
+ long getExpiredSessionCount();
+
+ /**
+ * Gets the highest number of sessions concurrently active on this node.
+ * Does not include replicated sessions that have not been accessed on
+ * this node.
+ */
+ long getMaxActiveSessionCount();
+
+ /**
+ * Gets the maximum number of active sessions that will concurrently be
+ * allowed on this node. Does not include replicated sessions that have
+ * not been accessed on this node.
+ */
+ int getMaxActiveAllowed();
+
+ /**
+ * Sets the maximum number of active sessions that will concurrently be
+ * allowed on this node. Does not include replicated sessions that have
+ * not been accessed on this node.
+ *
+ * <p>
+ * Note that if sessions fail over to this node from other nodes, the max
+ * number of active sessions may exceed this value.
+ * </p>
+ *
+ * @param max the max number of sessions, or <code>-1</code> if there is
+ * no limit.
+ */
+ void setMaxActiveAllowed(int max);
+
+ /**
+ * Gets the maximum time interval, in seconds, between client requests
+ * after which sessions created by this manager should be expired. A
+ * negative time indicates that the session should never time out.
+ */
+ int getMaxInactiveInterval();
+
+ /**
+ * Sets the maximum time interval, in seconds, between client requests
+ * after which sessions created by this manager should be expired. A
+ * negative time indicates that the session should never time out.
+ *
+ * @param interval The new maximum interval
+ */
+ void setMaxInactiveInterval(int minutes);
+
+ /**
+ * Gets whether this manager's sessions are distributable.
+ */
+ boolean getDistributable();
+
+ /**
+ * Gets the cumulative number of milliseconds spent in the
+ * <code>Manager.backgroundProcess()</code> method.
+ */
+ long getProcessingTime();
+
+ /**
+ * Outputs the replication statistics as an HTML table, with one row
+ * per session.
+ */
+ String reportReplicationStatistics();
+
+ /**
+ * Outputs the replication statistics as a comma-separated-values, with one
+ * row per session. First row is a header listing field names.
+ */
+ String reportReplicationStatisticsCSV();
+
+ /**
+ * Outputs the replication statistics for the given session as a set of
+ * comma-separated-values. First row is a header listing field names.
+ */
+ String reportReplicationStatisticsCSV(String sessionId);
+
+ /**
+ * Gets the number of characters used in creating a session id. Excludes
+ * any jvmRoute.
+ */
+ int getSessionIdLength();
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,237 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.*;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+import org.jboss.logging.Logger;
+
+/**
+ * Web request valve to specifically handle Tomcat jvmRoute using mod_jk(2)
+ * module. We assume that the session is set by cookie only for now, i.e., no
+ * support of that from URL. Furthermore, the session id has a format of
+ * id.jvmRoute where jvmRoute is used by JK module to determine sticky session
+ * during load balancing.
+ *
+ * @author Ben Wang
+ * @version $Revision: 58922 $
+ */
+public class JvmRouteValve extends ValveBase implements Lifecycle
+{
+ // The info string for this Valve
+ private static final String info = "JvmRouteValve/1.0";
+
+ protected static Logger log_ = Logger.getLogger(JvmRouteValve.class);
+
+ // Valve-lifecycle_ helper object
+ protected LifecycleSupport support = new LifecycleSupport(this);
+
+ protected AbstractJBossManager manager_;
+
+ /**
+ * Create a new Valve.
+ *
+ */
+ public JvmRouteValve(AbstractJBossManager manager)
+ {
+ super();
+ manager_ = manager;
+ }
+
+ /**
+ * Get information about this Valve.
+ */
+ public String getInfo()
+ {
+ return info;
+ }
+
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+
+ // Need to check it before let it through. This is ok because this
+ // valve is inserted only when mod_jk option is configured.
+ checkJvmRoute(request, response);
+
+ // let the servlet invokation go through
+ getNext().invoke(request, response);
+ }
+
+ public void checkJvmRoute(Request req, Response res)
+ throws IOException, ServletException
+ {
+ String oldsessionId = req.getRequestedSessionId();
+ HttpSession session = req.getSession(false);
+ if (session != null)
+ {
+ String sessionId = session.getId();
+
+ // Obtain JvmRoute
+ String jvmRoute = manager_.getJvmRoute();
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("checkJvmRoute(): check if need to re-route based on JvmRoute. Session id: " +
+ sessionId + " jvmRoute: " + jvmRoute);
+ }
+
+ if (jvmRoute == null)
+ {
+ throw new RuntimeException("JvmRouteValve.checkJvmRoute(): Tomcat JvmRoute is null. " +
+ "Need to assign a value in Tomcat server.xml for load balancing.");
+ }
+
+ // Check if incoming session id has JvmRoute appended. If not, append it.
+ boolean setCookie = !req.isRequestedSessionIdFromURL();
+ handleJvmRoute(oldsessionId, sessionId, jvmRoute, res, setCookie);
+ }
+ }
+
+ protected void handleJvmRoute(String oldsessionId,
+ String sessionId,
+ String jvmRoute,
+ HttpServletResponse response,
+ boolean setCookie)
+ {
+ // Get requested jvmRoute.
+ // TODO. The current format is assumed to be id.jvmRoute. Can be generalized later.
+ String receivedJvmRoute = null;
+ int index = oldsessionId.lastIndexOf(".");
+ if (index > 0)
+ {
+ receivedJvmRoute = oldsessionId.substring(index + 1, sessionId.length());
+ }
+
+ String requestedJvmRoute = null;
+ index = sessionId.lastIndexOf(".");
+ if (index > 0)
+ {
+ requestedJvmRoute = sessionId.substring(index + 1, sessionId.length());
+ }
+
+ String newId = null;
+ if (!jvmRoute.equals(requestedJvmRoute))
+ {
+ if (requestedJvmRoute == null)
+ {
+ // If this valve is turned on, we assume we have an appendix of jvmRoute.
+ // So this request is new.
+ newId = sessionId + "." + jvmRoute;
+ }
+ else
+ {
+ // We just had a failover since jvmRoute does not match.
+ // We will replace the old one with the new one.
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): We have detected a failover with different jvmRoute." +
+ " old one: " + requestedJvmRoute + " new one: " + jvmRoute + ". Will reset the session id.");
+ }
+
+ String base = sessionId.substring(0, index);
+ newId = base + "." + jvmRoute;
+ }
+
+ resetSessionId(sessionId, newId);
+
+ }
+ /* Also check the jvmRoute received (via req.getRequestedSessionId()) */
+ if (!jvmRoute.equals(receivedJvmRoute))
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("handleJvmRoute(): We have detected a failover with different jvmRoute." +
+ " received one: " + receivedJvmRoute + " new one: " + jvmRoute + ". Will resent the session id.");
+ }
+ String base = sessionId.substring(0, index);
+ newId = base + "." + jvmRoute;
+ }
+
+ /* Change the sessionid cookie if needed */
+ if (setCookie && newId != null)
+ manager_.setNewSessionCookie(newId, response);
+ }
+
+ private void resetSessionId(String oldId, String newId)
+ {
+ try
+ {
+ ClusteredSession session = (ClusteredSession)manager_.findSession(oldId);
+ // change session id with the new one using local jvmRoute.
+ if( session != null )
+ {
+ // Note this will trigger a session remove from the super Tomcat class.
+ session.resetIdWithRouteInfo(newId);
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("resetSessionId(): changed catalina session to= [" + newId + "] old one= [" + oldId + "]");
+ }
+ }
+ else if (log_.isDebugEnabled())
+ {
+ log_.debug("resetSessionId(): no session with id " + newId + " found");
+ }
+ }
+ catch (IOException e)
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("resetSessionId(): manager_.findSession() unable to find session= [" + oldId + "]", e);
+ }
+ throw new RuntimeException("JvmRouteValve.resetSessionId(): cannot find session [" + oldId + "]", e);
+ }
+ }
+
+ // Lifecycle Interface
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ support.addLifecycleListener(listener);
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ support.removeLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return support.findLifecycleListeners();
+ }
+
+ public void start() throws LifecycleException
+ {
+ support.fireLifecycleEvent(START_EVENT, this);
+ }
+
+ public void stop() throws LifecycleException
+ {
+ support.fireLifecycleEvent(STOP_EVENT, this);
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JvmRouteValve.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,15 @@
+clusteredSession.attributeEvent=Session attribute event listener threw exception
+clusteredSession.invalidate.ise=invalidate: Session already invalidated
+clusteredSession.isNew.ise=isNew: Session already invalidated
+clusteredSession.getAttribute.ise=getAttribute: Session already invalidated
+clusteredSession.getAttributeNames.ise=getAttributeNames: Session already invalidated
+clusteredSession.getCreationTime.ise=getCreationTime: Session already invalidated
+clusteredSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: Session already invalidated
+clusteredSession.getValueNames.ise=getAttributeNames: Session already invalidated
+clusteredSession.notSerializable=Cannot serialize session attribute {0} for session {1}
+clusteredSession.removeAttribute.ise=removeAttribute: Session already invalidated
+clusteredSession.sessionEvent=Session event listener threw exception
+clusteredSession.setAttribute.iae=setAttribute: Non-serializable attribute
+clusteredSession.setAttribute.ise=setAttribute: Session already invalidated
+clusteredSession.setAttribute.namenull=setAttribute: name parameter cannot be null
+clusteredSession.sessionCreated=Created Session id = {0}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,15 @@
+clusteredSession.attributeEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u5c5e\u6027\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+clusteredSession.invalidate.ise=invalidate: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.isNew.ise=isNew: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getAttribute.ise=getAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getAttributeNames.ise=getAttributeNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getCreationTime.ise=getCreationTime: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getValueNames.ise=getAttributeNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.notSerializable=\u30bb\u30c3\u30b7\u30e7\u30f3 {1} \u306e\u305f\u3081\u306b\u30bb\u30c3\u30b7\u30e7\u30f3\u5c5e\u6027 {0} \u3092\u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3067\u304d\u307e\u305b\u3093
+clusteredSession.removeAttribute.ise=removeAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.sessionEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+clusteredSession.setAttribute.iae=setAttribute: \u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3067\u304d\u306a\u3044\u5c5e\u6027\u3067\u3059
+clusteredSession.setAttribute.ise=setAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.setAttribute.namenull=setAttribute: name\u30d1\u30e9\u30e1\u30bf\u306fnull\u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+clusteredSession.sessionCreated=\u30bb\u30c3\u30b7\u30e7\u30f3ID = {0} \u3092\u751f\u6210\u3057\u307e\u3057\u305f
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ReplicationGranularity.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ReplicationGranularity.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ReplicationGranularity.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,33 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+public enum ReplicationGranularity
+{
+ SESSION, ATTRIBUTE, FIELD;
+
+ public static ReplicationGranularity fromString(String granularity)
+ {
+ return (granularity == null ? SESSION
+ : Enum.valueOf(ReplicationGranularity.class, granularity));
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ReplicationGranularity.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,204 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Map;
+
+
+/**
+ * Implementation of a clustered session for the JBossCacheManager. The replication granularity
+ * level is session based; that is, we replicate per whole session object.
+ * We use JBossCache for our internal replicated data store.
+ * The internal structure in JBossCache is as follows:
+ * <pre>
+ * /JSESSION
+ * /hostname
+ * /web_app_path (path + session id is unique)
+ * /id Map(id, session)
+ * (VERSION_KEY, version) // Used for version tracking. version is an Integer.
+ * </pre>
+ * <p/>
+ * Note that the isolation level of the cache dictates the
+ * concurrency behavior.</p>
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ *
+ * @version $Revision: 56542 $
+ */
+class SessionBasedClusteredSession
+ extends JBossCacheClusteredSession
+{
+ static final long serialVersionUID = 3200976125245487256L;
+
+ /**
+ * Descriptive information describing this Session implementation.
+ */
+ protected static final String info = "SessionBasedClusteredSession/1.0";
+
+ public SessionBasedClusteredSession(JBossCacheManager manager)
+ {
+ super(manager);
+ }
+
+ // ---------------------------------------------- Overridden Public Methods
+
+ /**
+ * Return a string representation of this object.
+ */
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("SessionBasedClusteredSession[");
+ sb.append(super.toString());
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+ public void removeMyself()
+ {
+ proxy_.removeSession(realId);
+ }
+
+ public void removeMyselfLocal()
+ {
+ proxy_.removeSessionLocal(realId);
+ }
+
+ // ----------------------------------------------HttpSession Public Methods
+
+ /**
+ * Does nothing -- all attributes are populated already
+ */
+ protected void populateAttributes()
+ {
+ // no-op
+ }
+
+ protected Object getJBossInternalAttribute(String name)
+ {
+ Object result = attributes.get(name);
+
+ // Do dirty check even if result is null, as w/ SET_AND_GET null
+ // still makes us dirty (ensures timely replication w/o using ACCESS)
+ if (isGetDirty(result))
+ {
+ sessionAttributesDirty();
+ }
+
+ return result;
+
+ }
+
+ protected Object removeJBossInternalAttribute(String name,
+ boolean localCall,
+ boolean localOnly)
+ {
+ if (localCall)
+ sessionAttributesDirty();
+ return attributes.remove(name);
+ }
+
+ protected Map getJBossInternalAttributes()
+ {
+ return attributes;
+ }
+
+ protected Object setJBossInternalAttribute(String name, Object value)
+ {
+ sessionAttributesDirty();
+ return attributes.put(name, value);
+ }
+
+ /**
+ * Overrides the superclass version by additionally reading the
+ * attributes map.
+ *
+ * <p>
+ * This method is deliberately public so it can be used to reset
+ * the internal state of a session object using serialized
+ * contents replicated from another JVM via JBossCache.
+ * </p>
+ *
+ * @see org.jboss.web.tomcat.service.session.ClusteredSession#readExternal(java.io.ObjectInput)
+ */
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+ {
+ synchronized (this)
+ {
+ // Let superclass read in everything but the attribute map
+ super.readExternal(in);
+
+ attributes = (Map) in.readObject();
+ }
+ }
+
+ /**
+ * Overrides the superclass version by appending the attributes map. Does
+ * not write any attributes whose names are found in
+ * {@link ClusteredSession#excludedAttributes}.
+ *
+ * @see org.jboss.web.tomcat.service.session.ClusteredSession#writeExternal(java.io.ObjectOutput)
+ */
+ public void writeExternal(ObjectOutput out) throws IOException
+ {
+ synchronized (this)
+ {
+ // Let superclass write out everything but the attribute map
+ super.writeExternal(out);
+
+ // Don't replicate any excluded attributes
+ Map excluded = removeExcludedAttributes(attributes);
+
+ out.writeObject(attributes);
+
+ // Restore any excluded attributes
+ if (excluded != null)
+ attributes.putAll(excluded);
+ }
+
+ }
+
+ protected void update(ClusteredSession replicated)
+ {
+ SessionBasedClusteredSession other = (SessionBasedClusteredSession) replicated;
+
+ super.update(other);
+
+ attributes = other.attributes;
+ }
+
+
+ /**
+ * Overrides the superclass version to return <code>true</code> if either
+ * the metadata or the attributes are dirty.
+ */
+ public boolean getReplicateSessionBody()
+ {
+ return isSessionDirty() || getExceedsMaxUnreplicatedInterval();
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,239 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Random;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import org.jboss.logging.Logger;
+
+/**
+ * Unique session id generator
+ *
+ * @author Ben Wang
+ */
+public class SessionIDGenerator
+{
+ protected final static int SESSION_ID_BYTES = 16; // We want 16 Bytes for the session-id
+ protected final static String SESSION_ID_HASH_ALGORITHM = "MD5";
+ protected final static String SESSION_ID_RANDOM_ALGORITHM = "SHA1PRNG";
+ protected final static String SESSION_ID_RANDOM_ALGORITHM_ALT = "IBMSecureRandom";
+ protected Logger log = Logger.getLogger(SessionIDGenerator.class);
+
+ protected MessageDigest digest = null;
+ protected Random random = null;
+ protected static SessionIDGenerator s_;
+
+ protected String sessionIdAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*";
+
+ public static SessionIDGenerator getInstance()
+ {
+ if (s_ == null) s_ = new SessionIDGenerator();
+ return s_;
+ }
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public void setSessionIdAlphabet(String sessionIdAlphabet)
+ {
+ if (sessionIdAlphabet.length() != 65) {
+ throw new IllegalArgumentException("SessionIdAlphabet must be exactly 65 characters long");
+ }
+
+ checkDuplicateChars(sessionIdAlphabet);
+
+ this.sessionIdAlphabet = sessionIdAlphabet;
+ }
+
+ protected void checkDuplicateChars(String sessionIdAlphabet) {
+ char[] alphabet = sessionIdAlphabet.toCharArray();
+ for (int i=0; i < alphabet.length; i++) {
+ if (!uniqueChar(alphabet[i], sessionIdAlphabet)) {
+ throw new IllegalArgumentException("All chars in SessionIdAlphabet must be unique");
+ }
+ }
+ }
+
+ // does a character appear in the String once and only once?
+ protected boolean uniqueChar(char c, String s) {
+ int firstIndex = s.indexOf(c);
+ if (firstIndex == -1) return false;
+ return s.indexOf(c, firstIndex + 1) == -1;
+ }
+
+ /**
+ * The SessionIdAlphabet is the set of characters used to create a session Id
+ */
+ public String getSessionIdAlphabet() {
+ return this.sessionIdAlphabet;
+ }
+
+ public synchronized String getSessionId()
+ {
+ String id = generateSessionId();
+ if (log.isDebugEnabled())
+ log.debug("getSessionId called: " + id);
+ return id;
+ }
+
+
+ /**
+ * Generate a session-id that is not guessable
+ *
+ * @return generated session-id
+ */
+ protected synchronized String generateSessionId()
+ {
+ if (this.digest == null)
+ {
+ this.digest = getDigest();
+ }
+
+ if (this.random == null)
+ {
+ this.random = getRandom();
+ }
+
+ byte[] bytes = new byte[SESSION_ID_BYTES];
+
+ // get random bytes
+ this.random.nextBytes(bytes);
+
+ // Hash the random bytes
+ bytes = this.digest.digest(bytes);
+
+ // Render the result as a String of hexadecimal digits
+ return encode(bytes);
+ }
+
+ /**
+ * Encode the bytes into a String with a slightly modified Base64-algorithm
+ * This code was written by Kevin Kelley <kelley at ruralnet.net>
+ * and adapted by Thomas Peuss <jboss at peuss.de>
+ *
+ * @param data The bytes you want to encode
+ * @return the encoded String
+ */
+ protected String encode(byte[] data)
+ {
+ char[] out = new char[((data.length + 2) / 3) * 4];
+ char[] alphabet = this.sessionIdAlphabet.toCharArray();
+
+ //
+ // 3 bytes encode to 4 chars. Output is always an even
+ // multiple of 4 characters.
+ //
+ for (int i = 0, index = 0; i < data.length; i += 3, index += 4)
+ {
+ boolean quad = false;
+ boolean trip = false;
+
+ int val = (0xFF & (int) data[i]);
+ val <<= 8;
+ if ((i + 1) < data.length)
+ {
+ val |= (0xFF & (int) data[i + 1]);
+ trip = true;
+ }
+ val <<= 8;
+ if ((i + 2) < data.length)
+ {
+ val |= (0xFF & (int) data[i + 2]);
+ quad = true;
+ }
+ out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
+ val >>= 6;
+ out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
+ val >>= 6;
+ out[index + 1] = alphabet[val & 0x3F];
+ val >>= 6;
+ out[index + 0] = alphabet[val & 0x3F];
+ }
+ return new String(out);
+ }
+
+ /**
+ * get a random-number generator
+ *
+ * @return a random-number generator
+ */
+ protected synchronized Random getRandom()
+ {
+ long seed;
+ Random random = null;
+
+ // Mix up the seed a bit
+ seed = System.currentTimeMillis();
+ seed ^= Runtime.getRuntime().freeMemory();
+
+ try
+ {
+ random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ try
+ {
+ random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM_ALT);
+ }
+ catch (NoSuchAlgorithmException e_alt)
+ {
+ log.error("Could not generate SecureRandom for session-id randomness", e);
+ log.error("Could not generate SecureRandom for session-id randomness", e_alt);
+ return null;
+ }
+ }
+
+ // set the generated seed for this PRNG
+ random.setSeed(seed);
+
+ return random;
+ }
+
+ /**
+ * get a MessageDigest hash-generator
+ *
+ * @return a hash generator
+ */
+ protected synchronized MessageDigest getDigest()
+ {
+ MessageDigest digest = null;
+
+ try
+ {
+ digest = MessageDigest.getInstance(SESSION_ID_HASH_ALGORITHM);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ log.error("Could not generate MessageDigest for session-id hashing", e);
+ return null;
+ }
+
+ return digest;
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionIDGenerator.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,301 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+public final class SessionReplicationContext
+{
+ private static final ThreadLocal replicationContext = new ThreadLocal();
+
+ private static final SessionReplicationContext EMPTY = new SessionReplicationContext();
+
+ private int webappCount;
+ private int activityCount;
+ private SnapshotManager soleManager;
+ private ClusteredSession soleSession;
+ private Map crossCtxSessions;
+ private Map expiredSessions;
+ private Request outerRequest;
+ private Response outerResponse;
+
+ /**
+ * Associate a SessionReplicationContext with the current thread, if
+ * there isn't one already. If there isn't one, associate the
+ * given request and response with the context.
+ * <p/>
+ * <strong>NOTE:</strong> Nested calls to this method and {@link #exitWebapp()}
+ * are supported; once a context is established the number of calls to this
+ * method and <code>exitWebapp()</code> are tracked.
+ *
+ * @param request
+ * @param response
+ */
+ public static void enterWebapp(Request request, Response response, boolean startCacheActivity)
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx == null)
+ {
+ ctx = new SessionReplicationContext(request, response);
+ replicationContext.set(ctx);
+ }
+
+ ctx.webappCount++;
+ if (startCacheActivity)
+ ctx.activityCount++;
+ }
+
+ /**
+ * Signals that the webapp is finished handling the request (and
+ * therefore replication can begin.)
+ *
+ * @return a SessionReplicationContext, from which information
+ * about any sessions needing replication can be obtained.
+ * Will not return <code>null</code>.
+ */
+ public static SessionReplicationContext exitWebapp()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null)
+ {
+ ctx.webappCount--;
+ if (ctx.webappCount < 1)
+ {
+ // We've unwound any nested webapp calls, so we'll clean up and
+ // return the context to allow replication. If all cache activity
+ // is done as well, clear the ThreadLocal
+
+ ctx.outerRequest = null;
+ ctx.outerResponse = null;
+
+ if (ctx.activityCount < 1)
+ replicationContext.set(null);
+
+ return ctx;
+ }
+ }
+
+ // A nested valve called us. Just return an empty context
+ return EMPTY;
+
+ }
+
+ public static void bindSession(ClusteredSession session, SnapshotManager manager)
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null && ctx.webappCount > 0)
+ {
+ ctx.addReplicatableSession(session, manager);
+ }
+ /*else {
+ We are past the part of the request cycle where we
+ track sessions for replication
+ }*/
+ }
+
+ public static void sessionExpired(ClusteredSession session, String realId, SnapshotManager manager)
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null && ctx.webappCount > 0)
+ {
+ ctx.addExpiredSession(session, manager);
+ }
+ }
+
+ public static boolean isSessionBoundAndExpired(String realId, SnapshotManager manager)
+ {
+ boolean result = false;
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null)
+ {
+ result = ctx.isSessionExpired(realId, manager);
+ }
+ return result;
+ }
+
+ /**
+ * Marks the current thread as actively processing the given session.
+ * If the thread has already been so marked, increases a counter
+ * so a subsequent call to finishLocalActivity does not remove
+ * the association (allowing nested calls).
+ */
+ public static void startCacheActivity()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx == null)
+ {
+ ctx = new SessionReplicationContext();
+ replicationContext.set(ctx);
+ }
+
+ ctx.activityCount++;
+ }
+
+ /**
+ * Marks the completion of activity on a given session. Should be called
+ * once for each invocation of {@link #startCacheActivity()}.
+ */
+ public static void finishCacheActivity()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ if (ctx != null)
+ {
+ ctx.activityCount--;
+ if (ctx.activityCount < 1 && ctx.webappCount < 1)
+ {
+ replicationContext.set(null);
+ }
+ }
+ }
+
+ public static boolean isLocallyActive()
+ {
+ return getCurrentContext() != null;
+ }
+
+ public static Request getOriginalRequest()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ return (ctx == null ? null : ctx.outerRequest);
+ }
+
+ public static Response getOriginalResponse()
+ {
+ SessionReplicationContext ctx = getCurrentContext();
+ return (ctx == null ? null : ctx.outerResponse);
+ }
+
+ private static SessionReplicationContext getCurrentContext()
+ {
+ return (SessionReplicationContext) replicationContext.get();
+ }
+
+ private SessionReplicationContext(Request request, Response response)
+ {
+ this.outerRequest = request;
+ this.outerResponse = response;
+ }
+
+ private SessionReplicationContext() {}
+
+ /**
+ * Gets a Map<SnapshotManager, ClusteredSession> of sessions that were accessed
+ * during the course of a request. Will only be non-null if
+ * {@link #bindSession(ClusteredSession, SnapshotManager)} was called
+ * with more than one SnapshotManager (i.e the request crossed session
+ * contexts.)
+ */
+ public Map getCrossContextSessions()
+ {
+ return crossCtxSessions;
+ }
+
+ /**
+ * Gets the SnapshotManager that was passed to
+ * {@link #bindSession(ClusteredSession, SnapshotManager)} if and only
+ * if only one such SnapshotManager was passed. Returns <code>null</code>
+ * otherwise, in which case a cross-context request is a possibility,
+ * and {@link #getCrossContextSessions()} should be checked.
+ */
+ public SnapshotManager getSoleSnapshotManager()
+ {
+ return soleManager;
+ }
+
+ /**
+ * Gets the ClusteredSession that was passed to
+ * {@link #bindSession(ClusteredSession, SnapshotManager)} if and only
+ * if only one SnapshotManager was passed. Returns <code>null</code>
+ * otherwise, in which case a cross-context request is a possibility,
+ * and {@link #getCrossContextSessions()} should be checked.
+ */
+ public ClusteredSession getSoleSession()
+ {
+ return soleSession;
+ }
+
+ private void addReplicatableSession(ClusteredSession session, SnapshotManager mgr)
+ {
+ if (crossCtxSessions != null)
+ {
+ crossCtxSessions.put(session, mgr);
+ }
+ else if (soleManager == null)
+ {
+ // First one bound
+ soleManager = mgr;
+ soleSession = session;
+ }
+ else if (!mgr.equals(soleManager))
+ {
+ // We have a cross-context call; need a Map for the sessions
+ crossCtxSessions = new HashMap();
+ crossCtxSessions.put(soleSession, soleManager);
+ crossCtxSessions.put(session, mgr);
+ soleManager = null;
+ soleSession = null;
+ }
+ else
+ {
+ soleSession = session;
+ }
+ }
+
+ private void addExpiredSession(ClusteredSession session, SnapshotManager manager)
+ {
+ boolean store = manager.equals(soleManager);
+ if (store)
+ {
+ soleManager = null;
+ soleSession = null;
+ }
+ else if (crossCtxSessions != null)
+ {
+ // Only store the session if it was previously in our map
+ store = (crossCtxSessions.remove(session) != null);
+ }
+
+ if (store)
+ {
+ if (this.expiredSessions == null)
+ {
+ expiredSessions = new HashMap();
+ }
+ expiredSessions.put(manager, session.getRealId());
+ }
+ }
+
+ private boolean isSessionExpired(String realId, SnapshotManager manager)
+ {
+ boolean result = false;
+ if (expiredSessions != null)
+ {
+ result = realId.equals(expiredSessions.get(manager));
+ }
+ return result;
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionReplicationContext.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,101 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.invocation.MarshalledValueInputStream;
+import org.jboss.invocation.MarshalledValueOutputStream;
+import org.jboss.invocation.MarshalledValue;
+import org.jboss.logging.Logger;
+import org.jboss.serial.io.JBossObjectInputStreamSharedTree;
+import org.jboss.serial.io.JBossObjectOutputStreamSharedTree;
+import org.jboss.serial.io.MarshalledObject;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+
+/**
+ * Factory class for creating object output and input streams,
+ * switching between JDK Serialization and JBoss Serialization classes.
+ * Using MarshalledValue to replace Serializable used inside different
+ * web app class loader context. Assuming that the caller classes are already
+ * handling the switch between web app class loader context.
+ *
+ *
+ * @author <a href="hmesha at novell.com">Hany Mesha</a>
+ */
+public class SessionSerializationFactory
+{
+ static Logger log_ = Logger.getLogger(SessionSerializationFactory.class);
+ static boolean useJBossSerialization = false;
+
+ static
+ {
+ String useJBossSerializationStr = System.getProperty("session.serialization.jboss", "false");
+ useJBossSerialization = Boolean.valueOf(useJBossSerializationStr).booleanValue();
+ }
+
+ public static ObjectOutputStream createObjectOutputStream(OutputStream out) throws IOException
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("createObjectOutputStream using JBossSerialization = " + useJBossSerialization);
+ }
+ return useJBossSerialization ? new JBossObjectOutputStreamSharedTree(out) : new MarshalledValueOutputStream(out);
+ }
+
+ public static ObjectInputStream createObjectInputStream(byte[] bytes) throws IOException
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("createObjectInputStream using JBossSerialization = " + useJBossSerialization);
+ }
+ ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+ return useJBossSerialization ? new JBossObjectInputStreamSharedTree(in) : new MarshalledValueInputStream(in);
+ }
+
+ public static MarshalledValue createMarshalledValue(Object o) throws IOException
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("createMarshalledValue using JBossSerialization = " + useJBossSerialization);
+ }
+ return new MarshalledValue (o);
+ }
+
+ public static MarshalledObject createMarshalledObject(Object o) throws IOException
+ {
+ if (log_.isDebugEnabled())
+ {
+ log_.debug("createMarshalledObject using JBossSerialization = " + useJBossSerialization);
+ }
+ return new MarshalledObject(o);
+ }
+
+ public static boolean useJBossSerialization()
+ {
+ return useJBossSerialization;
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,85 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.logging.Logger;
+
+/**
+ * Abstract base class for a session snapshot manager.
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @version $Revision: 56542 $
+ */
+public abstract class SnapshotManager
+{
+ // The manager the snapshot manager should use
+ private AbstractJBossManager manager;
+
+ // The context-path
+ private String contextPath;
+
+ private Logger log;
+
+ public SnapshotManager(AbstractJBossManager manager, String path)
+ {
+ this.manager = manager;
+ contextPath = path;
+
+ String suffix = path;
+ if (suffix == null || "".equals(suffix))
+ suffix = "ROOT";
+ log = Logger.getLogger(getClass().getName() + "." + suffix);
+ }
+
+ /**
+ * Tell the snapshot manager which session was modified and
+ * must be replicated
+ */
+ public abstract void snapshot(ClusteredSession session);
+
+ /**
+ * Start the snapshot manager
+ */
+ public abstract void start();
+
+ /**
+ * Stop the snapshot manager
+ */
+ public abstract void stop();
+
+ protected String getContextPath()
+ {
+ return contextPath;
+ }
+
+ protected Logger getLog()
+ {
+ return log;
+ }
+
+ protected AbstractJBossManager getManager()
+ {
+ return manager;
+ }
+
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SnapshotManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,148 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+
+package org.jboss.web.tomcat.service.session;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.aop.Advised;
+
+/**
+ * Utility methods related to JBoss distributed sessions.
+ *
+ * @author Brian Stansberry
+ * @version $Revision: 56542 $
+ */
+public class Util
+{
+ // Types that are considered "primitive".
+ private static final Set immediates =
+ new HashSet(Arrays.asList(new Object[]{
+ String.class,
+ Boolean.class,
+ Double.class,
+ Float.class,
+ Integer.class,
+ Long.class,
+ Short.class,
+ Character.class,
+ Boolean.TYPE,
+ Double.TYPE,
+ Float.TYPE,
+ Integer.TYPE,
+ Long.TYPE,
+ Short.TYPE,
+ Character.TYPE,
+ Class.class}));
+
+ /**
+ * Returns a session id with any trailing jvmRoute removed.
+ *
+ * @param sessionId the raw session id
+ *
+ * @return <code>sessionId</code> with the final '.' and any
+ * characters thereafter removed.
+ */
+ public static String getRealId(String sessionId)
+ {
+ int index = sessionId.lastIndexOf(".");
+ if (index > 0)
+ {
+ return sessionId.substring(0, index);
+ }
+ else
+ {
+ return sessionId;
+ }
+ }
+
+ /**
+ * Checks whether the given object is usable for FIELD granularity
+ * replication.
+ *
+ * @param pojo the pojo
+ * @return <code>true</code> if the attribute type is acceptable,
+ * <code>false</code> otherwise
+ */
+ public static boolean checkPojoType(Object pojo)
+ {
+ return ( (pojo instanceof Serializable)
+ || (pojo instanceof Collection)
+ || (pojo instanceof Map)
+ || (pojo instanceof Advised)
+ || (immediates.contains(pojo.getClass())));
+ }
+
+ public static Set parseComplexFields(Class clazz)
+ {
+ Set result = new HashSet();
+
+ while (clazz != null)
+ {
+ Field[] fields = clazz.getDeclaredFields();
+ for (int i = 0; i < fields.length; i++)
+ {
+ if (!immediates.contains(fields[i].getType())
+ && isReplicatable(fields[i]))
+ {
+ result.add(fields[i].getName());
+ }
+ }
+
+ clazz = clazz.getSuperclass();
+ }
+ return result;
+ }
+
+ /**
+ * Returns false if the given field is static, transient or final.
+ *
+ * @param f the field
+ * @return
+ */
+ public static boolean isReplicatable(Field f) {
+ int mods = f.getModifiers();
+ /**
+ * The following modifiers are ignored in the cache, i.e., they will not be stored in the cache.
+ * Whenever, user trying to access these fields, it will be accessed from the in-memory version.
+ */
+ if (Modifier.isStatic(mods)
+ || Modifier.isTransient(mods)
+ || Modifier.isFinal(mods)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Prevent instantiation.
+ */
+ private Util() {}
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1038 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.sso;
+
+
+import org.jboss.web.tomcat.service.session.JBossManager;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.Realm;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.session.ManagerBase;
+
+
+/**
+ * A <strong>Valve</strong> that supports a "single sign on" user experience,
+ * where the security identity of a user who successfully authenticates to one
+ * web application is propogated to other web applications in the same
+ * security domain. For successful use, the following requirements must
+ * be met:
+ * <ul>
+ * <li>This Valve must be configured on the Container that represents a
+ * virtual host (typically an implementation of <code>Host</code>).</li>
+ * <li>The <code>Realm</code> that contains the shared user and role
+ * information must be configured on the same Container (or a higher
+ * one), and not overridden at the web application level.</li>
+ * <li>The web applications themselves must use one of the standard
+ * Authenticators found in the
+ * <code>org.apache.catalina.authenticator</code> package.</li>
+ * </ul>
+ *
+ * @author Brian E. Stansberry based on the work of Craig R. McClanahan
+ * @version $Revision: 58435 $ $Date: 2006-11-16 05:18:12 +0100 (jeu., 16 nov. 2006) $
+ */
+public class ClusteredSingleSignOn
+ extends org.apache.catalina.authenticator.SingleSignOn
+ implements LifecycleListener
+{
+
+ // Override the superclass value
+ static
+ {
+ info = ClusteredSingleSignOn.class.getName();
+ }
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * Fully qualified name of a class implementing
+ * {@link SSOClusterManager SSOClusterManager} that will be used
+ * to manage SSOs across a cluster.
+ */
+ private String clusterManagerClass =
+ TreeCacheSSOClusterManager.class.getName();
+
+ /**
+ * Object used to provide cross-cluster support for single sign on.
+ */
+ private SSOClusterManager ssoClusterManager = null;
+
+ /**
+ * Object name of the tree cache used by SSOClusterManager.
+ * Only relevant if the SSOClusterManager implementation is
+ * TreeCacheSSOClusterManager.
+ */
+ private String treeCacheName = "jboss.cache:service=TomcatClusteringCache";
+
+ private Set activeManagers = Collections.synchronizedSet(new HashSet());
+
+ // ------------------------------------------------------------- Properties
+
+ /**
+ * Gets the object that provides SSO support across a cluster.
+ *
+ * @return the object provided cluster support, or <code>null</code> if
+ * no such object has been configured.
+ */
+ public SSOClusterManager getClusterManager()
+ {
+ return this.ssoClusterManager;
+ }
+
+
+ /**
+ * Sets the object that provides SSO support across a cluster.
+ *
+ * @param clusterManager the object that provides SSO support.
+ * @throws IllegalStateException if this method is invoked after this valve
+ * has been started.
+ */
+ public void setClusterManager(SSOClusterManager clusterManager)
+ {
+ if (started && (clusterManager != ssoClusterManager))
+ {
+ throw new IllegalStateException("already started -- cannot set a " +
+ "new SSOClusterManager");
+ }
+
+ this.ssoClusterManager = clusterManager;
+
+ if (clusterManager != null)
+ {
+ clusterManagerClass = clusterManager.getClass().getName();
+ }
+ }
+
+
+ /**
+ * Gets the name of the class that will be used to provide SSO support
+ * across a cluster.
+ *
+ * @return Fully qualified name of a class implementing
+ * {@link SSOClusterManager SSOClusterManager}
+ * that is being used to manage SSOs across a cluster.
+ * May return <code>null</code> (the default) if clustered
+ * SSO support is not configured.
+ */
+ public String getClusterManagerClass()
+ {
+ return clusterManagerClass;
+ }
+
+
+ /**
+ * Sets the name of the class that will be used to provide SSO support
+ * across a cluster.
+ * <p><b>NOTE: </b>
+ * If this Valve has already started, and no SSOClusterManager has been
+ * configured for it, calling this method will
+ *
+ * @param managerClass Fully qualified name of a class implementing
+ * {@link SSOClusterManager SSOClusterManager}
+ * that will be used to manage SSOs across a cluster.
+ * Class must declare a public no-arguments
+ * constructor. <code>null</code> is allowed.
+ */
+ public void setClusterManagerClass(String managerClass)
+ {
+ if (!started)
+ {
+ clusterManagerClass = managerClass;
+ }
+ else if (ssoClusterManager == null)
+ {
+ try
+ {
+ createClusterManager(managerClass);
+ }
+ catch (LifecycleException e)
+ {
+ getContainer().getLogger().error("Exception creating SSOClusterManager " +
+ managerClass, e);
+ }
+ }
+ else
+ {
+ getContainer().getLogger().error("Cannot set clusterManagerClass to " + managerClass +
+ "; already started using " + clusterManagerClass);
+ }
+ }
+
+ /**
+ * Object name of the tree cache used by SSOClusterManager.
+ * Only relevant if the SSOClusterManager implementation is
+ * TreeCacheSSOClusterManager.
+ */
+ public String getTreeCacheName()
+ {
+ return treeCacheName;
+ }
+
+ /**
+ * Sets the object name of the tree cache used by SSOClusterManager.
+ * Only relevant if the SSOClusterManager implementation is
+ * TreeCacheSSOClusterManager.
+ */
+ public void setTreeCacheName(String cacheName)
+ throws Exception
+ {
+ this.treeCacheName = cacheName;
+ if (ssoClusterManager != null
+ && ssoClusterManager instanceof TreeCacheSSOClusterManager)
+ {
+ ((TreeCacheSSOClusterManager) ssoClusterManager).setCacheName(cacheName);
+ }
+ }
+
+
+ // ------------------------------------------------------ Lifecycle Methods
+
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after <code>configure()</code>,
+ * and before any of the public methods of the component are utilized.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ public void start() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (started)
+ {
+ throw new LifecycleException
+ (sm.getString("authenticator.alreadyStarted"));
+ }
+
+ // Attempt to create an SSOClusterManager
+ createClusterManager(getClusterManagerClass());
+
+ lifecycle.fireLifecycleEvent(START_EVENT, null);
+ started = true;
+
+ if (ssoClusterManager != null)
+ {
+ ssoClusterManager.start();
+ }
+
+ }
+
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that needs to be reported
+ */
+ public void stop() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (!started)
+ {
+ throw new LifecycleException
+ (sm.getString("authenticator.notStarted"));
+ }
+
+ if (ssoClusterManager != null)
+ {
+ ssoClusterManager.stop();
+ }
+
+ lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+ started = false;
+
+ }
+
+
+ // ------------------------------------------------ SessionListener Methods
+
+
+ /**
+ * Updates the state of a single sign on session to reflect the destruction
+ * of a standard HTTP session.
+ * <p/>
+ * If the given event is a {@link Session#SESSION_DESTROYED_EVENT
+ * Session destroyed event}, checks whether the session was destroyed due
+ * to timeout or user action (i.e. logout). If due to timeout, disassociates
+ * the Session from the single sign on session. If due to logout, invokes
+ * the {@link #logout} method.
+ *
+ * @param event SessionEvent that has occurred
+ */
+ public void sessionEvent(SessionEvent event)
+ {
+ // We only care about session destroyed events
+ if (!Session.SESSION_DESTROYED_EVENT.equals(event.getType()))
+ return;
+
+ // Look up the single session id associated with this session (if any)
+ Session session = event.getSession();
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Process session destroyed on " + session);
+
+ String ssoId = null;
+ synchronized (reverse)
+ {
+ ssoId = (String) reverse.get(session);
+ }
+ if (ssoId == null)
+ return;
+
+ // Was the session destroyed as the result of a timeout or
+ // the undeployment of the containing webapp?
+ // If so, we'll just remove the expired session from the
+ // SSO. If the session was logged out, we'll log out
+ // of all sessions associated with the SSO.
+ if (isSessionTimedOut(session) || isManagerStopped(session))
+ {
+ removeSession(ssoId, session);
+ }
+ else
+ {
+ // The session was logged out.
+ logout(ssoId);
+ }
+
+ }
+
+ private boolean isSessionTimedOut(Session session)
+ {
+ return (session.getMaxInactiveInterval() > 0)
+ && (System.currentTimeMillis() - session.getLastAccessedTime() >=
+ session.getMaxInactiveInterval() * 1000);
+ }
+
+ private boolean isManagerStopped(Session session)
+ {
+ boolean stopped = false;
+
+ Manager manager = session.getManager();
+
+ if (manager instanceof ManagerBase)
+ {
+ ObjectName mgrName = ((ManagerBase)manager).getObjectName();
+ stopped = (!activeManagers.contains(mgrName));
+ }
+ else if (manager instanceof JBossManager)
+ {
+ ObjectName mgrName = ((JBossManager)manager).getObjectName();
+ stopped = (!activeManagers.contains(mgrName));
+ }
+ else if (manager instanceof Lifecycle)
+ {
+ stopped = (!activeManagers.contains(manager));
+ }
+ // else we have no way to tell, so assume not
+
+ return stopped;
+ }
+
+ // ---------------------------------------------- LifecycleListener Methods
+
+
+ public void lifecycleEvent(LifecycleEvent event)
+ {
+ String type = event.getType();
+ if (Lifecycle.BEFORE_STOP_EVENT.equals(type)
+ || Lifecycle.STOP_EVENT.equals(type)
+ || Lifecycle.AFTER_STOP_EVENT.equals(type))
+ {
+ Lifecycle source = event.getLifecycle();
+ boolean removed;
+ if (source instanceof ManagerBase)
+ {
+ removed = activeManagers.remove(((ManagerBase)source).getObjectName());
+ }
+ else if (source instanceof JBossManager)
+ {
+ removed = activeManagers.remove(((JBossManager)source).getObjectName());
+ }
+ else
+ {
+ removed = activeManagers.remove(source);
+ }
+
+ if (removed)
+ {
+ source.removeLifecycleListener(this);
+
+ if (getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug("ClusteredSSO: removed " +
+ "stopped manager " + source.toString());
+ }
+ }
+
+ // TODO consider getting the sessions and removing any from our sso's
+ // Idea is to cleanup after managers that don't destroy sessions
+
+ }
+ }
+
+
+ // ---------------------------------------------------------- Valve Methods
+
+
+ /**
+ * Perform single-sign-on support processing for this request.
+ * <p/>
+ * Overrides the superclass version by handling the fact that a
+ * single sign on may have been originated on another cluster node and
+ * thus may not have a <code>Principal</code> object associated with it
+ * on this node.
+ *
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ * @param context The valve context used to invoke the next valve
+ * in the current processing pipeline
+ * @throws IOException if an input/output error occurs
+ * @throws ServletException if a servlet error occurs
+ */
+ public void invoke(Request request, Response response)
+ throws IOException, ServletException
+ {
+ request.removeNote(Constants.REQ_SSOID_NOTE);
+
+ // Has a valid user already been authenticated?
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Process request for '" + request.getRequestURI() + "'");
+ if (request.getUserPrincipal() != null)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug(" Principal '" + request.getUserPrincipal().getName() +
+ "' has already been authenticated");
+ getNext().invoke(request, response);
+ return;
+ }
+
+ // Check for the single sign on cookie
+ Cookie cookie = null;
+ Cookie cookies[] = request.getCookies();
+ if (cookies == null)
+ cookies = new Cookie[0];
+ for (int i = 0; i < cookies.length; i++)
+ {
+ if (Constants.SINGLE_SIGN_ON_COOKIE.equals(cookies[i].getName()))
+ {
+ cookie = cookies[i];
+ break;
+ }
+ }
+ if (cookie == null)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug(" SSO cookie is not present");
+ getNext().invoke(request, response);
+ return;
+ }
+
+ // Look up the cached Principal associated with this cookie value
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug(" Checking for cached principal for " + cookie.getValue());
+ SingleSignOnEntry entry = getSingleSignOnEntry(cookie.getValue());
+ if (entry != null)
+ {
+ Principal ssoPrinc = entry.getPrincipal();
+ // have to deal with the fact that the entry may not have an
+ // associated Principal. SSO entries retrieved via a lookup from a
+ // cluster will not have a Principal, as Principal is not Serializable
+ if (getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug(" Found cached principal '" +
+ (ssoPrinc == null ? "NULL" : ssoPrinc.getName()) +
+ "' with auth type '" + entry.getAuthType() + "'");
+ }
+ request.setNote(Constants.REQ_SSOID_NOTE, cookie.getValue());
+ // Only set security elements if per-request reauthentication is
+ // not required AND the SSO entry had a Principal.
+ if (!getRequireReauthentication() && ssoPrinc != null)
+ {
+ request.setAuthType(entry.getAuthType());
+ request.setUserPrincipal(ssoPrinc);
+ }
+ }
+ else
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug(" No cached principal found, erasing SSO cookie");
+ cookie.setMaxAge(0);
+ response.addCookie(cookie);
+ }
+
+ // Invoke the next Valve in our pipeline
+ getNext().invoke(request, response);
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Associate the specified single sign on identifier with the
+ * specified Session.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of any new association of SSO and Session.
+ *
+ * @param ssoId Single sign on identifier
+ * @param session Session to be associated
+ */
+ protected void associate(String ssoId, Session session)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Associate sso id " + ssoId + " with session " + session);
+
+ SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+ boolean added = false;
+ if (sso != null)
+ added = sso.addSession(this, session);
+
+ synchronized (reverse)
+ {
+ reverse.put(session, ssoId);
+ }
+
+ // If we made a change, track the manager and notify any cluster
+ if (added)
+ {
+ Manager manager = session.getManager();
+
+ // Prefer to cache an ObjectName to avoid risk of leaking a manager,
+ // so if the manager exposes one, use it
+ Object mgrKey = null;
+ if (manager instanceof ManagerBase)
+ {
+ mgrKey = ((ManagerBase)manager).getObjectName();
+ }
+ else if (manager instanceof JBossManager)
+ {
+ mgrKey = ((JBossManager)manager).getObjectName();
+ }
+ else if (manager instanceof Lifecycle)
+ {
+ mgrKey = manager;
+ }
+ else {
+ getContainer().getLogger().warn("Manager for session " +
+ session.getIdInternal() +
+ " does not implement Lifecycle; web app shutdown may " +
+ " lead to incorrect SSO invalidations");
+ }
+
+ if (mgrKey != null)
+ {
+ synchronized (activeManagers)
+ {
+ if (!activeManagers.contains(mgrKey))
+ {
+ activeManagers.add(mgrKey);
+ ((Lifecycle) manager).addLifecycleListener(this);
+ }
+ }
+ }
+
+ if (ssoClusterManager != null)
+ ssoClusterManager.addSession(ssoId, session);
+ }
+ }
+
+
+ /**
+ * Deregister the specified session. If it is the last session,
+ * then also get rid of the single sign on identifier.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of any disassociation of SSO and Session.
+ *
+ * @param ssoId Single sign on identifier
+ * @param session Session to be deregistered
+ */
+ protected void deregister(String ssoId, Session session)
+ {
+ synchronized (reverse)
+ {
+ reverse.remove(session);
+ }
+
+ SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+ if (sso == null)
+ return;
+
+ boolean removed = sso.removeSession(session);
+ // If we changed anything, notify any cluster
+ if (removed && ssoClusterManager != null)
+ {
+ ssoClusterManager.removeSession(ssoId, session);
+ }
+
+ // see if this was the last session on this node,
+ // if remove sso entry from our local cache
+ if (sso.getSessionCount() == 0)
+ {
+ synchronized (cache)
+ {
+ sso = (SingleSignOnEntry) cache.remove(ssoId);
+ }
+ }
+ }
+
+
+ /**
+ * Deregister the specified single sign on identifier, and invalidate
+ * any associated sessions.
+ *
+ * @param ssoId Single sign on identifier to deregister
+ */
+ protected void deregister(String ssoId)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Deregistering sso id '" + ssoId + "'");
+
+ // Look up and remove the corresponding SingleSignOnEntry
+ SingleSignOnEntry sso = null;
+ synchronized (cache)
+ {
+ sso = (SingleSignOnEntry) cache.remove(ssoId);
+ }
+
+ if (sso == null)
+ return;
+
+ // Expire any associated sessions
+ Session sessions[] = sso.findSessions();
+ for (int i = 0; i < sessions.length; i++)
+ {
+ if (getContainer().getLogger().isTraceEnabled())
+ getContainer().getLogger().trace(" Invalidating session " + sessions[i]);
+ // Remove from reverse cache first to avoid recursion
+ synchronized (reverse)
+ {
+ reverse.remove(sessions[i]);
+ }
+ // Invalidate this session
+ sessions[i].expire();
+ }
+
+ // NOTE: Clients may still possess the old single sign on cookie,
+ // but it will be removed on the next request since it is no longer
+ // in the cache
+ }
+
+
+ /**
+ * Deregister the given SSO, invalidating any associated sessions, then
+ * notify any cluster of the logout.
+ *
+ * @param ssoId the id of the SSO session
+ */
+ protected void logout(String ssoId)
+ {
+ deregister(ssoId);
+ // broadcast logout to any cluster
+ if (ssoClusterManager != null)
+ ssoClusterManager.logout(ssoId);
+ }
+
+
+ /**
+ * Look up and return the cached SingleSignOn entry associated with this
+ * sso id value, if there is one; otherwise return <code>null</code>.
+ *
+ * @param ssoId Single sign on identifier to look up
+ */
+ protected SingleSignOnEntry getSingleSignOnEntry(String ssoId)
+ {
+ SingleSignOnEntry sso = localLookup(ssoId);
+ // If we don't have one locally and there is a cluster,
+ // query the cluster for the SSO
+ if (sso == null && ssoClusterManager != null)
+ {
+ sso = ssoClusterManager.lookup(ssoId);
+ if (sso != null)
+ {
+ // Store it locally
+ synchronized (cache)
+ {
+ cache.put(ssoId, sso);
+ }
+ }
+ }
+
+ return sso;
+ }
+
+
+ /**
+ * Attempts reauthentication to the given <code>Realm</code> using
+ * the credentials associated with the single sign-on session
+ * identified by argument <code>ssoId</code>.
+ * <p/>
+ * If reauthentication is successful, the <code>Principal</code> and
+ * authorization type associated with the SSO session will be bound
+ * to the given <code>HttpRequest</code> object via calls to
+ * {@link HttpRequest#setAuthType HttpRequest.setAuthType()} and
+ * {@link HttpRequest#setUserPrincipal HttpRequest.setUserPrincipal()}
+ * </p>
+ *
+ * @param ssoId identifier of SingleSignOn session with which the
+ * caller is associated
+ * @param realm Realm implementation against which the caller is to
+ * be authenticated
+ * @param request the request that needs to be authenticated
+ * @return <code>true</code> if reauthentication was successful,
+ * <code>false</code> otherwise.
+ */
+ protected boolean reauthenticate(String ssoId, Realm realm,
+ Request request)
+ {
+ if (ssoId == null || realm == null)
+ return false;
+
+ boolean reauthenticated = false;
+
+ SingleSignOnEntry entry = getSingleSignOnEntry(ssoId);
+ if (entry != null && entry.getCanReauthenticate())
+ {
+
+ String username = entry.getUsername();
+ if (username != null)
+ {
+ Principal reauthPrincipal =
+ realm.authenticate(username, entry.getPassword());
+ if (reauthPrincipal != null)
+ {
+ reauthenticated = true;
+ // Bind the authorization credentials to the request
+ request.setAuthType(entry.getAuthType());
+ request.setUserPrincipal(reauthPrincipal);
+ // JBAS-2314 -- bind principal to the entry as well
+ entry.setPrincipal(reauthPrincipal);
+ }
+ }
+ }
+
+ return reauthenticated;
+ }
+
+
+ /**
+ * Register the specified Principal as being associated with the specified
+ * value for the single sign on identifier.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of the registration.
+ *
+ * @param ssoId Single sign on identifier to register
+ * @param principal Associated user principal that is identified
+ * @param authType Authentication type used to authenticate this
+ * user principal
+ * @param username Username used to authenticate this user
+ * @param password Password used to authenticate this user
+ */
+ protected void register(String ssoId, Principal principal, String authType,
+ String username, String password)
+ {
+ registerLocal(ssoId, principal, authType, username, password);
+
+ // broadcast change to any cluster
+ if (ssoClusterManager != null)
+ ssoClusterManager.register(ssoId, authType, username, password);
+ }
+
+
+ /**
+ * Remove a single Session from a SingleSignOn. Called when
+ * a session is timed out and no longer active.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of any disassociation of SSO and Session.
+ *
+ * @param ssoId Single sign on identifier from which to remove the session.
+ * @param session the session to be removed.
+ */
+ protected void removeSession(String ssoId, Session session)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Removing session " + session.toString() +
+ " from sso id " + ssoId);
+
+ // Get a reference to the SingleSignOn
+ SingleSignOnEntry entry = getSingleSignOnEntry(ssoId);
+ if (entry == null)
+ return;
+
+ // Remove the inactive session from SingleSignOnEntry
+ boolean removed = entry.removeSession(session);
+ // If we changed anything, notify any cluster
+ if (removed && ssoClusterManager != null)
+ {
+ ssoClusterManager.removeSession(ssoId, session);
+ }
+
+ // Remove the inactive session from the 'reverse' Map.
+ synchronized (reverse)
+ {
+ reverse.remove(session);
+ }
+
+ // If there are no sessions left in the SingleSignOnEntry,
+ // deregister the entry.
+ if (entry.getSessionCount() == 0)
+ {
+ deregister(ssoId);
+ }
+ }
+
+
+ /**
+ * Updates any <code>SingleSignOnEntry</code> found under key
+ * <code>ssoId</code> with the given authentication data.
+ * <p/>
+ * The purpose of this method is to allow an SSO entry that was
+ * established without a username/password combination (i.e. established
+ * following DIGEST or CLIENT-CERT authentication) to be updated with
+ * a username and password if one becomes available through a subsequent
+ * BASIC or FORM authentication. The SSO entry will then be usable for
+ * reauthentication.
+ * <p/>
+ * <b>NOTE:</b> Only updates the SSO entry if a call to
+ * <code>SingleSignOnEntry.getCanReauthenticate()</code> returns
+ * <code>false</code>; otherwise, it is assumed that the SSO entry already
+ * has sufficient information to allow reauthentication and that no update
+ * is needed.
+ * <p/>
+ * Differs from the superclass version in that it notifies the cluster
+ * of any update.
+ *
+ * @param ssoId identifier of Single sign to be updated
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ protected void update(String ssoId, Principal principal, String authType,
+ String username, String password)
+ {
+ boolean needToBroadcast = updateLocal(ssoId, principal, authType,
+ username, password);
+
+ // if there was a change, broadcast it to any cluster
+ if (needToBroadcast && ssoClusterManager != null)
+ {
+ ssoClusterManager.updateCredentials(ssoId, authType,
+ username, password);
+ }
+ }
+
+ //---------------------------------------------- Package-Protected Methods
+
+ /**
+ * Search in our local cache for an SSO entry.
+ *
+ * @param ssoId the id of the SSO session
+ * @return any SingleSignOnEntry associated with the given id, or
+ * <code>null</code> if there is none.
+ */
+ SingleSignOnEntry localLookup(String ssoId)
+ {
+ synchronized (cache)
+ {
+ return ((SingleSignOnEntry) cache.get(ssoId));
+ }
+
+ }
+
+ /**
+ * Create a SingleSignOnEntry using the passed configuration parameters and
+ * register it in the local cache, bound to the given id.
+ *
+ * @param ssoId the id of the SSO session
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ void registerLocal(String ssoId, Principal principal, String authType,
+ String username, String password)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ {
+ getContainer().getLogger().debug("Registering sso id '" + ssoId + "' for user '" +
+ principal.getName() + "' with auth type '" + authType + "'");
+ }
+
+ synchronized (cache)
+ {
+ cache.put(ssoId, new SingleSignOnEntry(principal, authType,
+ username, password));
+ }
+ }
+
+ /**
+ * Updates any <code>SingleSignOnEntry</code> found under key
+ * <code>ssoId</code> with the given authentication data.
+ *
+ * @param ssoId identifier of Single sign to be updated
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ * @return <code>true</code> if the update resulted in an actual change
+ * to the entry's authType, username or principal properties
+ */
+ boolean updateLocal(String ssoId, Principal principal, String authType,
+ String username, String password)
+ {
+ boolean shouldBroadcast = false;
+
+ SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+ // Only update if the entry is missing information
+ if (sso != null)
+ {
+ if (sso.getCanReauthenticate() == false)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Update sso id " + ssoId + " to auth type " + authType);
+
+ synchronized (sso)
+ {
+ shouldBroadcast = sso.updateCredentials(principal, authType,
+ username, password);
+ }
+ }
+ else if (sso.getPrincipal() == null && principal != null)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Update sso id " + ssoId + " with principal " +
+ principal.getName());
+
+ synchronized (sso)
+ {
+ sso.setPrincipal(principal);
+ // No need to notify cluster; Principals don't replicate
+ }
+ }
+
+ }
+
+ return shouldBroadcast;
+
+ }
+
+ void remoteUpdate(String ssoId, String authType,
+ String username, String password)
+ {
+ SingleSignOnEntry sso = getSingleSignOnEntry(ssoId);
+ // Only update if the entry is missing information
+ if (sso != null && sso.getCanReauthenticate() == false)
+ {
+ if (getContainer().getLogger().isDebugEnabled())
+ getContainer().getLogger().debug("Update sso id " + ssoId + " to auth type " + authType);
+
+ synchronized (sso)
+ {
+ // Use the existing principal
+ Principal p = sso.getPrincipal();
+ sso.updateCredentials(p, authType, username, password);
+ }
+ }
+
+ }
+
+
+ // ------------------------------------------------------- Private Methods
+
+
+ /**
+ * Instantiates an instance of the given class, making it this valve's
+ * SSOClusterManager.
+ * <p/>
+ * If this valve has been started and the given class implements
+ * <code>Lifecycle</code>, starts the new SSOClusterManager.
+ *
+ * @param className fully qualified class name of an implementation
+ * of {@link SSOClusterManager SSOClusterManager}.
+ * @throws LifecycleException if there is any problem instantiating or
+ * starting the object, or if the created
+ * object does not implement
+ * <code>SSOClusterManger</code>
+ */
+ private void createClusterManager(String className)
+ throws LifecycleException
+ {
+ if (ssoClusterManager != null)
+ return;
+
+ if (className != null)
+ {
+ SSOClusterManager mgr = null;
+ try
+ {
+ ClassLoader tcl =
+ Thread.currentThread().getContextClassLoader();
+ Class clazz = tcl.loadClass(className);
+ mgr = (SSOClusterManager) clazz.newInstance();
+ mgr.setSingleSignOnValve(this);
+ if (mgr instanceof TreeCacheSSOClusterManager)
+ {
+ ((TreeCacheSSOClusterManager) mgr).setCacheName(getTreeCacheName());
+ }
+ ssoClusterManager = mgr;
+ clusterManagerClass = className;
+ }
+ catch (Throwable t)
+ {
+ throw new LifecycleException("Cannot create " +
+ "SSOClusterManager using " +
+ className, t);
+ }
+
+ if (started)
+ {
+ ssoClusterManager.start();
+ }
+ }
+ }
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/ClusteredSingleSignOn.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,121 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.sso;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.Lifecycle;
+
+/**
+ * Provides communications support between a SingleSignOn valve and other
+ * such valves configured for the same hostname within a server cluster.
+ * <p/>
+ * Implementations of this interface must declare a public no-arguments
+ * constructor.
+ *
+ * @author Brian E. Stansberry
+ * @version $Revision: 45726 $ $Date: 2006-06-21 21:50:00 +0200 (mer., 21 juin 2006) $
+ * @see ClusteredSingleSignOn
+ */
+public interface SSOClusterManager
+ extends Lifecycle
+{
+
+ /**
+ * Notify the cluster of the addition of a Session to an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param session the Session that has been added
+ */
+ void addSession(String ssoId, Session session);
+
+ /**
+ * Gets the SingleSignOn valve for which this object is handling
+ * cluster communications.
+ *
+ * @return the <code>SingleSignOn</code> valve.
+ */
+ ClusteredSingleSignOn getSingleSignOnValve();
+
+ /**
+ * Sets the SingleSignOn valve for which this object is handling
+ * cluster communications.
+ * <p><b>NOTE:</b> This method must be called before calls can be
+ * made to the other methods of this interface.
+ *
+ * @param valve a <code>SingleSignOn</code> valve.
+ */
+ void setSingleSignOnValve(ClusteredSingleSignOn valve);
+
+ /**
+ * Notifies the cluster that a single sign on session has been terminated
+ * due to a user logout.
+ *
+ * @param ssoId the id of the SSO session
+ */
+ void logout(String ssoId);
+
+ /**
+ * Queries the cluster for the existence of a SSO session with the given
+ * id, returning a <code>SingleSignOnEntry</code> if one is found.
+ *
+ * @param ssoId the id of the SSO session
+ * @return a <code>SingleSignOnEntry</code> created using information
+ * found on another cluster node, or <code>null</code> if no
+ * entry could be found.
+ */
+ SingleSignOnEntry lookup(String ssoId);
+
+ /**
+ * Notifies the cluster of the creation of a new SSO entry.
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ void register(String ssoId, String authType, String username,
+ String password);
+
+ /**
+ * Notify the cluster of the removal of a Session from an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param session the Session that has been removed
+ */
+ void removeSession(String ssoId, Session session);
+
+ /**
+ * Notifies the cluster of an update of the security credentials
+ * associated with an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ void updateCredentials(String ssoId, String authType, String username,
+ String password);
+
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SSOClusterManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,264 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.web.tomcat.service.sso;
+
+import java.security.Principal;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.authenticator.SingleSignOn;
+
+/**
+ * A class that represents entries in the cache of authenticated users.
+ *
+ * @author Brian E. Stansberry, based on work by Craig R. McClanahan
+ * @version $Revision: 45726 $ $Date: 2006-06-21 21:50:00 +0200 (mer., 21 juin 2006) $
+ * @see SingleSignOn
+ */
+class SingleSignOnEntry
+{
+ // ------------------------------------------------------ Instance Fields
+
+ private String authType = null;
+
+ private String password = null;
+
+ private Principal principal = null;
+
+ private Session sessions[] = new Session[0];
+
+ private String username = null;
+
+ private boolean canReauthenticate = false;
+
+ // --------------------------------------------------------- Constructors
+
+ /**
+ * Creates a new SingleSignOnEntry
+ *
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ SingleSignOnEntry(Principal principal, String authType,
+ String username, String password)
+ {
+ updateCredentials(principal, authType, username, password);
+ }
+
+ // ------------------------------------------------------- Package Methods
+
+ /**
+ * Adds a <code>Session</code> to the list of those associated with
+ * this SSO.
+ *
+ * @param sso The <code>SingleSignOn</code> valve that is managing
+ * the SSO session.
+ * @param session The <code>Session</code> being associated with the SSO.
+ * @return <code>true</code> if the given Session was a new addition (i.e.
+ * was not previously associated with this entry);
+ * <code>false</code> otherwise.
+ */
+ synchronized boolean addSession(SingleSignOn sso, Session session)
+ {
+ for (int i = 0; i < sessions.length; i++)
+ {
+ if (session == sessions[i])
+ return false;
+ }
+ Session results[] = new Session[sessions.length + 1];
+ System.arraycopy(sessions, 0, results, 0, sessions.length);
+ results[sessions.length] = session;
+ sessions = results;
+ session.addSessionListener(sso);
+ return true;
+ }
+
+ /**
+ * Removes the given <code>Session</code> from the list of those
+ * associated with this SSO.
+ *
+ * @param session the <code>Session</code> to remove.
+ * @return <code>true</code> if the given Session needed to be removed
+ * (i.e. was in fact previously associated with this entry);
+ * <code>false</code> otherwise.
+ */
+ synchronized boolean removeSession(Session session)
+ {
+ if (sessions.length == 0)
+ return false;
+
+ boolean removed = false;
+ Session[] nsessions = new Session[sessions.length - 1];
+ for (int i = 0, j = 0; i < sessions.length; i++)
+ {
+ if (session == sessions[i])
+ {
+ removed = true;
+ continue;
+ }
+ else if (!removed && i == nsessions.length)
+ {
+ // We have tested all our sessions, and have not had to
+ // remove any; break loop now so we don't cause an
+ // ArrayIndexOutOfBounds on nsessions
+ break;
+ }
+ nsessions[j++] = sessions[i];
+ }
+ sessions = nsessions;
+ // Only if we removed a session, do we replace our session list
+ if (removed)
+ sessions = nsessions;
+ return removed;
+ }
+
+ /**
+ * Returns the <code>Session</code>s associated with this SSO.
+ */
+ synchronized Session[] findSessions()
+ {
+ return (this.sessions);
+ }
+
+ /**
+ * Gets the name of the authentication type originally used to authenticate
+ * the user associated with the SSO.
+ *
+ * @return "BASIC", "CLIENT-CERT", "DIGEST", "FORM" or "NONE"
+ */
+ String getAuthType()
+ {
+ return (this.authType);
+ }
+
+ /**
+ * Gets whether the authentication type associated with the original
+ * authentication supports reauthentication.
+ *
+ * @return <code>true</code> if <code>getAuthType</code> returns
+ * "BASIC" or "FORM", <code>false</code> otherwise.
+ */
+ boolean getCanReauthenticate()
+ {
+ return (this.canReauthenticate);
+ }
+
+ /**
+ * Gets the password credential (if any) associated with the SSO.
+ *
+ * @return the password credential associated with the SSO, or
+ * <code>null</code> if the original authentication type
+ * does not involve a password.
+ */
+ String getPassword()
+ {
+ return (this.password);
+ }
+
+ /**
+ * Gets the <code>Principal</code> that has been authenticated by
+ * the SSO.
+ * <p/>
+ * <b>NOTE: </b> May return <code>null</code> if this object was
+ * retrieved via a lookup from another node in a cluster. Interface
+ * <code>Principal</code> does not extend <code>Serializable</code>,
+ * so a <code>SingleSignOnEntry</code>'s principal member cannot be
+ * serialized as part of SSO management in a cluster. A
+ * <code>Principal</code> cannot be bound to a
+ * <code>SingleSignOnEntry</code> until the SSO has been authenticated
+ * by the local node.
+ *
+ * @return The <code>Principal</code> that has been authenticated by
+ * the local SSO, or <code>null</code> if no authentication
+ * has been performed yet in this cluster node.
+ */
+ Principal getPrincipal()
+ {
+ return (this.principal);
+ }
+
+ /**
+ * Sets the <code>Principal</code> that has been authenticated by
+ * the SSO.
+ */
+ void setPrincipal(Principal principal)
+ {
+ this.principal = principal;
+ }
+
+ /**
+ * Returns the number of sessions associated with this SSO, either
+ * locally or remotely.
+ */
+ int getSessionCount()
+ {
+ return (sessions.length);
+ }
+
+ /**
+ * Gets the username provided by the user as part of the authentication
+ * process.
+ */
+ String getUsername()
+ {
+ return (this.username);
+ }
+
+
+ /**
+ * Updates the SingleSignOnEntry to reflect the latest security
+ * information associated with the caller.
+ *
+ * @param principal the <code>Principal</code> returned by the latest
+ * call to <code>Realm.authenticate</code>.
+ * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+ * DIGEST or FORM)
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ synchronized boolean updateCredentials(Principal principal, String authType,
+ String username, String password)
+ {
+
+ boolean changed =
+ (safeEquals(this.principal, principal)
+ || safeEquals(this.authType, authType)
+ || safeEquals(this.username, username)
+ || safeEquals(this.password, password));
+
+ this.principal = principal;
+ this.authType = authType;
+ this.username = username;
+ this.password = password;
+ this.canReauthenticate =
+ (Constants.BASIC_METHOD.equals(authType)
+ || Constants.FORM_METHOD.equals(authType));
+ return changed;
+ }
+
+ // ------------------------------------------------------- Private Methods
+
+ private boolean safeEquals(Object a, Object b)
+ {
+ return ((a == b)
+ || (a != null && a.equals(b))
+ || (b != null && b.equals(a)));
+ }
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/SingleSignOnEntry.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,1278 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.web.tomcat.service.sso;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Session;
+import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.cache.AbstractCacheListener;
+import org.jboss.cache.Cache;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.Region;
+import org.jboss.cache.RegionNotEmptyException;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.jmx.CacheJmxWrapperMBean;
+import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.util.NestedRuntimeException;
+
+/**
+ * An implementation of SSOClusterManager that uses a TreeCache
+ * to share SSO information between cluster nodes.
+ *
+ * @author Brian E. Stansberry
+ * @version $Revision: 59567 $ $Date: 2007-01-12 03:39:24 +0100 (ven., 12 janv. 2007) $
+ */
+public final class TreeCacheSSOClusterManager
+ extends AbstractCacheListener
+ implements SSOClusterManager
+{
+ // ------------------------------------------------------------- Constants
+
+ /**
+ * Final segment of any FQN that names a TreeCache node storing
+ * SSO credential information.
+ */
+ private static final String CREDENTIALS = "credentials";
+
+ /**
+ * First segment of any FQN that names a TreeCache node associated
+ * with an SSO
+ */
+ private static final String SSO = "SSO";
+
+ /**
+ * Final segment of any FQN that names a TreeCache node storing
+ * the set of Sessions associated with an SSO.
+ */
+ private static final String SESSIONS = "sessions";
+
+ /**
+ * Key under which data is stored to the TreeCache.
+ */
+ private static final String KEY = "key";
+
+ /**
+ * Default global value for the cacheName property
+ */
+ public static final String DEFAULT_GLOBAL_CACHE_NAME =
+ "jboss.cache:service=TomcatClusteringCache";
+
+ private static final Option GRAVITATE_OPTION = new Option();
+
+ static
+ {
+ GRAVITATE_OPTION.setForceDataGravitation(true);
+ }
+
+ // ------------------------------------------------------- Instance Fields
+
+ /**
+ * SSO id which the thread is currently storing to the cache
+ */
+ private ThreadLocal beingLocallyAdded = new ThreadLocal();
+
+ /**
+ * SSO id which a thread is currently removing from the cache
+ */
+ private ThreadLocal beingLocallyRemoved = new ThreadLocal();
+
+ /**
+ * SSO id which the thread is deregistering due to removal on another node
+ */
+ private ThreadLocal beingRemotelyRemoved = new ThreadLocal();
+
+ /**
+ * ObjectName of the TreeCache
+ */
+ private ObjectName cacheObjectName = null;
+
+ /**
+ * String version of the object name to use to access the TreeCache
+ */
+ private String cacheName = null;
+
+ /**
+ * The cache itself.
+ */
+ private Cache cache = null;
+
+ /**
+ * Transaction Manager
+ */
+ private TransactionManager tm = null;
+
+ /**
+ * The lifecycle event support for this component.
+ */
+ private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+ /**
+ * The Log-object for this class
+ */
+ private Logger log = Logger.getLogger(getClass().getName());;
+
+ /**
+ * Whether we are registered as a TreeCacheListener anywhere
+ */
+ private boolean registeredAsListener = false;
+
+ /**
+ * The MBean server we use to access our TreeCache
+ */
+ private MBeanServer server = null;
+
+ /**
+ * The SingleSignOn for which we are providing cluster support
+ */
+ private ClusteredSingleSignOn ssoValve = null;
+
+ /**
+ * Whether we have been started
+ */
+ private boolean started = false;
+
+ /**
+ * Whether a valid TreeCache is available for use
+ */
+ private boolean treeCacheAvailable = false;
+
+ /**
+ * Whether we have logged an error due to not having a valid cache
+ */
+ private boolean missingCacheErrorLogged = false;
+
+ /**
+ * Our node's address in the cluster.
+ */
+ private Serializable localAddress = null;
+
+ // ---------------------------------------------------------- Constructors
+
+
+ /**
+ * Creates a new TreeCacheSSOClusterManager
+ */
+ public TreeCacheSSOClusterManager()
+ {
+ // Find our MBeanServer
+ server = MBeanServerLocator.locateJBoss();
+ if (server == null)
+ server = MBeanServerLocator.locate();
+ }
+
+
+ /**
+ * Creates a new TreeCacheSSOClusterManager that works with the given
+ * MBeanServer. This constructor is only intended for use in unit testing.
+ */
+ public TreeCacheSSOClusterManager(MBeanServer server)
+ {
+ this.server = server;
+ }
+
+
+ // ------------------------------------------------------------ Properties
+
+ public String getCacheName()
+ {
+ return cacheName;
+ }
+
+ public void setCacheName(String objectName)
+ throws Exception
+ {
+ if (objectName == null)
+ {
+ setCacheObjectName(null);
+ }
+ else if (objectName.equals(cacheName) == false)
+ {
+ setCacheObjectName(new ObjectName(objectName));
+ }
+ }
+
+ public ObjectName getCacheObjectName()
+ {
+ return cacheObjectName;
+ }
+
+ public void setCacheObjectName(ObjectName objectName)
+ throws Exception
+ {
+ // If no change, do nothing
+ if ((objectName != null && objectName.equals(cacheObjectName))
+ || (cacheObjectName != null && cacheObjectName.equals(objectName))
+ || (objectName == null && cacheObjectName == null))
+ {
+ return;
+ }
+
+ removeAsCacheListener();
+ this.tm = null;
+
+ this.cacheObjectName = objectName;
+ this.cacheName = (objectName == null
+ ? null
+ : objectName.getCanonicalName());
+
+ if (false == isTreeCacheAvailable(true))
+ {
+ if (started)
+ {
+ logMissingCacheError();
+ }
+ else
+ {
+ // Just put an advice in the log
+ log.info("Cannot find TreeCache using " + cacheName + " -- tree" +
+ "CacheName must be set to point to a running TreeCache " +
+ "before ClusteredSingleSignOn can handle requests");
+ }
+ }
+ }
+
+ // ----------------------------------------------------- SSOClusterManager
+
+ /**
+ * Notify the cluster of the addition of a Session to an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param session the Session that has been added
+ */
+ public void addSession(String ssoId, Session session)
+ {
+ if (ssoId == null || session == null)
+ {
+ return;
+ }
+
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("addSession(): adding Session " + session.getId() +
+ " to cached session set for SSO " + ssoId);
+ }
+
+ Fqn fqn = getSessionsFqn(ssoId);
+ boolean doTx = false;
+ try
+ {
+ // Confirm we have a transaction manager; if not get it from TreeCache
+ // failure to find will throw an IllegalStateException
+ if (tm == null)
+ configureFromCache();
+
+ // Don't do anything if there is already a transaction
+ // context associated with this thread.
+ if(tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ Set sessions = getSessionSet(fqn, true);
+ sessions.add(new SessionAddress(session.getId(), localAddress));
+ putInTreeCache(fqn, sessions);
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if(doTx)
+ tm.setRollbackOnly();
+ }
+ catch (Exception ignored)
+ {
+ }
+ String sessId = (session == null ? "NULL" : session.getId());
+ log.error("caught exception adding session " + sessId +
+ " to SSO id " + ssoId, e);
+ }
+ finally
+ {
+ if (doTx)
+ endTransaction();
+ }
+ }
+
+
+ /**
+ * Gets the SingleSignOn valve for which this object is handling
+ * cluster communications.
+ *
+ * @return the <code>SingleSignOn</code> valve.
+ */
+ public ClusteredSingleSignOn getSingleSignOnValve()
+ {
+ return ssoValve;
+ }
+
+
+ /**
+ * Sets the SingleSignOn valve for which this object is handling
+ * cluster communications.
+ * <p><b>NOTE:</b> This method must be called before calls can be
+ * made to the other methods of this interface.
+ *
+ * @param valve a <code>SingleSignOn</code> valve.
+ */
+ public void setSingleSignOnValve(ClusteredSingleSignOn valve)
+ {
+ ssoValve = valve;
+ }
+
+
+ /**
+ * Notifies the cluster that a single sign on session has been terminated
+ * due to a user logout.
+ *
+ * @param ssoId
+ */
+ public void logout(String ssoId)
+ {
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ // Check whether we are already handling this removal
+ if (ssoId.equals(beingLocallyRemoved.get()))
+ {
+ return;
+ }
+
+ // Add this SSO to our list of in-process local removals so
+ // this.nodeRemoved() will ignore the removal
+ beingLocallyRemoved.set(ssoId);
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("Registering logout of SSO " + ssoId +
+ " in clustered cache");
+ }
+
+ Fqn fqn = getSingleSignOnFqn(ssoId);
+
+ try
+ {
+ removeFromTreeCache(fqn);
+ }
+ catch (Exception e)
+ {
+ log.error("Exception attempting to remove node " +
+ fqn.toString() + " from TreeCache", e);
+ }
+ finally
+ {
+ beingLocallyRemoved.set(null);
+ }
+ }
+
+
+ /**
+ * Queries the cluster for the existence of an SSO session with the given
+ * id, returning a <code>SingleSignOnEntry</code> if one is found.
+ *
+ * @param ssoId the id of the SSO session
+ * @return a <code>SingleSignOnEntry</code> created using information
+ * found on another cluster node, or <code>null</code> if no
+ * entry could be found.
+ */
+ public SingleSignOnEntry lookup(String ssoId)
+ {
+ if (!checkTreeCacheAvailable())
+ {
+ return null;
+ }
+
+ SingleSignOnEntry entry = null;
+ // Find the latest credential info from the cluster
+ Fqn fqn = getCredentialsFqn(ssoId);
+ try
+ {
+ SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn);
+ if (data != null)
+ {
+ entry = new SingleSignOnEntry(null,
+ data.getAuthType(),
+ data.getUsername(),
+ data.getPassword());
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("caught exception looking up SSOCredentials for SSO id " +
+ ssoId, e);
+ }
+ return entry;
+ }
+
+
+ /**
+ * Notifies the cluster of the creation of a new SSO entry.
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ public void register(String ssoId, String authType,
+ String username, String password)
+ {
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("Registering SSO " + ssoId + " in clustered cache");
+ }
+
+ storeSSOData(ssoId, authType, username, password);
+ }
+
+
+ /**
+ * Notify the cluster of the removal of a Session from an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param session the Session that has been removed
+ */
+ public void removeSession(String ssoId, Session session)
+ {
+ if (ssoId == null || session == null)
+ {
+ return;
+ }
+
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ // Check that this session removal is not due to our own deregistration
+ // of an SSO following receipt of a nodeRemoved() call
+ if (ssoId.equals(beingRemotelyRemoved.get()))
+ {
+ return;
+ }
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("removeSession(): removing Session " + session.getId() +
+ " from cached session set for SSO " + ssoId);
+ }
+
+ Fqn fqn = getSessionsFqn(ssoId);
+ boolean doTx = false;
+ boolean removing = false;
+ try
+ {
+ // Confirm we have a transaction manager; if not get it from TreeCache
+ // failure to find will throw an IllegalStateException
+ if (tm == null)
+ configureFromCache();
+
+ // Don't do anything if there is already a transaction
+ // context associated with this thread.
+ if(tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ Set sessions = getSessionSet(fqn, false);
+ if (sessions != null)
+ {
+ sessions.remove(new SessionAddress(session.getId(), localAddress));
+ if (sessions.size() == 0)
+ {
+ // No sessions left; remove node
+
+ // Add this SSO to our list of in-process local removals so
+ // this.nodeRemoved() will ignore the removal
+ removing = true;
+ beingLocallyRemoved.set(ssoId);
+ removeFromTreeCache(getSingleSignOnFqn(ssoId));
+ }
+ else
+ {
+ putInTreeCache(fqn, sessions);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if(doTx)
+ tm.setRollbackOnly();
+ }
+ catch (Exception x)
+ {
+ }
+
+ String sessId = (session == null ? "NULL" : session.getId());
+ log.error("caught exception removing session " + sessId +
+ " from SSO id " + ssoId, e);
+ }
+ finally
+ {
+ try
+ {
+ if (removing)
+ {
+ beingLocallyRemoved.set(null);
+ }
+ }
+ finally
+ {
+ if (doTx)
+ endTransaction();
+ }
+ }
+ }
+
+
+ /**
+ * Notifies the cluster of an update of the security credentials
+ * associated with an SSO session.
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ public void updateCredentials(String ssoId, String authType,
+ String username, String password)
+ {
+ if (!checkTreeCacheAvailable())
+ {
+ return;
+ }
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("Updating credentials for SSO " + ssoId +
+ " in clustered cache");
+ }
+
+ storeSSOData(ssoId, authType, username, password);
+ }
+
+
+ // ------------------------------------------------------ CacheListener
+
+ /**
+ * Extracts an SSO session id from the Fqn and uses it in an invocation of
+ * {@link ClusteredSingleSignOn#deregister(String) ClusteredSingleSignOn.deregister(String)}.
+ * <p/>
+ * Ignores invocations resulting from TreeCache changes originated by
+ * this object.
+ *
+ * @param fqn the fully-qualified name of the node that was removed
+ */
+ public void nodeRemoved(Fqn fqn)
+ {
+ String ssoId = getIdFromFqn(fqn);
+
+ if (ssoId == null)
+ return;
+
+ // Ignore messages generated by our own activity
+ if (ssoId.equals(beingLocallyRemoved.get()))
+ {
+ return;
+ }
+
+ beingRemotelyRemoved.set(ssoId);
+
+ try
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("received a node removed message for SSO " + ssoId);
+ }
+
+ ssoValve.deregister(ssoId);
+ }
+ finally
+ {
+ beingRemotelyRemoved.set(null);
+ }
+
+ }
+
+ /**
+ * Extracts an SSO session id from the Fqn and uses it in an invocation of
+ * {@link ClusteredSingleSignOn#update ClusteredSingleSignOn.update()}.
+ * <p/>
+ * Only responds to modifications of nodes whose FQN's final segment is
+ * "credentials".
+ * <p/>
+ * Ignores invocations resulting from TreeCache changes originated by
+ * this object.
+ * <p/>
+ * Ignores invocations for SSO session id's that are not registered
+ * with the local SingleSignOn valve.
+ *
+ * @param fqn the fully-qualified name of the node that was modified
+ */
+ public void nodeModified(Fqn fqn)
+ {
+ // We are only interested in changes to the CREDENTIALS node
+ if (CREDENTIALS.equals(getTypeFromFqn(fqn)) == false)
+ {
+ return;
+ }
+
+ String ssoId = getIdFromFqn(fqn); // won't be null or above check fails
+
+ // Ignore invocations that come as a result of our additions
+ if (ssoId.equals(beingLocallyAdded.get()))
+ {
+ return;
+ }
+
+ SingleSignOnEntry sso = ssoValve.localLookup(ssoId);
+ if (sso == null || sso.getCanReauthenticate())
+ {
+ // No reason to update
+ return;
+ }
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("received a credentials modified message for SSO " + ssoId);
+ }
+
+ // Put this SSO in the queue of those to be updated
+// credentialUpdater.enqueue(sso, ssoId);
+ try
+ {
+ SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn);
+ if (data != null)
+ {
+ // We want to release our read lock quickly, so get the needed
+ // data from the cache, commit the tx, and then use the data
+ String authType = data.getAuthType();
+ String username = data.getUsername();
+ String password = data.getPassword();
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("CredentialUpdater: Updating credentials for SSO " + sso);
+ }
+
+ synchronized (sso)
+ {
+ // Use the existing principal
+ Principal p = sso.getPrincipal();
+ sso.updateCredentials(p, authType, username, password);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("failed to update credentials for SSO " + ssoId, e);
+ }
+ }
+
+
+ // ------------------------------------------------------------- Lifecycle
+
+
+ /**
+ * Add a lifecycle event listener to this component.
+ *
+ * @param listener The listener to add
+ */
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.addLifecycleListener(listener);
+ }
+
+
+ /**
+ * Get the lifecycle listeners associated with this lifecycle. If this
+ * Lifecycle has no listeners registered, a zero-length array is returned.
+ */
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return lifecycle.findLifecycleListeners();
+ }
+
+
+ /**
+ * Remove a lifecycle event listener from this component.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.removeLifecycleListener(listener);
+ }
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called before any of the public
+ * methods of this component are utilized. It should also send a
+ * LifecycleEvent of type START_EVENT to any registered listeners.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ public void start() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (started)
+ {
+ throw new LifecycleException
+ ("TreeCacheSSOClusterManager already Started");
+ }
+
+ try
+ {
+ if (isTreeCacheAvailable(true))
+ {
+ integrateWithCache();
+ }
+ }
+ catch (Exception e)
+ {
+ throw new LifecycleException("Caught exception looking up " +
+ "TransactionManager from TreeCache", e);
+ }
+
+ started = true;
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(START_EVENT, null);
+ }
+
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component. It should also send a LifecycleEvent
+ * of type STOP_EVENT to any registered listeners.
+ *
+ * @throws LifecycleException if this component detects a fatal error
+ * that needs to be reported
+ */
+ public void stop() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (!started)
+ {
+ throw new LifecycleException
+ ("TreeCacheSSOClusterManager not Started");
+ }
+
+ started = false;
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+ }
+
+
+ // ------------------------------------------------------- Private Methods
+
+ private Object getFromTreeCache(Fqn fqn) throws Exception
+ {
+ InvocationContext ctx = cache.getInvocationContext();
+ Option existing = ctx.getOptionOverrides();
+ try
+ {
+ ctx.setOptionOverrides(GRAVITATE_OPTION);
+ return cache.get(fqn, KEY);
+ }
+ finally
+ {
+ ctx.setOptionOverrides(existing);
+ }
+ }
+
+ private Fqn getCredentialsFqn(String ssoid)
+ {
+ Object[] objs = new Object[]{SSO, ssoid, CREDENTIALS};
+ return new Fqn(objs);
+ }
+
+ private Fqn getSessionsFqn(String ssoid)
+ {
+ Object[] objs = new Object[]{SSO, ssoid, SESSIONS};
+ return new Fqn(objs);
+ }
+
+ private Fqn getSingleSignOnFqn(String ssoid)
+ {
+ Object[] objs = new Object[]{SSO, ssoid};
+ return new Fqn(objs);
+ }
+
+ /**
+ * Extracts an SSO session id from a fully qualified name object.
+ *
+ * @param fqn the Fully Qualified Name used by TreeCache
+ * @return the second element in the Fqn -- the SSO session id
+ */
+ private String getIdFromFqn(Fqn fqn)
+ {
+ String id = null;
+ if (fqn.size() > 1 && SSO.equals(fqn.get(0)))
+ {
+ id = (String) fqn.get(1);
+ }
+ return id;
+ }
+
+ private Set getSessionSet(Fqn fqn, boolean create)
+ throws Exception
+ {
+ Set sessions = (Set) getFromTreeCache(fqn);
+ if (create && sessions == null)
+ {
+ sessions = new HashSet();
+ }
+ return sessions;
+ }
+
+ /**
+ * Extracts the SSO tree cache node type from a fully qualified name
+ * object.
+ *
+ * @param fqn the Fully Qualified Name used by TreeCache
+ * @return the 3rd in the Fqn -- either
+ * {@link #CREDENTIALS CREDENTIALS} or {@link #SESSIONS SESSIONS},
+ * or <code>null</code> if <code>fqn</code> is not for an SSO.
+ */
+ private String getTypeFromFqn(Fqn fqn)
+ {
+ String type = null;
+ if (fqn.size() > 2 && SSO.equals(fqn.get(0)))
+ type = (String) fqn.get(2);
+ return type;
+ }
+
+ /**
+ * Obtains needed configuration information from the tree cache.
+ * Invokes "getTransactionManager" on the tree cache, caching the
+ * result or throwing an IllegalStateException if one is not found.
+ * Also get our cluster-wide unique local address from the cache.
+ *
+ * @throws Exception
+ */
+ private void configureFromCache() throws Exception
+ {
+ tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager();
+
+ if (tm == null)
+ {
+ throw new IllegalStateException("Cache does not have a " +
+ "transaction manager; please " +
+ "configure a valid " +
+ "TransactionManagerLookupClass");
+ }
+
+ // Find out our address
+ Object address = cache.getLocalAddress();
+ // In reality this is a JGroups IpAddress, but the API says
+ // "Object" so we have to be sure its Serializable
+ if (address instanceof Serializable)
+ localAddress = (Serializable) address;
+ else
+ localAddress = address.toString();
+ }
+
+ private void endTransaction()
+ {
+ try
+ {
+ if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
+ {
+ tm.commit();
+ }
+ else
+ {
+ tm.rollback();
+ }
+ }
+ catch (Exception e)
+ {
+ log.error(e);
+ throw new NestedRuntimeException("TreeCacheSSOClusterManager.endTransaction(): ", e);
+ }
+ }
+
+ /**
+ * Checks whether an MBean is registered under the value of property
+ * "cacheObjectName".
+ *
+ * @param forceCheck check for availability whether or not it has already
+ * been positively established
+ * @return <code>true</code> if property <code>cacheName</code> has been
+ * set and points to a registered MBean.
+ */
+ private synchronized boolean isTreeCacheAvailable(boolean forceCheck)
+ {
+ if (forceCheck || treeCacheAvailable == false)
+ {
+ boolean available = (cacheObjectName != null);
+ if (available)
+ {
+ Set s = server.queryMBeans(cacheObjectName, null);
+ available = s.size() > 0;
+ if (available)
+ {
+ try
+ {
+ // If Tomcat6 overrides the default cache name, it will do so
+ // after we are started. So we need to configure ourself here
+ // and throw an exception if there is a problem. Having this
+ // here also allows us to recover if our cache is started
+ // after we are
+ if (started)
+ integrateWithCache();
+ setMissingCacheErrorLogged(false);
+ }
+ catch (Exception e)
+ {
+ log.error("Caught exception configuring from cache " +
+ cacheObjectName, e);
+ available = false;
+ }
+ }
+ }
+ treeCacheAvailable = available;
+ }
+ return treeCacheAvailable;
+ }
+
+ private boolean checkTreeCacheAvailable()
+ {
+ boolean avail = isTreeCacheAvailable(false);
+ if (!avail)
+ logMissingCacheError();
+ return avail;
+ }
+
+ private void putInTreeCache(Fqn fqn, Object data) throws Exception
+ {
+ InvocationContext ctx = cache.getInvocationContext();
+ Option existing = ctx.getOptionOverrides();
+ try
+ {
+ ctx.setOptionOverrides(GRAVITATE_OPTION);
+ cache.put(fqn, KEY, data);
+ }
+ finally
+ {
+ ctx.setOptionOverrides(existing);
+ }
+ }
+
+ private void integrateWithCache() throws Exception
+ {
+ if (cache == null)
+ {
+ // Get the cache
+ CacheJmxWrapperMBean mbean = (CacheJmxWrapperMBean) MBeanProxyExt.create(CacheJmxWrapperMBean.class,
+ getCacheObjectName());
+ cache = mbean.getCache();
+
+ // Ensure we have a transaction manager and a cluster-wide unique address
+ configureFromCache();
+
+ // If the SSO region is inactive, activate it
+ activateCacheRegion();
+
+ registerAsCacheListener();
+
+ log.debug("Successfully integrated with cache service " + cacheObjectName);
+ }
+ }
+
+
+ /**
+ * If we are sharing a cache with HttpSession replication, the SSO
+ * region may not be active, so here we ensure it is.
+ *
+ * @throws Exception
+ */
+ private void activateCacheRegion() throws Exception
+ {
+ if (cache.getConfiguration().isInactiveOnStartup())
+ {
+ if (cache.getConfiguration().isUseRegionBasedMarshalling())
+ {
+ Region region =cache.getRegion(Fqn.fromString("/" + SSO), true);
+ try
+ {
+ region.activate();
+ }
+ catch (RegionNotEmptyException e)
+ {
+ log.debug(SSO + " region already active", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Invokes an operation on the JMX server to register ourself as a
+ * listener on the TreeCache service.
+ *
+ * @throws Exception
+ */
+ private void registerAsCacheListener() throws Exception
+ {
+ cache.addCacheListener(this);
+ registeredAsListener = true;
+ }
+
+
+ /**
+ * Invokes an operation on the JMX server to register ourself as a
+ * listener on the TreeCache service.
+ *
+ * @throws Exception
+ */
+ private void removeAsCacheListener() throws Exception
+ {
+ if (registeredAsListener && cache != null)
+ {
+ cache.removeCacheListener(this);
+ registeredAsListener = false;
+ }
+ }
+
+ private void removeFromTreeCache(Fqn fqn) throws Exception
+ {
+ InvocationContext ctx = cache.getInvocationContext();
+ Option existing = ctx.getOptionOverrides();
+ try
+ {
+ ctx.setOptionOverrides(GRAVITATE_OPTION);
+ cache.removeNode(fqn);
+ }
+ finally
+ {
+ ctx.setOptionOverrides(existing);
+ }
+ }
+
+ /**
+ * Stores the given data to the clustered cache in a tree branch whose FQN
+ * is the given SSO id. Stores the given credential data in a child node
+ * named "credentials". If parameter <code>storeSessions</code> is
+ * <code>true</code>, also stores an empty HashSet in a sibling node
+ * named "sessions". This HashSet will later be used to hold session ids
+ * associated with the SSO.
+ * <p/>
+ * Any items stored are stored under the key "key".
+ *
+ * @param ssoId the id of the SSO session
+ * @param authType the type of authenticator (BASIC, CLIENT-CERT, DIGEST
+ * or FORM) used to authenticate the SSO.
+ * @param username the username (if any) used for the authentication
+ * @param password the password (if any) used for the authentication
+ */
+ private void storeSSOData(String ssoId, String authType, String username,
+ String password)
+ {
+ SSOCredentials data = new SSOCredentials(authType, username, password);
+
+ // Add this SSO to our list of in-process local adds so
+ // this.nodeModified() will ignore the addition
+ beingLocallyAdded.set(ssoId);
+
+ try
+ {
+ putInTreeCache(getCredentialsFqn(ssoId), data);
+ }
+ catch (Exception e)
+ {
+ log.error("Exception attempting to add TreeCache nodes for SSO " +
+ ssoId, e);
+ }
+ finally
+ {
+ beingLocallyAdded.set(null);
+ }
+ }
+
+ private boolean isMissingCacheErrorLogged()
+ {
+ return missingCacheErrorLogged;
+ }
+
+ private void setMissingCacheErrorLogged(boolean missingCacheErrorLogged)
+ {
+ this.missingCacheErrorLogged = missingCacheErrorLogged;
+ }
+
+ private void logMissingCacheError()
+ {
+ StringBuffer msg = new StringBuffer("Cannot find TreeCache using ");
+ msg.append(getCacheName());
+ msg.append(" -- TreeCache must be started before ClusteredSingleSignOn ");
+ msg.append("can handle requests");
+
+ if (isMissingCacheErrorLogged())
+ {
+ // Just log it as a warning
+ log.warn(msg);
+ }
+ else
+ {
+ log.error(msg);
+ // Set a flag so we don't relog this error over and over
+ setMissingCacheErrorLogged(true);
+ }
+ }
+
+ // --------------------------------------------------------- Outer Classes
+
+ /**
+ * Private class used to store authentication credentials in the TreeCache.
+ * <p/>
+ * For security, password accessor is private.
+ */
+ public static class SSOCredentials
+ implements Serializable
+ {
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 5704877226920571663L;
+
+ private String authType = null;
+ private String password = null;
+ private String username = null;
+
+ /**
+ * Creates a new SSOCredentials.
+ *
+ * @param authType The authorization method used to authorize the
+ * SSO (BASIC, CLIENT-CERT, DIGEST, FORM or NONE).
+ * @param username The username of the user associated with the SSO
+ * @param password The password of the user associated with the SSO
+ */
+ private SSOCredentials(String authType, String username, String password)
+ {
+ this.authType = authType;
+ this.username = username;
+ this.password = password;
+ }
+
+ /**
+ * Gets the username of the user associated with the SSO.
+ *
+ * @return the username
+ */
+ public String getUsername()
+ {
+ return username;
+ }
+
+ /**
+ * Gets the authorization method used to authorize the SSO.
+ *
+ * @return "BASIC", "CLIENT-CERT", "DIGEST" or "FORM"
+ */
+ public String getAuthType()
+ {
+ return authType;
+ }
+
+ /**
+ * Gets the password of the user associated with the SSO.
+ *
+ * @return the password, or <code>null</code> if the authorization
+ * type was DIGEST or CLIENT-CERT.
+ */
+ private String getPassword()
+ {
+ return password;
+ }
+
+ } // end SSOCredentials
+
+ static class SessionAddress implements Serializable
+ {
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -3702932999380140004L;
+
+ Serializable address;
+ String sessionId;
+
+ SessionAddress(String sessionId, Serializable address)
+ {
+ this.sessionId = sessionId;
+ this.address = address;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+
+ if (!(obj instanceof SessionAddress))
+ return false;
+
+ SessionAddress other = (SessionAddress) obj;
+
+ return (sessionId.equals(other.sessionId)
+ && address.equals(other.address));
+ }
+
+ public int hashCode()
+ {
+ int total = (19 * 43) + sessionId.hashCode();
+ return ((total * 43) + address.hashCode());
+ }
+
+
+ }
+
+} // end TreeCacheSSOClusterManager
+
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+ <mbean name="ClusteredSingleSignOn"
+ description="A Valve that supports a 'single signon' user experience"
+ domain="Catalina"
+ group="Valve"
+ type="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn">
+
+ <attribute name="className"
+ description="Fully qualified class name of the managed object"
+ type="java.lang.String"
+ writeable="false"/>
+
+ <attribute name="cookieDomain"
+ description="Domain to which sso cookies should be scoped"
+ type="java.lang.String"/>
+
+ <attribute name="requireReauthentication"
+ description="Should we attempt to reauthenticate each request against the security Realm?"
+ type="boolean"/>
+
+ <attribute name="clusterManager"
+ description="SSOClusterManager to use for cluster support"
+ type="org.jboss.web.tomcat.service.sso.SSOClusterManager"/>
+
+ <attribute name="clusterManagerClass"
+ description="Fully qualified class name of the SSOClusterManager to use for cluster support"
+ type="java.lang.String"/>
+
+ <attribute name="treeCacheName"
+ description="The ObjectName of the TreeCache that this Valve should use for cluster communications.
+ Only relevant if clusterManagerClass is org.jboss.web.tomcat.service.sso.TreeCacheSSOClusterManager"
+ type="java.lang.String"/>
+
+ </mbean>
+
+</mbeans-descriptors>
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/sso/mbeans-descriptors.xml
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: trunk/tomcat/src/resources/META-INF/jboss-aop.xml
===================================================================
--- trunk/tomcat/src/resources/META-INF/jboss-aop.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/META-INF/jboss-aop.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -34,14 +34,14 @@
</introduction>
<!-- If a POJO has AopMarker annotation, it will be aspectized. -->
- <prepare expr="field(* @org.jboss.web.tomcat.tc5.session.AopMarker->*)" />
+ <prepare expr="field(* @org.jboss.web.tomcat.service.session.AopMarker->*)" />
<!-- Make any POJO with a AopMarker annotation as a Subject/Observerable -->
- <bind pointcut="set(* @org.jboss.web.tomcat.tc5.session.AopMarker->*)">
+ <bind pointcut="set(* @org.jboss.web.tomcat.service.session.AopMarker->*)">
<interceptor class="org.jboss.aspects.patterns.observable.SubjectInterceptor"/>
</bind>
- <introduction class="@org.jboss.web.tomcat.tc5.session.AopMarker">
+ <introduction class="@org.jboss.web.tomcat.service.session.AopMarker">
<mixin>
<interfaces>org.jboss.aspects.patterns.observable.Subject</interfaces>
<class>org.jboss.aspects.patterns.observable.SubjectImpl</class>
@@ -50,14 +50,14 @@
</introduction>
<!-- If a POJO has InstanceOfAopMarker annotation, it will be aspectized. -->
- <prepare expr="field(* $instanceof{@org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker}->*)" />
+ <prepare expr="field(* $instanceof{@org.jboss.web.tomcat.service.session.InstanceOfAopMarker}->*)" />
<!-- Make any POJO with a AopMarker annotation as a Subject/Observerable -->
- <bind pointcut="set(* @org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker->*)">
+ <bind pointcut="set(* @org.jboss.web.tomcat.service.session.InstanceOfAopMarker->*)">
<interceptor class="org.jboss.aspects.patterns.observable.SubjectInterceptor"/>
</bind>
- <introduction class="@org.jboss.web.tomcat.tc5.session.InstanceOfAopMarker">
+ <introduction class="@org.jboss.web.tomcat.service.session.InstanceOfAopMarker">
<mixin>
<interfaces>org.jboss.aspects.patterns.observable.Subject</interfaces>
<class>org.jboss.aspects.patterns.observable.SubjectImpl</class>
Added: trunk/tomcat/src/resources/jboss-web-cluster-beans.xml
===================================================================
--- trunk/tomcat/src/resources/jboss-web-cluster-beans.xml (rev 0)
+++ trunk/tomcat/src/resources/jboss-web-cluster-beans.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+ <!-- First we create a Configuration object for the cache -->
+ <bean name="TomcatClusteringCacheConfig" class="org.jboss.cache.config.Configuration">
+
+ <!-- Externally injected services -->
+ <property name="runtimeConfig">
+ <bean name="TomcatClusteringCacheRuntimeConfig" class="org.jboss.cache.config.RuntimeConfig">
+ <property name="transactionManager">
+ <bean name="TomcatClusteringBatchManager" class="org.jboss.cache.transaction.BatchModeTransactionManager"/>
+ </property>
+ <property name="muxChannelFactory"><inject bean="jgroups.mux:name=Multiplexer"/></property>
+ </bean>
+ </property>
+
+ <property name="multiplexerStack">${jboss.multiplexer.stack:udp}</property>
+
+ <property name="clusterName">${jboss.partition.name:DefaultPartition}-TomcatCache</property>
+
+ <property name="nodeLockingSchemeString" class="java.lang.String">PESSIMISTIC</property>
+ <property name="isolationLevelString" class="java.lang.String">REPEATABLE_READ</property>
+ <property name="cacheModeString">REPL_ASYNC</property>
+
+ <property name="syncReplTimeout">20000</property>
+ <property name="lockAcquisitionTimeout">15000</property>
+
+ <property name="useRegionBasedMarshalling">false</property>
+ <property name="inactiveOnStartup">false</property>
+
+ <property name="exposeManagementStatistics">true</property>
+
+ <property name="buddyReplicationConfig">
+ <bean name="TomcatClusteringBuddyReplicationConfig"
+ class="org.jboss.cache.config.BuddyReplicationConfig">
+
+ <property name="enabled">true</property>
+ <property name="buddyPoolName">default</property>
+ <property name="buddyCommunicationTimeout">2000</property>
+
+ <property name="autoDataGravitation">false</property>
+ <property name="dataGravitationRemoveOnFind">true</property>
+ <property name="dataGravitationSearchBackupTrees">true</property>
+
+ <property name="buddyLocatorConfig">
+ <bean name="TomcatClusteringBuddyLocatorConfig" class="org.jboss.cache.buddyreplication.NextMemberBuddyLocatorConfig">
+ <property name="numBuddies">1</property>
+ <property name="ignoreColocatedBuddies">true</property>
+ </bean>
+ </property>
+ </bean>
+ </property>
+ <property name="cacheLoaderConfig">
+ <bean name="TomcatClusteringCacheLoaderConfig"
+ class="org.jboss.cache.config.CacheLoaderConfig">
+ <property name="passivation">true</property>
+ <property name="preload">/</property>
+ <property name="shared">false</property>
+ <property name="individualCacheLoaderConfigs">
+ <list>
+ <bean name="TomcatClusteringFileCacheLoader" class="org.jboss.cache.loader.FileCacheLoaderConfig">
+ <property name="location">${jboss.server.data.dir}${/}tomcat-cluster</property>
+ <property name="async">true</property>
+ <property name="fetchPersistentState">true</property>
+ <property name="ignoreModifications">false</property>
+ </bean>
+ </list>
+ </property>
+ </bean>
+ </property>
+ </bean>
+
+ <!-- Now we use the above configuration to construct the cache itself -->
+ <bean name="TomcatClusteringCache" class="org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper">
+
+ <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.cache:service=TomcatClusteringCache", exposedInterface=org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean.class, registerDirectly=true)</annotation>
+
+ <property name="configuration"><inject bean="TomcatClusteringCacheConfig"/></property>
+ <property name="registerPlainCache">true</property>
+
+ </bean>
+
+</deployment>
\ No newline at end of file
Property changes on: trunk/tomcat/src/resources/jboss-web-cluster-beans.xml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/tomcat/src/resources/jboss-web-cluster-service.xml
===================================================================
--- trunk/tomcat/src/resources/jboss-web-cluster-service.xml (rev 0)
+++ trunk/tomcat/src/resources/jboss-web-cluster-service.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ===================================================================== -->
+<!-- -->
+<!-- Customized TreeCache Service Configuration for Tomcat 5 Clustering -->
+<!-- -->
+<!-- ===================================================================== -->
+
+<server>
+
+ <!-- ==================================================================== -->
+ <!-- Defines TreeCache configuration -->
+ <!-- ==================================================================== -->
+
+ <!-- Note we are using TreeCacheAop -->
+ <mbean code="org.jboss.cache.aop.TreeCacheAop"
+ name="jboss.cache:service=TomcatClusteringCache">
+
+ <depends>jboss:service=Naming</depends>
+ <depends>jboss:service=TransactionManager</depends>
+
+ <!--JGroups multiplexer configuration. Allows cache to use a shared
+ JGroups channel with other clustering services.
+ If you don't want to use the multiplexer, you can comment these
+ elements out and uncomment the ClusterConfig attribute below. -->
+ <depends>jgroups.mux:name=Multiplexer</depends>
+ <attribute name="MultiplexerService">jgroups.mux:name=Multiplexer</attribute>
+ <attribute name="MultiplexerStack">${jboss.multiplexer.stack:udp}</attribute>
+
+ <!-- Name of cluster. Needs to be the same for all clusters, in order
+ to find each other -->
+ <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
+
+ <!-- make sure to specify BatchModeTransactionManager only! -->
+ <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</attribute>
+
+ <!--
+ Isolation level : SERIALIZABLE
+ REPEATABLE_READ (default)
+ READ_COMMITTED
+ READ_UNCOMMITTED
+ NONE
+ -->
+ <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+ <!--
+ Valid modes are LOCAL, REPL_ASYNC and REPL_SYNC
+
+ If you use REPL_SYNC and a UDP-based JGroups channel
+ we recommend you comment out the FC (flow control)
+ protocol in the channel config.
+ -->
+ <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+ <!-- JGroups protocol stack config in XML format.
+ Not needed if multiplexer is used, so commented out by default.
+ If your CacheMode is set to REPL_SYNC we recommend you comment
+ out the FC (flow control) protocol
+
+ <attribute name="ClusterConfig">
+ <config>
+ <UDP mcast_addr="${jgroups.udp.mcast_addr:230.1.2.7}"
+ mcast_port="45578"
+ ucast_recv_buf_size="20000000"
+ ucast_send_buf_size="640000"
+ mcast_recv_buf_size="25000000"
+ mcast_send_buf_size="640000"
+ loopback="false"
+ max_bundle_size="64000"
+ max_bundle_timeout="30"
+ use_incoming_packet_handler="true"
+ use_outgoing_packet_handler="false"
+ ip_ttl="${jgroups.udp.ip_ttl:2}"
+ down_thread="false" up_thread="false"
+ enable_bundling="true"/>
+ <PING timeout="2000"
+ down_thread="false" up_thread="false" num_initial_members="3"/>
+ <MERGE2 max_interval="100000"
+ down_thread="false" up_thread="false" min_interval="20000"/>
+ <FD_SOCK down_thread="false" up_thread="false"/>
+ <FD shun="true" up_thread="false" down_thread="false"
+ timeout="20000" max_tries="5"/>
+ <VERIFY_SUSPECT timeout="1500"
+ up_thread="false" down_thread="false"/>
+ <pbcast.NAKACK max_xmit_size="60000"
+ use_mcast_xmit="false" gc_lag="50"
+ retransmit_timeout="300,600,1200,2400,4800"
+ down_thread="false" up_thread="false"
+ discard_delivered_msgs="true"/>
+ <UNICAST timeout="300,600,1200,2400,3600"
+ down_thread="false" up_thread="false"/>
+ <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
+ down_thread="false" up_thread="false"
+ max_bytes="400000"/>
+ <pbcast.GMS print_local_addr="true" join_timeout="3000"
+ down_thread="false" up_thread="false"
+ join_retry_timeout="2000" shun="true"/>
+ <FC max_credits="2000000" down_thread="false" up_thread="false"
+ min_threshold="0.10"/>
+ <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
+ <pbcast.STATE_TRANSFER down_thread="false" up_thread="false"/>
+ </config>
+ </attribute>
+ -->
+
+ <!-- JGroups protocol stack properties in String format.
+ Not needed if multiplexer is used or ClusterConfig property is set.
+ Can also be a URL, e.g. file:/home/bela/default.xml
+ <attribute name="ClusterProperties"></attribute>
+ -->
+
+
+
+ <!--
+ Number of milliseconds to wait until all responses for a
+ synchronous call have been received.
+ -->
+ <attribute name="SyncReplTimeout">20000</attribute>
+
+ <!-- Max number of milliseconds to wait for a lock acquisition -->
+ <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+ <!--
+ Indicate whether to the cache should unmarshall objects replicated
+ from other cluster nodes, or store them internally as a byte[]
+ until a web app requests them. Must be "true" if session replication
+ granularity "FIELD" is used in any webapp, otherwise "false" is
+ recommended.
+ -->
+ <attribute name="UseRegionBasedMarshalling">false</attribute>
+ <!--
+ Whether or not the entire tree is inactive upon startup, only
+ responding to replication messages after activateRegion() is
+ called to activate one or more parts of the when a webapp is
+ deployed. Must have the same value as "UseRegionBasedMarshalling".
+ -->
+ <attribute name="InactiveOnStartup">false</attribute>
+
+ <!-- Buddy Replication config.
+
+ See http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossCacheBuddyReplicationDesign
+ and the JBoss Cache docs for more on buddy replication.
+
+ By default, buddy replication is disabled.
+
+ Following are the configuration elements likely to be changed:
+
+ buddyReplicationEnabled true if you want buddy replication; false if data
+ should be replicated to all nodes in the cluster
+
+ numBuddies to how many backup nodes should each node replicate
+ its state
+
+ buddyPoolName allows logical subgrouping of nodes within the cluster;
+ if possible, buddies will be chosen from nodes in the
+ same buddy pool
+
+ Do not change the data gravitation related options.
+ -->
+ <attribute name="BuddyReplicationConfig">
+ <config>
+ <buddyReplicationEnabled>true</buddyReplicationEnabled>
+ <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
+ <buddyLocatorProperties>
+ numBuddies = 1
+ ignoreColocatedBuddies = true
+ </buddyLocatorProperties>
+
+ <buddyPoolName>default</buddyPoolName>
+ <buddyCommunicationTimeout>2000</buddyCommunicationTimeout>
+
+ <autoDataGravitation>false</autoDataGravitation>
+ <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
+ <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
+
+ </config>
+ </attribute>
+
+ <!-- passivation store config using jboss cache 1.3 + -->
+
+ <attribute name="CacheLoaderConfiguration">
+ <config>
+ <passivation>true</passivation>
+ <preload>/</preload>
+ <shared>false</shared>
+ <cacheloader>
+ <class>org.jboss.cache.loader.FileCacheLoader</class>
+ <properties>
+ location=/tmp
+ </properties>
+ <async>true</async>
+ <fetchPersistentState>true</fetchPersistentState>
+ <ignoreModifications>false</ignoreModifications>
+ </cacheloader>
+ </config>
+ </attribute>
+
+
+ </mbean>
+
+</server>
Property changes on: trunk/tomcat/src/resources/jboss-web-cluster-service.xml
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: trunk/tomcat/src/resources/server.xml
===================================================================
--- trunk/tomcat/src/resources/server.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/server.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -10,7 +10,7 @@
connectors.
-->
<Service name="jboss.web"
- className="org.jboss.web.tomcat.tc6.StandardService">
+ className="org.jboss.web.tomcat.service.StandardService">
<!-- A HTTP/1.1 Connector on port 8080 -->
<Connector port="8080" address="${jboss.bind.address}"
@@ -125,7 +125,7 @@
caching and replication across the cluster. The JBossCache
instance must be configured separately. By default, the valve
shares a JBossCache with the service that supports HttpSession
- replication. See the "tc6-cluster-service.xml" file in the
+ replication. See the "jboss-web-cluster-service.xml" file in the
server/all/deploy directory for cache configuration details.
Besides the attributes supported by the standard Tomcat
@@ -142,7 +142,7 @@
to support session replication.
-->
<!--
- <Valve className="org.jboss.web.tomcat.tc6.sso.ClusteredSingleSignOn" />
+ <Valve className="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn" />
-->
<!-- Check for unclosed connections and transaction terminated checks
@@ -151,7 +151,7 @@
Important: The dependency on the CachedConnectionManager
in META-INF/jboss-service.xml must be uncommented, too
TODO:
- <Valve className="org.jboss.web.tomcat.tc6.jca.CachedConnectionValve"
+ <Valve className="org.jboss.web.tomcat.service.jca.CachedConnectionValve"
cachedConnectionManagerObjectName="jboss.jca:service=CachedConnectionManager"
transactionManagerObjectName="jboss:service=TransactionManager" />
-->
Deleted: trunk/tomcat/src/resources/tc5-cluster-service.xml
===================================================================
--- trunk/tomcat/src/resources/tc5-cluster-service.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/tc5-cluster-service.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,194 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ===================================================================== -->
-<!-- -->
-<!-- Customized TreeCache Service Configuration for Tomcat 5 Clustering -->
-<!-- -->
-<!-- ===================================================================== -->
-
-<server>
-
- <!-- ==================================================================== -->
- <!-- Defines TreeCache configuration -->
- <!-- ==================================================================== -->
-
- <!-- Note we are using TreeCacheAop -->
- <mbean code="org.jboss.cache.aop.TreeCacheAop"
- name="jboss.cache:service=TomcatClusteringCache">
-
- <depends>jboss:service=Naming</depends>
- <depends>jboss:service=TransactionManager</depends>
- <!-- We need the AspectDeployer to deploy our FIELD granularity aspects -->
- <depends>jboss.aop:service=AspectDeployer</depends>
-
- <!--uncomment next three statements if using JGroups multiplexer -->
- <!--
- <depends>jgroups.mux:name=Multiplexer</depends>
- <attribute name="MultiplexerService">jgroups.mux:name=Multiplexer</attribute>
- <attribute name="MultiplexerStack">fc-fast-minimalthreads</attribute>
- -->
-
- <!-- make sure to specify BatchModeTransactionManager only! -->
- <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</attribute>
-
- <!--
- Isolation level : SERIALIZABLE
- REPEATABLE_READ (default)
- READ_COMMITTED
- READ_UNCOMMITTED
- NONE
- -->
- <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
-
- <!--
- Valid modes are LOCAL, REPL_ASYNC and REPL_SYNC
-
- If you use REPL_SYNC and a UDP-based ClusterConfig
- we recommend you comment out the FC (flow control)
- protocol in the ClusterConfig section below.
- -->
- <attribute name="CacheMode">REPL_ASYNC</attribute>
-
- <!-- Name of cluster. Needs to be the same for all clusters, in order
- to find each other
- -->
- <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
-
- <!-- JGroups protocol stack properties. Can also be a URL,
- e.g. file:/home/bela/default.xml
- <attribute name="ClusterProperties"></attribute>
- -->
-
- <attribute name="ClusterConfig">
- <config>
- <UDP mcast_addr="${jboss.partition.udpGroup:230.1.2.7}"
- mcast_port="45577"
- ucast_recv_buf_size="20000000"
- ucast_send_buf_size="640000"
- mcast_recv_buf_size="25000000"
- mcast_send_buf_size="640000"
- loopback="false"
- max_bundle_size="64000"
- max_bundle_timeout="30"
- use_incoming_packet_handler="true"
- use_outgoing_packet_handler="false"
- ip_ttl="2"
- down_thread="false" up_thread="false"
- enable_bundling="true"/>
- <PING timeout="2000"
- down_thread="false" up_thread="false" num_initial_members="3"/>
- <MERGE2 max_interval="100000"
- down_thread="false" up_thread="false" min_interval="20000"/>
- <FD_SOCK down_thread="false" up_thread="false"/>
- <FD shun="true" up_thread="true" down_thread="true"
- timeout="20000" max_tries="5"/>
- <VERIFY_SUSPECT timeout="1500"
- up_thread="false" down_thread="false"/>
- <pbcast.NAKACK max_xmit_size="60000"
- use_mcast_xmit="false" gc_lag="50"
- retransmit_timeout="100,200,300,600,1200,2400,4800"
- down_thread="false" up_thread="false"
- discard_delivered_msgs="true"/>
- <UNICAST timeout="300,600,1200,2400,3600"
- down_thread="false" up_thread="false"/>
- <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
- down_thread="false" up_thread="false"
- max_bytes="2100000"/>
- <pbcast.GMS print_local_addr="true" join_timeout="3000"
- down_thread="false" up_thread="false"
- join_retry_timeout="2000" shun="true"/>
- <!-- If your CacheMode is set to REPL_SYNC we recommend you
- comment out the FC (flow control) protocol -->
- <FC max_credits="10000000" down_thread="false" up_thread="false"
- min_threshold="0.20"/>
- <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
- <pbcast.STATE_TRANSFER down_thread="false" up_thread="false"/>
- </config>
- </attribute>
-
- <!--
- Number of milliseconds to wait until all responses for a
- synchronous call have been received.
- -->
- <attribute name="SyncReplTimeout">20000</attribute>
-
- <!-- Max number of milliseconds to wait for a lock acquisition -->
- <attribute name="LockAcquisitionTimeout">15000</attribute>
-
- <!--
- Indicate whether to the cache should unmarshall objects replicated
- from other cluster nodes, or store them internally as a byte[]
- until a web app requests them. Must be "true" if session replication
- granularity "FIELD" is used in any webapp, otherwise "false" is
- recommended.
- -->
- <attribute name="UseRegionBasedMarshalling">false</attribute>
- <!--
- Whether or not the entire tree is inactive upon startup, only
- responding to replication messages after activateRegion() is
- called to activate one or more parts of the when a webapp is
- deployed. Must have the same value as "UseRegionBasedMarshalling".
- -->
- <attribute name="InactiveOnStartup">false</attribute>
-
- <!-- Buddy Replication config.
-
- See http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossCacheBuddyReplicationDesign
- and the JBoss Cache docs for more on buddy replication.
-
- By default, buddy replication is disabled.
-
- Following are the configuration elements likely to be changed:
-
- buddyReplicationEnabled true if you want buddy replication; false if data
- should be replicated to all nodes in the cluster
-
- numBuddies to how many backup nodes should each node replicate
- its state
-
- buddyPoolName allows logical subgrouping of nodes within the cluster;
- if possible, buddies will be chosen from nodes in the
- same buddy pool
-
- Do not change the data gravitation related options.
- -->
- <attribute name="BuddyReplicationConfig">
- <config>
- <buddyReplicationEnabled>true</buddyReplicationEnabled>
- <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
- <buddyLocatorProperties>
- numBuddies = 1
- ignoreColocatedBuddies = true
- </buddyLocatorProperties>
-
- <buddyPoolName>default</buddyPoolName>
- <buddyCommunicationTimeout>2000</buddyCommunicationTimeout>
-
- <autoDataGravitation>false</autoDataGravitation>
- <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
- <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
-
- </config>
- </attribute>
-
- <!-- passivation store config using jboss cache 1.3 + -->
-
- <attribute name="CacheLoaderConfiguration">
- <config>
- <passivation>true</passivation>
- <preload>/</preload>
- <shared>false</shared>
- <cacheloader>
- <class>org.jboss.cache.loader.FileCacheLoader</class>
- <properties>
- location=/tmp
- </properties>
- <async>true</async>
- <fetchPersistentState>true</fetchPersistentState>
- <ignoreModifications>false</ignoreModifications>
- </cacheloader>
- </config>
- </attribute>
- </mbean>
-
-</server>
Deleted: trunk/tomcat/src/resources/tc6-cluster-beans.xml
===================================================================
--- trunk/tomcat/src/resources/tc6-cluster-beans.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/tc6-cluster-beans.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<deployment xmlns="urn:jboss:bean-deployer:2.0">
-
- <!-- First we create a Configuration object for the cache -->
- <bean name="TomcatClusteringCacheConfig" class="org.jboss.cache.config.Configuration">
-
- <!-- Externally injected services -->
- <property name="runtimeConfig">
- <bean name="TomcatClusteringCacheRuntimeConfig" class="org.jboss.cache.config.RuntimeConfig">
- <property name="transactionManager">
- <bean name="TomcatClusteringBatchManager" class="org.jboss.cache.transaction.BatchModeTransactionManager"/>
- </property>
- <property name="muxChannelFactory"><inject bean="jgroups.mux:name=Multiplexer"/></property>
- </bean>
- </property>
-
- <property name="multiplexerStack">${jboss.multiplexer.stack:udp}</property>
-
- <property name="clusterName">${jboss.partition.name:DefaultPartition}-TomcatCache</property>
-
- <property name="nodeLockingSchemeString" class="java.lang.String">PESSIMISTIC</property>
- <property name="isolationLevelString" class="java.lang.String">REPEATABLE_READ</property>
- <property name="cacheModeString">REPL_ASYNC</property>
-
- <property name="syncReplTimeout">20000</property>
- <property name="lockAcquisitionTimeout">15000</property>
-
- <property name="useRegionBasedMarshalling">false</property>
- <property name="inactiveOnStartup">false</property>
-
- <property name="exposeManagementStatistics">true</property>
-
- <property name="buddyReplicationConfig">
- <bean name="TomcatClusteringBuddyReplicationConfig"
- class="org.jboss.cache.config.BuddyReplicationConfig">
-
- <property name="enabled">true</property>
- <property name="buddyPoolName">default</property>
- <property name="buddyCommunicationTimeout">2000</property>
-
- <property name="autoDataGravitation">false</property>
- <property name="dataGravitationRemoveOnFind">true</property>
- <property name="dataGravitationSearchBackupTrees">true</property>
-
- <property name="buddyLocatorConfig">
- <bean name="TomcatClusteringBuddyLocatorConfig" class="org.jboss.cache.buddyreplication.NextMemberBuddyLocatorConfig">
- <property name="numBuddies">1</property>
- <property name="ignoreColocatedBuddies">true</property>
- </bean>
- </property>
- </bean>
- </property>
- <property name="cacheLoaderConfig">
- <bean name="TomcatClusteringCacheLoaderConfig"
- class="org.jboss.cache.config.CacheLoaderConfig">
- <property name="passivation">true</property>
- <property name="preload">/</property>
- <property name="shared">false</property>
- <property name="individualCacheLoaderConfigs">
- <list>
- <bean name="TomcatClusteringFileCacheLoader" class="org.jboss.cache.loader.FileCacheLoaderConfig">
- <property name="location">${jboss.server.data.dir}${/}tomcat-cluster</property>
- <property name="async">true</property>
- <property name="fetchPersistentState">true</property>
- <property name="ignoreModifications">false</property>
- </bean>
- </list>
- </property>
- </bean>
- </property>
- </bean>
-
- <!-- Now we use the above configuration to construct the cache itself -->
- <bean name="TomcatClusteringCache" class="org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper">
-
- <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.cache:service=TomcatClusteringCache", exposedInterface=org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean.class, registerDirectly=true)</annotation>
-
- <property name="configuration"><inject bean="TomcatClusteringCacheConfig"/></property>
- <property name="registerPlainCache">true</property>
-
- </bean>
-
-</deployment>
\ No newline at end of file
Deleted: trunk/tomcat/src/resources/tc6-cluster-service.xml
===================================================================
--- trunk/tomcat/src/resources/tc6-cluster-service.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/tc6-cluster-service.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,201 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!-- ===================================================================== -->
-<!-- -->
-<!-- Customized TreeCache Service Configuration for Tomcat 5 Clustering -->
-<!-- -->
-<!-- ===================================================================== -->
-
-<server>
-
- <!-- ==================================================================== -->
- <!-- Defines TreeCache configuration -->
- <!-- ==================================================================== -->
-
- <!-- Note we are using TreeCacheAop -->
- <mbean code="org.jboss.cache.aop.TreeCacheAop"
- name="jboss.cache:service=TomcatClusteringCache">
-
- <depends>jboss:service=Naming</depends>
- <depends>jboss:service=TransactionManager</depends>
-
- <!--JGroups multiplexer configuration. Allows cache to use a shared
- JGroups channel with other clustering services.
- If you don't want to use the multiplexer, you can comment these
- elements out and uncomment the ClusterConfig attribute below. -->
- <depends>jgroups.mux:name=Multiplexer</depends>
- <attribute name="MultiplexerService">jgroups.mux:name=Multiplexer</attribute>
- <attribute name="MultiplexerStack">${jboss.multiplexer.stack:udp}</attribute>
-
- <!-- Name of cluster. Needs to be the same for all clusters, in order
- to find each other -->
- <attribute name="ClusterName">Tomcat-${jboss.partition.name:Cluster}</attribute>
-
- <!-- make sure to specify BatchModeTransactionManager only! -->
- <attribute name="TransactionManagerLookupClass">org.jboss.cache.BatchModeTransactionManagerLookup</attribute>
-
- <!--
- Isolation level : SERIALIZABLE
- REPEATABLE_READ (default)
- READ_COMMITTED
- READ_UNCOMMITTED
- NONE
- -->
- <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
-
- <!--
- Valid modes are LOCAL, REPL_ASYNC and REPL_SYNC
-
- If you use REPL_SYNC and a UDP-based JGroups channel
- we recommend you comment out the FC (flow control)
- protocol in the channel config.
- -->
- <attribute name="CacheMode">REPL_ASYNC</attribute>
-
- <!-- JGroups protocol stack config in XML format.
- Not needed if multiplexer is used, so commented out by default.
- If your CacheMode is set to REPL_SYNC we recommend you comment
- out the FC (flow control) protocol
-
- <attribute name="ClusterConfig">
- <config>
- <UDP mcast_addr="${jgroups.udp.mcast_addr:230.1.2.7}"
- mcast_port="45578"
- ucast_recv_buf_size="20000000"
- ucast_send_buf_size="640000"
- mcast_recv_buf_size="25000000"
- mcast_send_buf_size="640000"
- loopback="false"
- max_bundle_size="64000"
- max_bundle_timeout="30"
- use_incoming_packet_handler="true"
- use_outgoing_packet_handler="false"
- ip_ttl="${jgroups.udp.ip_ttl:2}"
- down_thread="false" up_thread="false"
- enable_bundling="true"/>
- <PING timeout="2000"
- down_thread="false" up_thread="false" num_initial_members="3"/>
- <MERGE2 max_interval="100000"
- down_thread="false" up_thread="false" min_interval="20000"/>
- <FD_SOCK down_thread="false" up_thread="false"/>
- <FD shun="true" up_thread="false" down_thread="false"
- timeout="20000" max_tries="5"/>
- <VERIFY_SUSPECT timeout="1500"
- up_thread="false" down_thread="false"/>
- <pbcast.NAKACK max_xmit_size="60000"
- use_mcast_xmit="false" gc_lag="50"
- retransmit_timeout="300,600,1200,2400,4800"
- down_thread="false" up_thread="false"
- discard_delivered_msgs="true"/>
- <UNICAST timeout="300,600,1200,2400,3600"
- down_thread="false" up_thread="false"/>
- <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
- down_thread="false" up_thread="false"
- max_bytes="400000"/>
- <pbcast.GMS print_local_addr="true" join_timeout="3000"
- down_thread="false" up_thread="false"
- join_retry_timeout="2000" shun="true"/>
- <FC max_credits="2000000" down_thread="false" up_thread="false"
- min_threshold="0.10"/>
- <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
- <pbcast.STATE_TRANSFER down_thread="false" up_thread="false"/>
- </config>
- </attribute>
- -->
-
- <!-- JGroups protocol stack properties in String format.
- Not needed if multiplexer is used or ClusterConfig property is set.
- Can also be a URL, e.g. file:/home/bela/default.xml
- <attribute name="ClusterProperties"></attribute>
- -->
-
-
-
- <!--
- Number of milliseconds to wait until all responses for a
- synchronous call have been received.
- -->
- <attribute name="SyncReplTimeout">20000</attribute>
-
- <!-- Max number of milliseconds to wait for a lock acquisition -->
- <attribute name="LockAcquisitionTimeout">15000</attribute>
-
- <!--
- Indicate whether to the cache should unmarshall objects replicated
- from other cluster nodes, or store them internally as a byte[]
- until a web app requests them. Must be "true" if session replication
- granularity "FIELD" is used in any webapp, otherwise "false" is
- recommended.
- -->
- <attribute name="UseRegionBasedMarshalling">false</attribute>
- <!--
- Whether or not the entire tree is inactive upon startup, only
- responding to replication messages after activateRegion() is
- called to activate one or more parts of the when a webapp is
- deployed. Must have the same value as "UseRegionBasedMarshalling".
- -->
- <attribute name="InactiveOnStartup">false</attribute>
-
- <!-- Buddy Replication config.
-
- See http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossCacheBuddyReplicationDesign
- and the JBoss Cache docs for more on buddy replication.
-
- By default, buddy replication is disabled.
-
- Following are the configuration elements likely to be changed:
-
- buddyReplicationEnabled true if you want buddy replication; false if data
- should be replicated to all nodes in the cluster
-
- numBuddies to how many backup nodes should each node replicate
- its state
-
- buddyPoolName allows logical subgrouping of nodes within the cluster;
- if possible, buddies will be chosen from nodes in the
- same buddy pool
-
- Do not change the data gravitation related options.
- -->
- <attribute name="BuddyReplicationConfig">
- <config>
- <buddyReplicationEnabled>true</buddyReplicationEnabled>
- <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
- <buddyLocatorProperties>
- numBuddies = 1
- ignoreColocatedBuddies = true
- </buddyLocatorProperties>
-
- <buddyPoolName>default</buddyPoolName>
- <buddyCommunicationTimeout>2000</buddyCommunicationTimeout>
-
- <autoDataGravitation>false</autoDataGravitation>
- <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
- <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
-
- </config>
- </attribute>
-
- <!-- passivation store config using jboss cache 1.3 + -->
-
- <attribute name="CacheLoaderConfiguration">
- <config>
- <passivation>true</passivation>
- <preload>/</preload>
- <shared>false</shared>
- <cacheloader>
- <class>org.jboss.cache.loader.FileCacheLoader</class>
- <properties>
- location=/tmp
- </properties>
- <async>true</async>
- <fetchPersistentState>true</fetchPersistentState>
- <ignoreModifications>false</ignoreModifications>
- </cacheloader>
- </config>
- </attribute>
-
-
- </mbean>
-
-</server>
Modified: trunk/tomcat/src/resources/war-deployers-all-beans.xml
===================================================================
--- trunk/tomcat/src/resources/war-deployers-all-beans.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/war-deployers-all-beans.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,208 +1,208 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-Web application deployers
-$Id:$
--->
-<deployment xmlns="urn:jboss:bean-deployer:2.0">
-
- <!-- Tomcat
- <bean name="VFSClassLoader" class="org.jboss.virtual.classloading.VFSClassLoader">
- <constructor factoryClass="org.jboss.virtual.classloading.VFSClassLoaderFactory"
- factoryMethod="newClassLoader">
- <parameter class="java.net.URI">${jboss.server.home.url}/deployers/jbossweb-tomcat6.deployer/</parameter>
- <parameter>
- <array class="[Ljava.lang.String;" elementClass="java.lang.String">
- <value>*.jar</value>
- <value>jsf-libs/*.jar</value>
- </array>
- </parameter>
- </constructor>
- </bean>
- -->
-
- <!-- web.xml parsing deployer -->
- <bean name="WebAppParsingDeployer" class="org.jboss.deployment.WebAppParsingDeployer">
- <install bean="MainDeployer" method="addDeployer">
- <parameter><this/></parameter>
- </install>
- <uninstall bean="MainDeployer" method="removeDeployer">
- <parameter><this/></parameter>
- </uninstall>
- <property name="type">war</property>
- <property name="relativeOrder">2000</property>
- </bean>
- <bean name="JBossWebAppParsingDeployer" class="org.jboss.deployment.JBossWebAppParsingDeployer">
- <install bean="MainDeployer" method="addDeployer">
- <parameter><this/></parameter>
- </install>
- <uninstall bean="MainDeployer" method="removeDeployer">
- <parameter><this/></parameter>
- </uninstall>
- <property name="type">war</property>
- <property name="relativeOrder">2001</property>
- </bean>
-
- <bean name="WarDeployer" class="org.jboss.web.tomcat.tc6.deployers.TomcatDeployer">
- <install bean="MainDeployer" method="addDeployer">
- <parameter><this/></parameter>
- </install>
- <uninstall bean="MainDeployer" method="removeDeployer">
- <parameter><this/></parameter>
- </uninstall>
-
- <property name="type">war</property>
- <!-- You can configure a set of authenticators keyed by http-auth method
- used. This will apply the same set of authenticators across all web
- applications. You can override the set of authenticators at the web
- application level by adding <authenticators> element to the respective
- jboss-web.xml
- -->
- <property name="authenticators">
- <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String">
- <entry>
- <key>BASIC</key>
- <value>org.apache.catalina.authenticator.BasicAuthenticator</value>
- </entry>
- <entry>
- <key>CLIENT-CERT</key>
- <value>org.apache.catalina.authenticator.SSLAuthenticator</value>
- </entry>
- <entry>
- <key>DIGEST</key>
- <value>org.apache.catalina.authenticator.DigestAuthenticator</value>
- </entry>
- <entry>
- <key>FORM</key>
- <value>org.apache.catalina.authenticator.FormAuthenticator</value>
- </entry>
- <entry>
- <key>NONE</key>
- <value>org.apache.catalina.authenticator.NonLoginAuthenticator</value>
- </entry>
- </map>
- </property>
-
- <!-- The JAAS security domain to use in the absense of an explicit
- security-domain specification in the war WEB-INF/jboss-web.xml
- -->
- <property name="defaultSecurityDomain">java:/jaas/other</property>
-
- <!-- Get the flag indicating if the normal Java2 parent first class
- loading model should be used over the servlet 2.3 web container first
- model.
- -->
- <property name="java2ClassLoadingCompliance">false</property>
- <!-- A flag indicating if the JBoss Loader should be used. This loader
- uses a unified class loader as the class loader rather than the tomcat
- specific class loader.
- The default is false to ensure that wars have isolated class loading
- for duplicate jars and jsp files.
- -->
- <property name="useJBossWebLoader">false</property>
- <!-- The list of package prefixes that should not be loaded without
- delegating to the parent class loader before trying the web app
- class loader. The packages listed here are those tha are used by
- the web container implementation and cannot be overriden. The format
- is a comma separated list of the package names. There cannot be any
- whitespace between the package prefixes.
- This setting only applies when UseJBossWebLoader=false.
- -->
- <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
-
- <property name="lenientEjbLink">true</property>
-
- <!--Flag to delete the Work Dir on Context Destroy -->
- <property name="deleteWorkDirOnContextDestroy">false</property>
-
- <!--
- Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
- - org.jboss.web.tomcat.tc6.session.JBossCacheManager
- -->
- <property name="managerClass">org.jboss.web.tomcat.tc6.session.JBossCacheManager</property>
-
- <!-- The name of the request property under with the authenticated JAAS
- Subject is stored on successful authentication. If null or empty then
- the Subject will not be stored.
- -->
- <!--
- <property name="subjectAttributeName">j_subject</property>
- -->
-
- <!-- The SessionIdAlphabet is the set of characters used to create a session Id
- It must be made up of exactly 65 unique characters
- <property name="sessionIdAlphabet">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*</property>
- -->
-
- <!--
- *******************************************************
- ****************** CLUSTERING *************************
- *******************************************************
- In order to activate HTTP Session clustering for Tomcat
- make sure you run JBoss's "all" configuration i.e.
- "run -c all"
- (the default configuration doesn't contain clustering)
-
- Furthermore, you may change SnapshotMode and
- SnapshotInterval propertys below to indicate when to
- synchronize changes with the other node(s).
-
- If you use Apache+mod_jk(2) you will most probably use
- the AJP1.3 connector below. Thus, if you so wish,
- you may comment (i.e. deactivate) the HTTP connector
- as it won't be used anymore.
-
- *******************************************************
- *******************************************************
- *******************************************************
- -->
-
- <!--
- If you are using clustering, the following two propertys
- define when the sessions are replicated to the other nodes.
- The default value, "instant", synchronously replicates changes
- to the other nodes at the end of requests. In this case, the
- "SnapshotInterval" property is not used.
- The "interval" mode, in association with the "SnapshotInterval"
- property, indicates that modified sessions will only be replicated
- every "SnapshotInterval" milliseconds at most.
-
- Note that this property is not in effect if the replication-granularity
- is set to FIELD. If it is FIELD, it will be per http request (that is,
- "instant" mode.)
- -->
- <property name="snapshotMode">instant</property> <!-- you may switch to "interval" -->
- <property name="snapshotInterval">2000</property>
-
- <!--
- Whether to use MOD_JK(2) for load balancing with sticky session
- combined with JvmRoute. If set to true, it will insert a JvmRouteValve
- to intercept every request and replace the JvmRoute if it detects a
- failover. In addition, you will need to set the JvmRoute inside
- Tomcat, e.g.,
- Engine name="jboss.web" jmvRoute="Node1" defaultHost="localhost"
- in server.xml.
-
- For clustering purpose only.
- -->
- <property name="useJK">false</property>
-
- <property name="domain">jboss.web</property>
- <property name="securityManagerService">
- <inject bean="jboss.security:service=JaasSecurityManager" />
- </property>
- <!-- TODO determine how to express this dependency
- <depends>jboss.cache:service=TomcatClusteringCache</depends>
- -->
-
- <depends>jboss:service=TransactionManager</depends>
- <depends>SecurityDeployer</depends>
-
- <!-- Only needed if the org.jboss.web.tomcat.tc6.jca.CachedConnectionValve
-TODO: injection
- is enabled in the tomcat server.xml file.
- <depends>jboss.jca:service=CachedConnectionManager</depends>
- -->
- </bean>
-
-</deployment>
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Web application deployers
+$Id:$
+-->
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+ <!-- Tomcat
+ <bean name="VFSClassLoader" class="org.jboss.virtual.classloading.VFSClassLoader">
+ <constructor factoryClass="org.jboss.virtual.classloading.VFSClassLoaderFactory"
+ factoryMethod="newClassLoader">
+ <parameter class="java.net.URI">${jboss.server.home.url}/deployers/jbossweb-tomcat6.deployer/</parameter>
+ <parameter>
+ <array class="[Ljava.lang.String;" elementClass="java.lang.String">
+ <value>*.jar</value>
+ <value>jsf-libs/*.jar</value>
+ </array>
+ </parameter>
+ </constructor>
+ </bean>
+ -->
+
+ <!-- web.xml parsing deployer -->
+ <bean name="WebAppParsingDeployer" class="org.jboss.deployment.WebAppParsingDeployer">
+ <install bean="MainDeployer" method="addDeployer">
+ <parameter><this/></parameter>
+ </install>
+ <uninstall bean="MainDeployer" method="removeDeployer">
+ <parameter><this/></parameter>
+ </uninstall>
+ <property name="type">war</property>
+ <property name="relativeOrder">2000</property>
+ </bean>
+ <bean name="JBossWebAppParsingDeployer" class="org.jboss.deployment.JBossWebAppParsingDeployer">
+ <install bean="MainDeployer" method="addDeployer">
+ <parameter><this/></parameter>
+ </install>
+ <uninstall bean="MainDeployer" method="removeDeployer">
+ <parameter><this/></parameter>
+ </uninstall>
+ <property name="type">war</property>
+ <property name="relativeOrder">2001</property>
+ </bean>
+
+ <bean name="WarDeployer" class="org.jboss.web.tomcat.service.deployers.TomcatDeployer">
+ <install bean="MainDeployer" method="addDeployer">
+ <parameter><this/></parameter>
+ </install>
+ <uninstall bean="MainDeployer" method="removeDeployer">
+ <parameter><this/></parameter>
+ </uninstall>
+
+ <property name="type">war</property>
+ <!-- You can configure a set of authenticators keyed by http-auth method
+ used. This will apply the same set of authenticators across all web
+ applications. You can override the set of authenticators at the web
+ application level by adding <authenticators> element to the respective
+ jboss-web.xml
+ -->
+ <property name="authenticators">
+ <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String">
+ <entry>
+ <key>BASIC</key>
+ <value>org.apache.catalina.authenticator.BasicAuthenticator</value>
+ </entry>
+ <entry>
+ <key>CLIENT-CERT</key>
+ <value>org.apache.catalina.authenticator.SSLAuthenticator</value>
+ </entry>
+ <entry>
+ <key>DIGEST</key>
+ <value>org.apache.catalina.authenticator.DigestAuthenticator</value>
+ </entry>
+ <entry>
+ <key>FORM</key>
+ <value>org.apache.catalina.authenticator.FormAuthenticator</value>
+ </entry>
+ <entry>
+ <key>NONE</key>
+ <value>org.apache.catalina.authenticator.NonLoginAuthenticator</value>
+ </entry>
+ </map>
+ </property>
+
+ <!-- The JAAS security domain to use in the absense of an explicit
+ security-domain specification in the war WEB-INF/jboss-web.xml
+ -->
+ <property name="defaultSecurityDomain">java:/jaas/other</property>
+
+ <!-- Get the flag indicating if the normal Java2 parent first class
+ loading model should be used over the servlet 2.3 web container first
+ model.
+ -->
+ <property name="java2ClassLoadingCompliance">false</property>
+ <!-- A flag indicating if the JBoss Loader should be used. This loader
+ uses a unified class loader as the class loader rather than the tomcat
+ specific class loader.
+ The default is false to ensure that wars have isolated class loading
+ for duplicate jars and jsp files.
+ -->
+ <property name="useJBossWebLoader">false</property>
+ <!-- The list of package prefixes that should not be loaded without
+ delegating to the parent class loader before trying the web app
+ class loader. The packages listed here are those tha are used by
+ the web container implementation and cannot be overriden. The format
+ is a comma separated list of the package names. There cannot be any
+ whitespace between the package prefixes.
+ This setting only applies when UseJBossWebLoader=false.
+ -->
+ <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
+
+ <property name="lenientEjbLink">true</property>
+
+ <!--Flag to delete the Work Dir on Context Destroy -->
+ <property name="deleteWorkDirOnContextDestroy">false</property>
+
+ <!--
+ Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
+ - org.jboss.web.tomcat.service.session.JBossCacheManager
+ -->
+ <property name="managerClass">org.jboss.web.tomcat.service.session.JBossCacheManager</property>
+
+ <!-- The name of the request property under with the authenticated JAAS
+ Subject is stored on successful authentication. If null or empty then
+ the Subject will not be stored.
+ -->
+ <!--
+ <property name="subjectAttributeName">j_subject</property>
+ -->
+
+ <!-- The SessionIdAlphabet is the set of characters used to create a session Id
+ It must be made up of exactly 65 unique characters
+ <property name="sessionIdAlphabet">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*</property>
+ -->
+
+ <!--
+ *******************************************************
+ ****************** CLUSTERING *************************
+ *******************************************************
+ In order to activate HTTP Session clustering for Tomcat
+ make sure you run JBoss's "all" configuration i.e.
+ "run -c all"
+ (the default configuration doesn't contain clustering)
+
+ Furthermore, you may change SnapshotMode and
+ SnapshotInterval propertys below to indicate when to
+ synchronize changes with the other node(s).
+
+ If you use Apache+mod_jk(2) you will most probably use
+ the AJP1.3 connector below. Thus, if you so wish,
+ you may comment (i.e. deactivate) the HTTP connector
+ as it won't be used anymore.
+
+ *******************************************************
+ *******************************************************
+ *******************************************************
+ -->
+
+ <!--
+ If you are using clustering, the following two propertys
+ define when the sessions are replicated to the other nodes.
+ The default value, "instant", synchronously replicates changes
+ to the other nodes at the end of requests. In this case, the
+ "SnapshotInterval" property is not used.
+ The "interval" mode, in association with the "SnapshotInterval"
+ property, indicates that modified sessions will only be replicated
+ every "SnapshotInterval" milliseconds at most.
+
+ Note that this property is not in effect if the replication-granularity
+ is set to FIELD. If it is FIELD, it will be per http request (that is,
+ "instant" mode.)
+ -->
+ <property name="snapshotMode">instant</property> <!-- you may switch to "interval" -->
+ <property name="snapshotInterval">2000</property>
+
+ <!--
+ Whether to use MOD_JK(2) for load balancing with sticky session
+ combined with JvmRoute. If set to true, it will insert a JvmRouteValve
+ to intercept every request and replace the JvmRoute if it detects a
+ failover. In addition, you will need to set the JvmRoute inside
+ Tomcat, e.g.,
+ Engine name="jboss.web" jmvRoute="Node1" defaultHost="localhost"
+ in server.xml.
+
+ For clustering purpose only.
+ -->
+ <property name="useJK">false</property>
+
+ <property name="domain">jboss.web</property>
+ <property name="securityManagerService">
+ <inject bean="jboss.security:service=JaasSecurityManager" />
+ </property>
+ <!-- TODO determine how to express this dependency
+ <depends>jboss.cache:service=TomcatClusteringCache</depends>
+ -->
+
+ <depends>jboss:service=TransactionManager</depends>
+ <depends>SecurityDeployer</depends>
+
+ <!-- Only needed if the org.jboss.web.tomcat.service.jca.CachedConnectionValve
+TODO: injection
+ is enabled in the tomcat server.xml file.
+ <depends>jboss.jca:service=CachedConnectionManager</depends>
+ -->
+ </bean>
+
+</deployment>
Modified: trunk/tomcat/src/resources/war-deployers-beans.xml
===================================================================
--- trunk/tomcat/src/resources/war-deployers-beans.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/war-deployers-beans.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -1,238 +1,238 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-Web application deployers
-$Id:$
--->
-<deployment xmlns="urn:jboss:bean-deployer:2.0">
-
- <!-- Tomcat
- <bean name="VFSClassLoader" class="org.jboss.virtual.classloading.VFSClassLoader">
- <constructor factoryClass="org.jboss.virtual.classloading.VFSClassLoaderFactory"
- factoryMethod="newClassLoader">
- <parameter class="java.net.URI">${jboss.server.home.url}/deployers/jbossweb-tomcat6.deployer/</parameter>
- <parameter>
- <array class="[Ljava.lang.String;" elementClass="java.lang.String">
- <value>*.jar</value>
- <value>jsf-libs/*.jar</value>
- </array>
- </parameter>
- </constructor>
- </bean>
- -->
-
- <!-- web.xml parsing deployer -->
- <bean name="WebAppParsingDeployer" class="org.jboss.deployment.WebAppParsingDeployer">
- <install bean="MainDeployer" method="addDeployer">
- <parameter><this/></parameter>
- </install>
- <uninstall bean="MainDeployer" method="removeDeployer">
- <parameter><this/></parameter>
- </uninstall>
- <property name="type">war</property>
- <property name="relativeOrder">2000</property>
- </bean>
- <bean name="JBossWebAppParsingDeployer" class="org.jboss.deployment.JBossWebAppParsingDeployer">
- <install bean="MainDeployer" method="addDeployer">
- <parameter><this/></parameter>
- </install>
- <uninstall bean="MainDeployer" method="removeDeployer">
- <parameter><this/></parameter>
- </uninstall>
- <property name="type">war</property>
- <property name="relativeOrder">2001</property>
- </bean>
-
- <!-- Allow for war local class loaders: in testing
- <bean name="WarClassLoaderDeployer" class="org.jboss.web.tomcat.tc6.deployers.WarClassLoaderDeployer">
- <install bean="MainDeployer" method="addDeployer">
- <parameter><this/></parameter>
- </install>
- <uninstall bean="MainDeployer" method="removeDeployer">
- <parameter><this/></parameter>
- </uninstall>
- <!- - CLASSLOADER_DEPLOYER - 1 - ->
- <property name="relativeOrder">3999</property>
- <!- - A flag indicating if the JBoss Loader should be used. This loader
- uses a unified class loader as the class loader rather than the tomcat
- specific class loader.
- The default is false to ensure that wars have isolated class loading
- for duplicate jars and jsp files.
- - ->
- <property name="useJBossWebLoader">false</property>
- <!- - The list of package prefixes that should not be loaded without
- delegating to the parent class loader before trying the web app
- class loader. The packages listed here are those tha are used by
- the web container implementation and cannot be overriden. The format
- is a comma separated list of the package names. There cannot be any
- whitespace between the package prefixes.
- This setting only applies when UseJBossWebLoader=false.
- - ->
- <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
- </bean>
- -->
-
- <!-- The WebMetaData to service mbean deployer -->
- <bean name="WarDeployer" class="org.jboss.web.tomcat.tc6.deployers.TomcatDeployer">
- <install bean="MainDeployer" method="addDeployer">
- <parameter><this/></parameter>
- </install>
- <uninstall bean="MainDeployer" method="removeDeployer">
- <parameter><this/></parameter>
- </uninstall>
-
- <property name="type">war</property>
- <!-- You can configure a set of authenticators keyed by http-auth method
- used. This will apply the same set of authenticators across all web
- applications. You can override the set of authenticators at the web
- application level by adding <authenticators> element to the respective
- jboss-web.xml
- -->
- <property name="authenticators">
- <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String">
- <entry>
- <key>BASIC</key>
- <value>org.apache.catalina.authenticator.BasicAuthenticator</value>
- </entry>
- <entry>
- <key>CLIENT-CERT</key>
- <value>org.apache.catalina.authenticator.SSLAuthenticator</value>
- </entry>
- <entry>
- <key>DIGEST</key>
- <value>org.apache.catalina.authenticator.DigestAuthenticator</value>
- </entry>
- <entry>
- <key>FORM</key>
- <value>org.apache.catalina.authenticator.FormAuthenticator</value>
- </entry>
- <entry>
- <key>NONE</key>
- <value>org.apache.catalina.authenticator.NonLoginAuthenticator</value>
- </entry>
- </map>
- </property>
-
- <!-- The JAAS security domain to use in the absense of an explicit
- security-domain specification in the war WEB-INF/jboss-web.xml
- -->
- <property name="defaultSecurityDomain">java:/jaas/other</property>
-
- <!-- Get the flag indicating if the normal Java2 parent first class
- loading model should be used over the servlet 2.3 web container first
- model.
- -->
- <property name="java2ClassLoadingCompliance">false</property>
- <!-- A flag indicating if the JBoss Loader should be used. This loader
- uses a unified class loader as the class loader rather than the tomcat
- specific class loader.
- The default is false to ensure that wars have isolated class loading
- for duplicate jars and jsp files.
- -->
- <property name="useJBossWebLoader">false</property>
- <!-- The list of package prefixes that should not be loaded without
- delegating to the parent class loader before trying the web app
- class loader. The packages listed here are those tha are used by
- the web container implementation and cannot be overriden. The format
- is a comma separated list of the package names. There cannot be any
- whitespace between the package prefixes.
- This setting only applies when UseJBossWebLoader=false.
- -->
- <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
-
- <property name="lenientEjbLink">true</property>
-
- <!--Flag to delete the Work Dir on Context Destroy -->
- <property name="deleteWorkDirOnContextDestroy">false</property>
-
- <!--
- Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
- - org.jboss.web.tomcat.tc6.session.JBossCacheManager
- -->
- <property name="managerClass">org.jboss.web.tomcat.tc6.session.JBossCacheManager</property>
-
- <!-- The class used as the deployment wrapper -->
- <property name="deploymentClass">org.jboss.web.tomcat.tc6.deployers.TomcatDeployment</property>
-
- <!-- The name of the request property under with the authenticated JAAS
- Subject is stored on successful authentication. If null or empty then
- the Subject will not be stored.
- -->
- <!--
- <property name="subjectAttributeName">j_subject</property>
- -->
-
- <!-- The SessionIdAlphabet is the set of characters used to create a session Id
- It must be made up of exactly 65 unique characters
- <property name="sessionIdAlphabet">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*</property>
- -->
-
- <!--
- *******************************************************
- ****************** CLUSTERING *************************
- *******************************************************
- In order to activate HTTP Session clustering for Tomcat
- make sure you run JBoss's "all" configuration i.e.
- "run -c all"
- (the default configuration doesn't contain clustering)
-
- Furthermore, you may change SnapshotMode and
- SnapshotInterval propertys below to indicate when to
- synchronize changes with the other node(s).
-
- If you use Apache+mod_jk(2) you will most probably use
- the AJP1.3 connector below. Thus, if you so wish,
- you may comment (i.e. deactivate) the HTTP connector
- as it won't be used anymore.
-
- *******************************************************
- *******************************************************
- *******************************************************
- -->
-
- <!--
- If you are using clustering, the following two propertys
- define when the sessions are replicated to the other nodes.
- The default value, "instant", synchronously replicates changes
- to the other nodes at the end of requests. In this case, the
- "SnapshotInterval" property is not used.
- The "interval" mode, in association with the "SnapshotInterval"
- property, indicates that modified sessions will only be replicated
- every "SnapshotInterval" milliseconds at most.
-
- Note that this property is not in effect if the replication-granularity
- is set to FIELD. If it is FIELD, it will be per http request (that is,
- "instant" mode.)
- -->
- <property name="snapshotMode">instant</property> <!-- you may switch to "interval" -->
- <property name="snapshotInterval">2000</property>
-
- <!--
- Whether to use MOD_JK(2) for load balancing with sticky session
- combined with JvmRoute. If set to true, it will insert a JvmRouteValve
- to intercept every request and replace the JvmRoute if it detects a
- failover. In addition, you will need to set the JvmRoute inside
- Tomcat, e.g.,
- Engine name="jboss.web" jmvRoute="Node1" defaultHost="localhost"
- in server.xml.
-
- For clustering purpose only.
- -->
- <property name="useJK">false</property>
-
- <property name="domain">jboss.web</property>
- <property name="securityManagerService">
- <inject bean="jboss.security:service=JaasSecurityManager" />
- </property>
-
- <depends>jboss:service=TransactionManager</depends>
- <depends>SecurityDeployer</depends>
-
- <!-- Only needed if the org.jboss.web.tomcat.tc6.jca.CachedConnectionValve
-TODO: injection
- is enabled in the tomcat server.xml file.
- <depends>jboss.jca:service=CachedConnectionManager</depends>
- -->
- </bean>
-
-</deployment>
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Web application deployers
+$Id:$
+-->
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+
+ <!-- Tomcat
+ <bean name="VFSClassLoader" class="org.jboss.virtual.classloading.VFSClassLoader">
+ <constructor factoryClass="org.jboss.virtual.classloading.VFSClassLoaderFactory"
+ factoryMethod="newClassLoader">
+ <parameter class="java.net.URI">${jboss.server.home.url}/deployers/jboss-web.deployer/</parameter>
+ <parameter>
+ <array class="[Ljava.lang.String;" elementClass="java.lang.String">
+ <value>*.jar</value>
+ <value>jsf-libs/*.jar</value>
+ </array>
+ </parameter>
+ </constructor>
+ </bean>
+ -->
+
+ <!-- web.xml parsing deployer -->
+ <bean name="WebAppParsingDeployer" class="org.jboss.deployment.WebAppParsingDeployer">
+ <install bean="MainDeployer" method="addDeployer">
+ <parameter><this/></parameter>
+ </install>
+ <uninstall bean="MainDeployer" method="removeDeployer">
+ <parameter><this/></parameter>
+ </uninstall>
+ <property name="type">war</property>
+ <property name="relativeOrder">2000</property>
+ </bean>
+ <bean name="JBossWebAppParsingDeployer" class="org.jboss.deployment.JBossWebAppParsingDeployer">
+ <install bean="MainDeployer" method="addDeployer">
+ <parameter><this/></parameter>
+ </install>
+ <uninstall bean="MainDeployer" method="removeDeployer">
+ <parameter><this/></parameter>
+ </uninstall>
+ <property name="type">war</property>
+ <property name="relativeOrder">2001</property>
+ </bean>
+
+ <!-- Allow for war local class loaders: in testing
+ <bean name="WarClassLoaderDeployer" class="org.jboss.web.tomcat.service.deployers.WarClassLoaderDeployer">
+ <install bean="MainDeployer" method="addDeployer">
+ <parameter><this/></parameter>
+ </install>
+ <uninstall bean="MainDeployer" method="removeDeployer">
+ <parameter><this/></parameter>
+ </uninstall>
+ <!- - CLASSLOADER_DEPLOYER - 1 - ->
+ <property name="relativeOrder">3999</property>
+ <!- - A flag indicating if the JBoss Loader should be used. This loader
+ uses a unified class loader as the class loader rather than the tomcat
+ specific class loader.
+ The default is false to ensure that wars have isolated class loading
+ for duplicate jars and jsp files.
+ - ->
+ <property name="useJBossWebLoader">false</property>
+ <!- - The list of package prefixes that should not be loaded without
+ delegating to the parent class loader before trying the web app
+ class loader. The packages listed here are those tha are used by
+ the web container implementation and cannot be overriden. The format
+ is a comma separated list of the package names. There cannot be any
+ whitespace between the package prefixes.
+ This setting only applies when UseJBossWebLoader=false.
+ - ->
+ <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
+ </bean>
+ -->
+
+ <!-- The WebMetaData to service mbean deployer -->
+ <bean name="WarDeployer" class="org.jboss.web.tomcat.service.deployers.TomcatDeployer">
+ <install bean="MainDeployer" method="addDeployer">
+ <parameter><this/></parameter>
+ </install>
+ <uninstall bean="MainDeployer" method="removeDeployer">
+ <parameter><this/></parameter>
+ </uninstall>
+
+ <property name="type">war</property>
+ <!-- You can configure a set of authenticators keyed by http-auth method
+ used. This will apply the same set of authenticators across all web
+ applications. You can override the set of authenticators at the web
+ application level by adding <authenticators> element to the respective
+ jboss-web.xml
+ -->
+ <property name="authenticators">
+ <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String">
+ <entry>
+ <key>BASIC</key>
+ <value>org.apache.catalina.authenticator.BasicAuthenticator</value>
+ </entry>
+ <entry>
+ <key>CLIENT-CERT</key>
+ <value>org.apache.catalina.authenticator.SSLAuthenticator</value>
+ </entry>
+ <entry>
+ <key>DIGEST</key>
+ <value>org.apache.catalina.authenticator.DigestAuthenticator</value>
+ </entry>
+ <entry>
+ <key>FORM</key>
+ <value>org.apache.catalina.authenticator.FormAuthenticator</value>
+ </entry>
+ <entry>
+ <key>NONE</key>
+ <value>org.apache.catalina.authenticator.NonLoginAuthenticator</value>
+ </entry>
+ </map>
+ </property>
+
+ <!-- The JAAS security domain to use in the absense of an explicit
+ security-domain specification in the war WEB-INF/jboss-web.xml
+ -->
+ <property name="defaultSecurityDomain">java:/jaas/other</property>
+
+ <!-- Get the flag indicating if the normal Java2 parent first class
+ loading model should be used over the servlet 2.3 web container first
+ model.
+ -->
+ <property name="java2ClassLoadingCompliance">false</property>
+ <!-- A flag indicating if the JBoss Loader should be used. This loader
+ uses a unified class loader as the class loader rather than the tomcat
+ specific class loader.
+ The default is false to ensure that wars have isolated class loading
+ for duplicate jars and jsp files.
+ -->
+ <property name="useJBossWebLoader">false</property>
+ <!-- The list of package prefixes that should not be loaded without
+ delegating to the parent class loader before trying the web app
+ class loader. The packages listed here are those tha are used by
+ the web container implementation and cannot be overriden. The format
+ is a comma separated list of the package names. There cannot be any
+ whitespace between the package prefixes.
+ This setting only applies when UseJBossWebLoader=false.
+ -->
+ <property name="filteredPackages">javax.servlet,org.apache.commons.logging</property>
+
+ <property name="lenientEjbLink">true</property>
+
+ <!--Flag to delete the Work Dir on Context Destroy -->
+ <property name="deleteWorkDirOnContextDestroy">false</property>
+
+ <!--
+ Class of the session manager (used if context is marked as 'distributable'. Currently allowed values:
+ - org.jboss.web.tomcat.service.session.JBossCacheManager
+ -->
+ <property name="managerClass">org.jboss.web.tomcat.service.session.JBossCacheManager</property>
+
+ <!-- The class used as the deployment wrapper -->
+ <property name="deploymentClass">org.jboss.web.tomcat.service.deployers.TomcatDeployment</property>
+
+ <!-- The name of the request property under with the authenticated JAAS
+ Subject is stored on successful authentication. If null or empty then
+ the Subject will not be stored.
+ -->
+ <!--
+ <property name="subjectAttributeName">j_subject</property>
+ -->
+
+ <!-- The SessionIdAlphabet is the set of characters used to create a session Id
+ It must be made up of exactly 65 unique characters
+ <property name="sessionIdAlphabet">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*</property>
+ -->
+
+ <!--
+ *******************************************************
+ ****************** CLUSTERING *************************
+ *******************************************************
+ In order to activate HTTP Session clustering for Tomcat
+ make sure you run JBoss's "all" configuration i.e.
+ "run -c all"
+ (the default configuration doesn't contain clustering)
+
+ Furthermore, you may change SnapshotMode and
+ SnapshotInterval propertys below to indicate when to
+ synchronize changes with the other node(s).
+
+ If you use Apache+mod_jk(2) you will most probably use
+ the AJP1.3 connector below. Thus, if you so wish,
+ you may comment (i.e. deactivate) the HTTP connector
+ as it won't be used anymore.
+
+ *******************************************************
+ *******************************************************
+ *******************************************************
+ -->
+
+ <!--
+ If you are using clustering, the following two propertys
+ define when the sessions are replicated to the other nodes.
+ The default value, "instant", synchronously replicates changes
+ to the other nodes at the end of requests. In this case, the
+ "SnapshotInterval" property is not used.
+ The "interval" mode, in association with the "SnapshotInterval"
+ property, indicates that modified sessions will only be replicated
+ every "SnapshotInterval" milliseconds at most.
+
+ Note that this property is not in effect if the replication-granularity
+ is set to FIELD. If it is FIELD, it will be per http request (that is,
+ "instant" mode.)
+ -->
+ <property name="snapshotMode">instant</property> <!-- you may switch to "interval" -->
+ <property name="snapshotInterval">2000</property>
+
+ <!--
+ Whether to use MOD_JK(2) for load balancing with sticky session
+ combined with JvmRoute. If set to true, it will insert a JvmRouteValve
+ to intercept every request and replace the JvmRoute if it detects a
+ failover. In addition, you will need to set the JvmRoute inside
+ Tomcat, e.g.,
+ Engine name="jboss.web" jmvRoute="Node1" defaultHost="localhost"
+ in server.xml.
+
+ For clustering purpose only.
+ -->
+ <property name="useJK">false</property>
+
+ <property name="domain">jboss.web</property>
+ <property name="securityManagerService">
+ <inject bean="jboss.security:service=JaasSecurityManager" />
+ </property>
+
+ <depends>jboss:service=TransactionManager</depends>
+ <depends>SecurityDeployer</depends>
+
+ <!-- Only needed if the org.jboss.web.tomcat.service.jca.CachedConnectionValve
+TODO: injection
+ is enabled in the tomcat server.xml file.
+ <depends>jboss.jca:service=CachedConnectionManager</depends>
+ -->
+ </bean>
+
+</deployment>
Modified: trunk/tomcat/src/resources/web.xml
===================================================================
--- trunk/tomcat/src/resources/web.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/web.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -30,8 +30,7 @@
org.jboss.web.tomcat.filters.ReplyHeaderFilter</filter-class>
<init-param>
<param-name>X-Powered-By</param-name>
- <param-value>Servlet 2.4;
- JBoss-5.0.0.Alpha (build: CVSTag=HEAD date=200606261952)/Tomcat-5.5</param-value>
+ <param-value>Servlet 2.5; JBoss-5.0/JBossWeb-2.0/Tomcat-6.0</param-value>
</init-param>
</filter>
@@ -258,7 +257,7 @@
<init-param>
<param-name>engineOptionsClass</param-name>
<param-value>
- org.jboss.web.tomcat.tc6.jasper.JspServletOptions</param-value>
+ org.jboss.web.tomcat.service.jasper.JspServletOptions</param-value>
</init-param>
<!-- Specify the jars relative to the jbossweb-tomcat6.sar that should
be scanned for common tag lib descriptors to include in every war
Modified: trunk/tomcat/src/resources/webserver-xmbean.xml
===================================================================
--- trunk/tomcat/src/resources/webserver-xmbean.xml 2007-03-16 00:26:50 UTC (rev 61354)
+++ trunk/tomcat/src/resources/webserver-xmbean.xml 2007-03-16 02:23:53 UTC (rev 61355)
@@ -15,7 +15,7 @@
<interceptor code="org.jboss.mx.interceptor.DynamicInterceptor"/>
</interceptors>
</descriptors>
- <class>org.jboss.web.tomcat.tc6.Tomcat6</class>
+ <class>org.jboss.web.tomcat.service.deployers.TomcatDeployer</class>
<!-- Tomcat6MBean attributes -->
More information about the jboss-cvs-commits
mailing list