[jboss-cvs] JBossAS SVN: r58921 - in branches/JBoss_4_0_3_SP1_CP: testsuite/imports testsuite/src/main/org/jboss/test testsuite/src/main/org/jboss/test/web/test testsuite/src/resources/test-configs/tomcat-sso/deploy/jbossweb-tomcat55.sar testsuite/src/resources/test-configs/tomcat-sso-cluster/deploy/jbossweb-tomcat55.sar testsuite/src/resources/web/sso/war tomcat/src/main/org/jboss/web/tomcat/tc5/session tomcat/src/main/org/jboss/web/tomcat/tc5/sso
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Thu Dec 7 22:07:41 EST 2006
Author: vivekl at redhat.com
Date: 2006-12-07 22:07:36 -0500 (Thu, 07 Dec 2006)
New Revision: 58921
Removed:
branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso-cluster/deploy/jbossweb-tomcat55.sar/context.xml
branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso/deploy/jbossweb-tomcat55.sar/context.xml
branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/index.jsp
branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/isValid.jsp
Modified:
branches/JBoss_4_0_3_SP1_CP/testsuite/imports/test-jars.xml
branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/JBossClusteredTestCase.java
branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/JBossTestClusteredServices.java
branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/ClusteredSingleSignOnUnitTestCase.java
branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/SSOBaseCase.java
branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/SingleSignOnUnitTestCase.java
branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso-cluster/deploy/jbossweb-tomcat55.sar/server.xml
branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/web-sso-expire.xml
branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/session/JBossManager.java
branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/ClusteredSingleSignOn.java
branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/TreeCacheSSOClusterManager.java
branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/mbeans-descriptors.xml
Log:
ASPATCH129: JBAS-3808: Port JBAS-3096 Fix to 4.0.3.SP1
Revision 58722 reverted since this is not meant for a CP release.
Modified: branches/JBoss_4_0_3_SP1_CP/testsuite/imports/test-jars.xml
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/imports/test-jars.xml 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/imports/test-jars.xml 2006-12-08 03:07:36 UTC (rev 58921)
@@ -5086,7 +5086,6 @@
</classes>
<fileset dir="${build.resources}/web/sso/war">
<include name="**/*.html"/>
- <include name="**/*.jsp"/>
</fileset>
</war>
<war destfile="${build.lib}/sso-expire.war"
@@ -5100,7 +5099,6 @@
</classes>
<fileset dir="${build.resources}/web/sso/war">
<include name="**/*.html"/>
- <include name="**/*.jsp"/>
</fileset>
</war>
<zip destfile="${build.lib}/web-sso.ear">
Modified: branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/JBossClusteredTestCase.java
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/JBossClusteredTestCase.java 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/JBossClusteredTestCase.java 2006-12-08 03:07:36 UTC (rev 58921)
@@ -63,16 +63,6 @@
return clusterServices.getHttpURLs();
}
- protected void deploy(RMIAdaptor server, String name) throws Exception
- {
- clusterServices.deploy(server, name);
- }
-
- protected void undeploy(RMIAdaptor server, String name) throws Exception
- {
- clusterServices.undeploy(server, name);
- }
-
public static Test getDeploySetup(final Test test, final String jarName)
throws Exception
{
Modified: branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/JBossTestClusteredServices.java
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/JBossTestClusteredServices.java 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/JBossTestClusteredServices.java 2006-12-08 03:07:36 UTC (rev 58921)
@@ -6,7 +6,6 @@
*/
package org.jboss.test;
-import java.net.URL;
import java.util.ArrayList;
import java.util.Hashtable;
@@ -134,53 +133,6 @@
}
- /**
- * Deploy a package on the given server with the main deployer. The supplied
- * name is interpreted as a url, or as a filename in jbosstest.deploy.lib or
- * ../lib.
- *
- * @param server server on which the package should be deployed
- * @param name filename/url of package to deploy.
- * @exception Exception Description of Exception
- */
- public void deploy(RMIAdaptor server, String name) throws Exception
- {
- if (Boolean.getBoolean("jbosstest.nodeploy") == true)
- {
- log.debug("Skipping deployment of: " + name);
- return;
- }
-
- URL deployURL = new URL(getDeployURL(name));
- log.debug("Deploying " + name + ", url=" + deployURL + " to " + server);
- invoke(server,
- getDeployerName(),
- "deploy",
- new Object[]{deployURL},
- new String[]{"java.net.URL"});
- }
-
- /**
- * Undeploy a package from the given server with the main deployer.
- * The supplied name is interpreted as a url, or as a filename in
- * jbosstest.deploy.lib or ../lib.
- *
- * @param server server on which the package should be deployed
- * @param name filename/url of package to undeploy.
- * @exception Exception Description of Exception
- */
- public void undeploy(RMIAdaptor server, String name) throws Exception
- {
- if (Boolean.getBoolean("jbosstest.nodeploy") == true)
- return;
-
- URL deployURL = new URL(getDeployURL(name));
- log.debug("Undeploying " + name + ", url=" + deployURL);
- Object[] args = {deployURL};
- String[] sig = {"java.net.URL"};
- invoke(server, getDeployerName(), "undeploy", args, sig);
- }
-
public void init() throws Exception
{
if (initialContext == null)
Modified: branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/ClusteredSingleSignOnUnitTestCase.java
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/ClusteredSingleSignOnUnitTestCase.java 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/ClusteredSingleSignOnUnitTestCase.java 2006-12-08 03:07:36 UTC (rev 58921)
@@ -1,37 +1,23 @@
/*
-* JBoss, a division of Red Hat
-* Copyright 2006, Red Hat Middleware, LLC, 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.
-*/
+ * JBoss, the OpenSource J2EE webOS
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
package org.jboss.test.web.test;
-import java.util.Set;
+import java.net.HttpURLConnection;
-import javax.management.ObjectName;
-
import junit.framework.Test;
-import junit.framework.TestSuite;
+import org.apache.commons.httpclient.Cookie;
+import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpState;
-import org.jboss.cache.Fqn;
-import org.jboss.jmx.adaptor.rmi.RMIAdaptor;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.log4j.Category;
import org.jboss.test.JBossClusteredTestCase;
@@ -43,13 +29,6 @@
public class ClusteredSingleSignOnUnitTestCase
extends JBossClusteredTestCase
{
- // NOTE: these variables must be static as apparently a separate instance
- // of this class is created for each test
- private static boolean deployed0 = true;
- private static boolean deployed1 = true;
-
- private RMIAdaptor[] adaptors = null;
-
public ClusteredSingleSignOnUnitTestCase(String name)
{
super(name);
@@ -59,119 +38,11 @@
*/
public static Test suite() throws Exception
{
- // Have to build the suite in detail, as testSessionExpiration must come first
- TestSuite suite = new TestSuite();
- suite.addTest(new ClusteredSingleSignOnUnitTestCase("testSessionExpiration"));
- suite.addTest(new ClusteredSingleSignOnUnitTestCase("testFormAuthSingleSignOn"));
- suite.addTest(new ClusteredSingleSignOnUnitTestCase("testWebappUndeploy"));
-
-// return JBossClusteredTestCase.getDeploySetup(suite, "web-sso-clustered.ear");
-
- return JBossClusteredTestCase.getDeploySetup(suite, "web-sso.ear");
+ Test t1 = JBossClusteredTestCase.getDeploySetup(ClusteredSingleSignOnUnitTestCase.class,
+ "web-sso.ear");
+ return t1;
}
- protected void setUp() throws Exception
- {
- super.setUp();
-
- log.debug("deployed0 = " + deployed0);
- log.debug("deployed1 = " + deployed1);
-
- adaptors = getAdaptors();
- if (!deployed0)
- {
-// deploy(adaptors[0], "web-sso-clustered.ear");
- deploy(adaptors[0], "web-sso.ear");
- deployed0 = true;
- }
- if (!deployed1)
- {
-// deploy(adaptors[1], "web-sso-clustered.ear");
- deploy(adaptors[1], "web-sso.ear");
- deployed1 = true;
- }
- }
-
- protected void tearDown() throws Exception
- {
- super.tearDown();
-
- log.debug("deployed0 = " + deployed0);
- log.debug("deployed1 = " + deployed1);
- }
-
- /**
- * Tests that undeploying a webapp on one server doesn't kill an sso
- * that also has a session from another webapp associated with it.
- * See JBAS-2429.
- *
- * TODO create an independently deployable war so we can test this in
- * a non-clustered environment as well; this isn't a clustering issue
- *
- * @throws Exception
- */
- public void testWebappUndeploy() throws Exception
- {
- log.info("+++ testWebappUndeploy");
-
- String[] httpURLs = super.getHttpURLs();
-
- String serverA = httpURLs[0];
- String serverB = httpURLs[1];
-
- String warA1 = serverA + "/war1/";
- String warB1 = serverB + "/war1/";
- String warB2 = serverB + "/war2/";
-
- // Start by accessing the secured index.html of war1
- HttpClient httpConn = new HttpClient();
- SSOBaseCase.checkAccessDenied(httpConn, warA1 + "index.html");
-
- HttpState state = httpConn.getState();
-
- String sessionID = SSOBaseCase.getSessionIdFromState(state);
- log.debug("Saw JSESSIONID="+sessionID);
-
- // Submit the login form
- SSOBaseCase.executeFormLogin(httpConn, warA1);
-
- String ssoID = SSOBaseCase.processSSOCookie(state, serverA, serverB);
- log.debug("Saw JSESSIONIDSSO="+ssoID);
-
- // Pause a moment before switching wars to better simulate real life
- // use cases. Otherwise, the test case can "outrun" the async
- // replication in the TreeCache used by the clustered SSO
- // 500 ms is a long time, but this isn't a test of replication speed
- // and we don't want spurious failures.
- if (!serverA.equals(serverB))
- Thread.sleep(500);
-
- // Now try getting the war2 index using the JSESSIONIDSSO cookie
- log.debug("Prepare /war2/index.html get");
- SSOBaseCase.checkAccessAllowed(httpConn, warB2 + "index.html");
-
- // Sleep some more to allow the updated sso to propagate back to serverA
- if (!serverA.equals(serverB))
- Thread.sleep(500);
-
- // We now have a clustered sso context, plus a war1 session on
- // serverA and a war2 session on serverB. No war1 session on serverB,
- // so the only way to access war1 on B without a login is through sso.
-
- //Undeploy the ear from serverA and confirm that it doesn't kill the sso
-// undeploy(adaptors[0], "web-sso-clustered.ear");
- undeploy(adaptors[0], "web-sso.ear");
- deployed0 = false;
-
- // Sleep some more to allow the updated sso to propagate back to serverB
- if (!serverA.equals(serverB))
- Thread.sleep(500);
-
- // Now try getting the war1 index using the JSESSIONIDSSO cookie
- log.debug("Prepare /war1/index.html get");
- SSOBaseCase.checkAccessAllowed(httpConn, warB1 + "index.html");
- }
-
/** Test single sign-on across two web apps using form based auth
*
* @throws Exception
@@ -193,83 +64,81 @@
* Tests that use of transactions in ClusteredSSO does not interfere
* with session expiration thread. See JBAS-2212.
*
+ * TODO this is a quick-and-dirty test to get JBoss 4.0.3 out;
+ * clearly needs to be refactored
+ *
* @throws Exception
*/
public void testSessionExpiration()
throws Exception
{
- log.info("+++ testSessionExpiration");
+ log.info("+++ testFormAuthSingleSignOn");
String[] httpURLs = super.getHttpURLs();
String serverA = httpURLs[0];
- String serverB = httpURLs[1];
log.info(System.getProperties());
log.info("serverA: "+serverA);
- log.info("serverB: "+serverB);
- String warA3 = serverA + "/war3/";
- String warB3 = serverB + "/war3/";
-
- // First create an SSO that we won't use again -- we later test that it
- // gets cleaned up from the cache
- HttpClient httpConn1 = new HttpClient();
- SSOBaseCase.checkAccessDenied(httpConn1, warA3 + "index.jsp");
- SSOBaseCase.executeFormLogin(httpConn1, warA3);
-
- // Now the standard SSO tests
+ // Start by accessing the secured index.html of war1
HttpClient httpConn = new HttpClient();
- SSOBaseCase.checkAccessDenied(httpConn, warA3 + "index.jsp");
- HttpState state = httpConn.getState();
- String sessionID = SSOBaseCase.getSessionIdFromState(state);
- log.debug("Saw JSESSIONID="+sessionID);
- SSOBaseCase.executeFormLogin(httpConn, warA3);
- String ssoID = SSOBaseCase.processSSOCookie(state, serverA, serverB);
- log.debug("Saw JSESSIONIDSSO="+ssoID);
+ GetMethod indexGet = new GetMethod(serverA+"/war3/index.html");
+ int responseCode = httpConn.executeMethod(indexGet);
+ String body = indexGet.getResponseBodyAsString();
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ assertTrue("Redirected to login page", body.indexOf("j_security_check") > 0 );
- // Wait more than 15 secs to let session time out
- // It's life is 5 secs and the processor runs every 10
- try {
- Thread.sleep(15500);
- }
- catch (InterruptedException ie)
+ HttpState state = httpConn.getState();
+ Cookie[] cookies = state.getCookies();
+ String sessionID = null;
+ for(int c = 0; c < cookies.length; c ++)
{
- log.debug("Interrupted while waiting for session expiration");
+ Cookie k = cookies[c];
+ if( k.getName().equalsIgnoreCase("JSESSIONID") )
+ sessionID = k.getValue();
}
-
- // Try accessing war3 again on both nodes -- should succeed
- SSOBaseCase.checkAccessAllowed(httpConn, warA3 + "index.jsp");
- SSOBaseCase.checkAccessAllowed(httpConn, warB3 + "index.jsp");
+ log.debug("Saw JSESSIONID="+sessionID);
- // Wait more than 30 secs to let SSO time out -- 15 for the sessions
- // (as above) and 15 for the SSOs maxEmptyLife
+ // Submit the login form
+ PostMethod formPost = new PostMethod(serverA+"/war3/j_security_check");
+ formPost.addRequestHeader("Referer", serverA+"/war3/login.html");
+ formPost.addParameter("j_username", "jduke");
+ formPost.addParameter("j_password", "theduke");
+ responseCode = httpConn.executeMethod(formPost.getHostConfiguration(),
+ formPost, state);
+ String response = formPost.getStatusText();
+ log.debug("responseCode="+responseCode+", response="+response);
+ assertTrue("Saw HTTP_MOVED_TEMP("+responseCode+")",
+ responseCode == HttpURLConnection.HTTP_MOVED_TEMP);
+
+ // Follow the redirect to the index.html page
+ Header location = formPost.getResponseHeader("Location");
+ String indexURI = location.getValue();
+ GetMethod war1Index = new GetMethod(indexURI);
+ responseCode = httpConn.executeMethod(war1Index.getHostConfiguration(),
+ war1Index, state);
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ body = war1Index.getResponseBodyAsString();
+ if( body.indexOf("j_security_check") > 0 )
+ fail("get of "+indexURI+" redirected to login page");
+
+ // Wait more than 1 minute to let session time out
try {
- Thread.sleep(30500);
+ Thread.sleep(75000);
}
catch (InterruptedException ie)
{
- log.debug("Interrupted while waiting for SSO expiration");
+ log.debug("Interrupted while waiting for session expiration");
}
- // Try accessing war3 again on both nodes -- should fail
- SSOBaseCase.checkAccessDenied(httpConn, warA3 + "index.jsp");
- SSOBaseCase.checkAccessDenied(httpConn, warB3 + "index.jsp");
+ // Try accessing war1 again
+ war1Index = new GetMethod(serverA+"/war3/index.html");
+ responseCode = httpConn.executeMethod(war1Index.getHostConfiguration(),
+ war1Index, state);
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ body = war1Index.getResponseBodyAsString();
+ log.debug("body: " + body);
+ if( body.indexOf("j_security_check") < 0 )
+ fail("get of /war1/index.html not redirected to login page");
- // Confirm that the SSO we created at the start was removed from
- // the cache
- Set ssoIds = getSSOIds(adaptors[0]);
- log.info("node0 sso ids are " + ssoIds);
- assertEquals("node0 cache has no SSOs", 0, ssoIds.size());
- ssoIds = getSSOIds(adaptors[1]);
- log.info("node1 sso ids are " + ssoIds);
- assertEquals("node1 cache has no SSOs", 0, ssoIds.size());
}
-
- private Set getSSOIds(RMIAdaptor adaptor) throws Exception
- {
- Fqn ssoRoot = new Fqn(new Object[] {"SSO"});
- ObjectName oname = new ObjectName("jboss.cache:service=TomcatClusteringCache");
- return (Set) adaptor.invoke(oname, "getChildrenNames",
- new Object[]{ ssoRoot },
- new String[]{ Fqn.class.getName() });
- }
}
Modified: branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/SSOBaseCase.java
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/SSOBaseCase.java 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/SSOBaseCase.java 2006-12-08 03:07:36 UTC (rev 58921)
@@ -1,34 +1,18 @@
/*
-* JBoss, a division of Red Hat
-* Copyright 2006, Red Hat Middleware, LLC, 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.
-*/
+ * JBoss, the OpenSource J2EE webOS
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
package org.jboss.test.web.test;
-import java.io.IOException;
import java.net.HttpURLConnection;
import junit.framework.TestCase;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
-import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
@@ -51,137 +35,132 @@
Category log)
throws Exception
{
- String warA1 = serverA + "/war1/";
- String warB2 = serverB + "/war2/";
-
// Start by accessing the secured index.html of war1
HttpClient httpConn = new HttpClient();
-
- checkAccessDenied(httpConn, warA1 + "index.html");
+ GetMethod indexGet = new GetMethod(serverA+"/war1/index.html");
+ int responseCode = httpConn.executeMethod(indexGet);
+ String body = indexGet.getResponseBodyAsString();
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ assertTrue("Redirected to login page", body.indexOf("j_security_check") > 0 );
HttpState state = httpConn.getState();
-
- String sessionID = getSessionIdFromState(state);
+ Cookie[] cookies = state.getCookies();
+ String sessionID = null;
+ for(int c = 0; c < cookies.length; c ++)
+ {
+ Cookie k = cookies[c];
+ if( k.getName().equalsIgnoreCase("JSESSIONID") )
+ sessionID = k.getValue();
+ }
log.debug("Saw JSESSIONID="+sessionID);
// Submit the login form
- executeFormLogin(httpConn, warA1);
-
- String ssoID = processSSOCookie(state, serverA, serverB);
- log.debug("Saw JSESSIONIDSSO="+ssoID);
-
- // Pause a moment before switching wars to better simulate real life
- // use cases. Otherwise, the test case can "outrun" the async
- // replication in the TreeCache used by the clustered SSO
- // 500 ms is a long time, but this isn't a test of replication speed
- // and we don't want spurious failures.
- if (!serverA.equals(serverB))
- Thread.sleep(500);
-
- // Now try getting the war2 index using the JSESSIONIDSSO cookie
- log.debug("Prepare /war2/index.html get");
- checkAccessAllowed(httpConn, warB2 + "index.html");
-
- /* Access a secured servlet that calls a secured ejb in war2 to test
- propagation of the SSO identity to the ejb container. */
- checkAccessAllowed(httpConn, warB2 + "EJBServlet");
-
- // Now try logging out of war2
- executeLogout(httpConn, warB2);
-
- // Again, pause before switching wars
- if (!serverA.equals(serverB))
- Thread.sleep(500);
-
- // Try accessing war1 again
- checkAccessDenied(httpConn, warA1 + "index.html");
-
- // Try accessing war2 again
- checkAccessDenied(httpConn, warB2 + "index.html");
-
- }
-
- public static void executeLogout(HttpClient httpConn, String warURL) throws IOException, HttpException
- {
- GetMethod logout = new GetMethod(warURL + "Logout");
- logout.setFollowRedirects(false);
- int responseCode = httpConn.executeMethod(logout.getHostConfiguration(),
- logout, httpConn.getState());
- assertTrue("Logout: Saw HTTP_MOVED_TEMP("+responseCode+")",
- responseCode == HttpURLConnection.HTTP_MOVED_TEMP);
- Header location = logout.getResponseHeader("Location");
- String indexURI = location.getValue();
- if( indexURI.indexOf("index.html") < 0 )
- fail("get of " + warURL + "Logout not redirected to login page");
- }
-
- public static void checkAccessAllowed(HttpClient httpConn, String url) throws IOException, HttpException
- {
- GetMethod war2Index = new GetMethod(url);
- int responseCode = httpConn.executeMethod(war2Index.getHostConfiguration(),
- war2Index, httpConn.getState());
- assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
- String body = war2Index.getResponseBodyAsString();
- if( body.indexOf("j_security_check") > 0 )
- fail("get of " + url + " redirected to login page");
- }
-
- public static void executeFormLogin(HttpClient httpConn, String warURL) throws IOException, HttpException
- {
- PostMethod formPost = new PostMethod(warURL + "j_security_check");
- formPost.addRequestHeader("Referer", warURL + "login.html");
+ PostMethod formPost = new PostMethod(serverA+"/war1/j_security_check");
+ formPost.addRequestHeader("Referer", serverA+"/war1/login.html");
formPost.addParameter("j_username", "jduke");
formPost.addParameter("j_password", "theduke");
- int responseCode = httpConn.executeMethod(formPost.getHostConfiguration(),
- formPost, httpConn.getState());
+ responseCode = httpConn.executeMethod(formPost.getHostConfiguration(),
+ formPost, state);
+ String response = formPost.getStatusText();
+ log.debug("responseCode="+responseCode+", response="+response);
assertTrue("Saw HTTP_MOVED_TEMP("+responseCode+")",
responseCode == HttpURLConnection.HTTP_MOVED_TEMP);
// Follow the redirect to the index.html page
Header location = formPost.getResponseHeader("Location");
String indexURI = location.getValue();
- GetMethod warIndex = new GetMethod(indexURI);
- responseCode = httpConn.executeMethod(warIndex.getHostConfiguration(),
- warIndex, httpConn.getState());
+ GetMethod war1Index = new GetMethod(indexURI);
+ responseCode = httpConn.executeMethod(war1Index.getHostConfiguration(),
+ war1Index, state);
assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
- String body = warIndex.getResponseBodyAsString();
+ body = war1Index.getResponseBodyAsString();
if( body.indexOf("j_security_check") > 0 )
fail("get of "+indexURI+" redirected to login page");
- }
- public static void checkAccessDenied(HttpClient httpConn, String url) throws IOException, HttpException
- {
- GetMethod indexGet = new GetMethod(url);
- int responseCode = httpConn.executeMethod(indexGet);
- String body = indexGet.getResponseBodyAsString();
- assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
- assertTrue("Redirected to login page", body.indexOf("j_security_check") > 0 );
- }
-
- public static String processSSOCookie(HttpState state, String serverA, String serverB)
- {
- Cookie[] cookies = state.getCookies();
+ cookies = state.getCookies();
String ssoID = null;
for(int c = 0; c < cookies.length; c ++)
{
- if( "JSESSIONIDSSO".equalsIgnoreCase(cookies[c].getName()) )
+ Cookie k = cookies[c];
+ if( k.getName().equalsIgnoreCase("JSESSIONIDSSO") )
{
- ssoID = cookies[c].getValue();
+ ssoID = k.getValue();
if (serverA.equals(serverB) == false)
{
// Make an sso cookie to send to serverB
- Cookie copy = copyCookie(cookies[c], serverB);
+ Cookie copy = copyCookie(k, serverB);
state.addCookie(copy);
+ log.debug("Added state cookie: "+copy);
}
}
}
+ assertTrue("Saw JSESSIONIDSSO", ssoID != null);
+ log.debug("Saw JSESSIONIDSSO="+ssoID);
+
+ // Now try getting the war2 index using the JSESSIONIDSSO cookie
+ log.debug("Prepare /war2/index.html get");
+ GetMethod war2Index = new GetMethod(serverB+"/war2/index.html");
+ responseCode = httpConn.executeMethod(war2Index.getHostConfiguration(),
+ war2Index, state);
+ response = war2Index.getStatusText();
+ log.debug("responseCode="+responseCode+", response="+response);
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ body = war2Index.getResponseBodyAsString();
+ log.debug("body: "+body);
+ if( body.indexOf("j_security_check") > 0 )
+ fail("get of /war2/index.html redirected to login page");
+
+ /* Access a secured servlet that calls a secured ejb in war2 to test
+ propagation of the SSO identity to the ejb container.
+ */
+ GetMethod war2Servlet = new GetMethod(serverB+"/war2/EJBServlet");
+ responseCode = httpConn.executeMethod(war2Servlet.getHostConfiguration(),
+ war2Servlet, state);
+ response = war2Servlet.getStatusText();
+ log.debug("responseCode="+responseCode+", response="+response);
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ body = war2Servlet.getResponseBodyAsString();
+ log.debug("body: "+body);
+ if( body.indexOf("j_security_check") > 0 )
+ fail("get of /war2/EJBServlet redirected to login page");
+
+ // Now try logging out of war2
+ GetMethod war2Logout = new GetMethod(serverB+"/war2/Logout");
+ war2Logout.setFollowRedirects(false);
+ responseCode = httpConn.executeMethod(war2Logout.getHostConfiguration(),
+ war2Logout, state);
+ response = war2Logout.getStatusText();
+ log.debug("responseCode="+responseCode+", response="+response);
+ assertTrue("Logout: Saw HTTP_MOVED_TEMP("+responseCode+")",
+ responseCode == HttpURLConnection.HTTP_MOVED_TEMP);
+ location = war2Logout.getResponseHeader("Location");
+ indexURI = location.getValue();
+ if( indexURI.indexOf("index.html") < 0 )
+ fail("get of /war2/Logout not redirected to login page");
- assertNotNull("Saw JSESSIONIDSSO", ssoID);
+ // Try accessing war1 again
+ war1Index = new GetMethod(serverA+"/war1/index.html");
+ responseCode = httpConn.executeMethod(war1Index.getHostConfiguration(),
+ war1Index, state);
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ body = war1Index.getResponseBodyAsString();
+ log.debug("body: " + body);
+ if( body.indexOf("j_security_check") < 0 )
+ fail("get of /war1/index.html not redirected to login page");
- return ssoID;
+ // Try accessing war2 again
+ war2Index = new GetMethod(serverB+"/war2/index.html");
+ responseCode = httpConn.executeMethod(war2Index.getHostConfiguration(),
+ war2Index, state);
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ body = war2Index.getResponseBodyAsString();
+ log.debug("body: " + body);
+ if( body.indexOf("j_security_check") < 0 )
+ fail("get of /war2/index.html not redirected to login page");
+
}
- public static Cookie copyCookie(Cookie toCopy, String targetServer)
+ private static Cookie copyCookie(Cookie toCopy, String targetServer)
{
// Parse the target server down to a domain name
int index = targetServer.indexOf("://");
@@ -208,17 +187,4 @@
false);
return copy;
}
-
- public static String getSessionIdFromState(HttpState state)
- {
- Cookie[] cookies = state.getCookies();
- String sessionID = null;
- for(int c = 0; c < cookies.length; c ++)
- {
- if( "JSESSIONID".equalsIgnoreCase(cookies[c].getName()) )
- sessionID = cookies[c].getName();
- }
- return sessionID;
-
- }
}
Modified: branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/SingleSignOnUnitTestCase.java
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/SingleSignOnUnitTestCase.java 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/main/org/jboss/test/web/test/SingleSignOnUnitTestCase.java 2006-12-08 03:07:36 UTC (rev 58921)
@@ -1,26 +1,20 @@
/*
-* JBoss, a division of Red Hat
-* Copyright 2006, Red Hat Middleware, LLC, 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.
-*/
+ * JBoss, the OpenSource J2EE webOS
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
package org.jboss.test.web.test;
+import java.net.HttpURLConnection;
+
+import org.apache.commons.httpclient.Cookie;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpState;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
import org.jboss.test.JBossTestCase;
import org.jboss.test.JBossTestSetup;
import junit.framework.Test;
@@ -33,7 +27,7 @@
*/
public class SingleSignOnUnitTestCase extends JBossTestCase
{
- private String baseURLNoAuth = "http://" + getServerHost() + ":" + Integer.getInteger("web.port", 8080);
+ private String baseURLNoAuth = "http://" + getServerHost() + ":" + Integer.getInteger("web.port", 8080) + "/";
public SingleSignOnUnitTestCase(String name)
{
@@ -46,12 +40,86 @@
*/
public void testFormAuthSingleSignOn() throws Exception
{
- log.info("+++ testFormAuthSingleSignOn");
+ // Start by accessing the secured index.html of war1
+ HttpClient httpConn = new HttpClient();
+ GetMethod indexGet = new GetMethod(baseURLNoAuth+"war1/index.html");
+ int responseCode = httpConn.executeMethod(indexGet);
+ String body = indexGet.getResponseBodyAsString();
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ assertTrue("Redirected to login page", body.indexOf("j_security_check") > 0 );
- SSOBaseCase.executeFormAuthSingleSignOnTest(baseURLNoAuth, baseURLNoAuth, getLog());
+ HttpState state = httpConn.getState();
+ Cookie[] cookies = state.getCookies();
+ String sessionID = null;
+ for(int c = 0; c < cookies.length; c ++)
+ {
+ Cookie k = cookies[c];
+ if( k.getName().equalsIgnoreCase("JSESSIONID") )
+ sessionID = k.getValue();
+ }
+ getLog().debug("Saw JSESSIONID="+sessionID);
+ // Submit the login form
+ PostMethod formPost = new PostMethod(baseURLNoAuth+"war1/j_security_check");
+ formPost.addRequestHeader("Referer", baseURLNoAuth+"war1/login.html");
+ formPost.addParameter("j_username", "jduke");
+ formPost.addParameter("j_password", "theduke");
+ responseCode = httpConn.executeMethod(formPost.getHostConfiguration(),
+ formPost, state);
+ String response = formPost.getStatusText();
+ getLog().debug("responseCode="+responseCode+", response="+response);
+ assertTrue("Saw HTTP_MOVED_TEMP("+responseCode+")",
+ responseCode == HttpURLConnection.HTTP_MOVED_TEMP);
+
+ // Follow the redirect to the index.html page
+ Header location = formPost.getResponseHeader("Location");
+ String indexURI = location.getValue();
+ GetMethod war1Index = new GetMethod(indexURI);
+ responseCode = httpConn.executeMethod(war1Index.getHostConfiguration(),
+ war1Index, state);
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ body = war1Index.getResponseBodyAsString();
+ if( body.indexOf("j_security_check") > 0 )
+ fail("get of "+indexURI+" redirected to login page");
+
+ cookies = state.getCookies();
+ String ssoID = null;
+ for(int c = 0; c < cookies.length; c ++)
+ {
+ Cookie k = cookies[c];
+ if( k.getName().equalsIgnoreCase("JSESSIONIDSSO") )
+ ssoID = k.getValue();
}
+ assertTrue("Saw JSESSIONIDSSO", ssoID != null);
+ getLog().debug("Saw JSESSIONIDSSO="+ssoID);
+ // Now try getting the war2 index using the JSESSIONIDSSO cookie
+ GetMethod war2Index = new GetMethod(baseURLNoAuth+"war2/index.html");
+ responseCode = httpConn.executeMethod(war2Index.getHostConfiguration(),
+ war2Index, state);
+ response = war2Index.getStatusText();
+ getLog().debug("responseCode="+responseCode+", response="+response);
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ body = war2Index.getResponseBodyAsString();
+ getLog().debug("body: "+body);
+ if( body.indexOf("j_security_check") > 0 )
+ fail("get of /war2/index.html redirected to login page");
+
+ /* Access a secured servlet that calls a secured ejb in war2 to test
+ propagation of the SSO identity to the ejb container.
+ */
+ GetMethod war2Servlet = new GetMethod(baseURLNoAuth+"war2/EJBServlet");
+ responseCode = httpConn.executeMethod(war2Servlet.getHostConfiguration(),
+ war2Servlet, state);
+ response = war2Servlet.getStatusText();
+ getLog().debug("responseCode="+responseCode+", response="+response);
+ assertTrue("Get OK", responseCode == HttpURLConnection.HTTP_OK);
+ body = war2Servlet.getResponseBodyAsString();
+ getLog().debug("body: "+body);
+ if( body.indexOf("j_security_check") > 0 )
+ fail("get of /war2/EJBServlet redirected to login page");
+ }
+
/** One time setup for all SingleSignOnUnitTestCase unit tests
*/
public static Test suite() throws Exception
Deleted: branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso/deploy/jbossweb-tomcat55.sar/context.xml
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso/deploy/jbossweb-tomcat55.sar/context.xml 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso/deploy/jbossweb-tomcat55.sar/context.xml 2006-12-08 03:07:36 UTC (rev 58921)
@@ -1,13 +0,0 @@
-<!-- The contents of this file will be loaded for each web application -->
-<Context cookies="true" crossContext="true">
-
- <!-- We want a low processExpiresFrequency so we can have
- fast turnaround of expiration tests -->
- <Manager pathname="" processExpiresFrequency="1"/>
-
- <!-- Install an InstanceListener to handle the establishment of the run-as
- role for servlet init/destroy events.
- -->
- <InstanceListener>org.jboss.web.tomcat.security.RunAsListener</InstanceListener>
-
-</Context>
Deleted: branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso-cluster/deploy/jbossweb-tomcat55.sar/context.xml
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso-cluster/deploy/jbossweb-tomcat55.sar/context.xml 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso-cluster/deploy/jbossweb-tomcat55.sar/context.xml 2006-12-08 03:07:36 UTC (rev 58921)
@@ -1,13 +0,0 @@
-<!-- The contents of this file will be loaded for each web application -->
-<Context cookies="true" crossContext="true">
-
- <!-- We want a low processExpiresFrequency so we can have
- fast turnaround of expiration tests -->
- <Manager pathname="" processExpiresFrequency="1"/>
-
- <!-- Install an InstanceListener to handle the establishment of the run-as
- role for servlet init/destroy events.
- -->
- <InstanceListener>org.jboss.web.tomcat.security.RunAsListener</InstanceListener>
-
-</Context>
Modified: branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso-cluster/deploy/jbossweb-tomcat55.sar/server.xml
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso-cluster/deploy/jbossweb-tomcat55.sar/server.xml 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/test-configs/tomcat-sso-cluster/deploy/jbossweb-tomcat55.sar/server.xml 2006-12-08 03:07:36 UTC (rev 58921)
@@ -14,7 +14,7 @@
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true"/>
- <Engine name="jboss.web" defaultHost="localhost" backgroundProcessorDelay="10">
+ <Engine name="jboss.web" defaultHost="localhost">
<!-- The JAAS based authentication and authorization realm implementation
- certificatePrincipal : the class name of the
@@ -32,7 +32,7 @@
deployed to this host. Provides SSO across a cluster.
-->
<Valve className="org.jboss.web.tomcat.tc5.sso.ClusteredSingleSignOn"
- debug="2" maxEmptyLife="11" processExpiresInterval="5"/>
+ debug="2" />
<!-- Default context parameters -->
<DefaultContext cookies="true" crossContext="true" override="true"/>
Deleted: branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/index.jsp
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/index.jsp 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/index.jsp 2006-12-08 03:07:36 UTC (rev 58921)
@@ -1,15 +0,0 @@
-<%
- // Give the session a quick timeout so the timeout test can be short
- session.setMaxInactiveInterval(5);
-%>
-
-<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
-<html>
-<head>
- <title>A Secure Web App</title>
-</head>
-
-<body>
-<h1>A Secure Web App</h1>
-</body>
-</html>
Deleted: branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/isValid.jsp
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/isValid.jsp 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/isValid.jsp 2006-12-08 03:07:36 UTC (rev 58921)
@@ -1 +0,0 @@
-<% response.setHeader("X-SessionValid", session.isValid()); %>
\ No newline at end of file
Modified: branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/web-sso-expire.xml
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/web-sso-expire.xml 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/testsuite/src/resources/web/sso/war/web-sso-expire.xml 2006-12-08 03:07:36 UTC (rev 58921)
@@ -5,7 +5,7 @@
<web-app>
<description>WebApp Integration Tests</description>
- <!--distributable/-->
+ <distributable/>
<servlet>
<servlet-name>LogoutServlet</servlet-name>
<servlet-class>org.jboss.test.web.servlets.LogoutServlet</servlet-class>
@@ -16,10 +16,6 @@
<url-pattern>/Logout</url-pattern>
</servlet-mapping>
- <session-config>
- <session-timeout>1</session-timeout>
- </session-config>
-
<security-constraint>
<web-resource-collection>
<web-resource-name>Restricted</web-resource-name>
@@ -32,6 +28,10 @@
</auth-constraint>
</security-constraint>
+ <session-config>
+ <session-timeout>1</session-timeout>
+ </session-config>
+
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
Modified: branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/session/JBossManager.java
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/session/JBossManager.java 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/session/JBossManager.java 2006-12-08 03:07:36 UTC (rev 58921)
@@ -208,15 +208,6 @@
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_;
Modified: branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/ClusteredSingleSignOn.java
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/ClusteredSingleSignOn.java 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/ClusteredSingleSignOn.java 2006-12-08 03:07:36 UTC (rev 58921)
@@ -8,36 +8,22 @@
import org.jboss.web.tomcat.tc5.Tomcat5;
-import org.jboss.web.tomcat.tc5.session.JBossManager;
import java.io.IOException;
import java.security.Principal;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import javax.management.ObjectName;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
-import org.apache.catalina.Lifecycle;
-import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
-import org.apache.catalina.LifecycleListener;
-import org.apache.catalina.Manager;
import org.apache.catalina.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;
-import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
-
/**
* A <strong>Valve</strong> that supports a "single sign on" user experience,
* where the security identity of a user who successfully authenticates to one
@@ -60,13 +46,8 @@
*/
public class ClusteredSingleSignOn
extends org.apache.catalina.authenticator.SingleSignOn
- implements LifecycleListener
{
- /** By default we process expired SSOs no more often than once per minute */
- public static final int DEFAULT_PROCESS_EXPIRES_INTERVAL = 60;
- /** By default we let SSOs without active sessions live for 30 mins */
- public static final int DEFAULT_MAX_EMPTY_LIFE = 1800;
-
+
// Override the superclass value
static
{
@@ -96,30 +77,6 @@
*/
private String treeCacheName = Tomcat5.DEFAULT_CACHE_NAME;
- /** Currently started Managers that have associated as session with an SSO */
- private Set activeManagers = Collections.synchronizedSet(new HashSet());
-
- /** Max number of ms an SSO with no active sessions will be usable by a request */
- private int maxEmptyLife = DEFAULT_MAX_EMPTY_LIFE * 1000;
-
- /**
- * Minimum number of ms since the last processExpires() run
- * before a new run is allowed.
- */
- private int processExpiresInterval = DEFAULT_PROCESS_EXPIRES_INTERVAL * 1000;
-
- /** Timestamp of the last processExpires() run */
- private long lastProcessExpires = System.currentTimeMillis();
-
- /**
- * Map<String, Long> containing the ids of SSOs with no active sessions
- * and the time at which they entered that state
- */
- private Map emptySSOs = new ConcurrentHashMap();
-
- /** Used for sync locking of processExpires runs */
- private final Object mutex = new Object();
-
// ------------------------------------------------------------- Properties
/**
@@ -238,105 +195,7 @@
}
}
- /**
- * Gets the max number of seconds an SSO with no active sessions will be
- * usable by a request.
- *
- * @return a non-negative number
- *
- * @see #DEFAULT_MAX_EMPTY_LIFE *
- * @see #setMaxEmptyLife()
- */
- public int getMaxEmptyLife()
- {
- return (maxEmptyLife / 1000);
- }
-
- /**
- * Sets the maximum number of seconds an SSO with no active sessions will be
- * usable by a request.
- * <p>
- * A positive value for this property allows a user to continue to use an SSO
- * even after all the sessions associated with it have been expired. It does not
- * keep an SSO alive if a session associated with it has been invalidated due to
- * an <code>HttpSession.invalidate()</code> call.
- * </p>
- * <p>
- * The primary purpose of this property is to avoid the situation where a server
- * on which all of an SSO's sessions lives is shutdown, thus expiring all the
- * sessions and causing the invalidation of the SSO. A positive value for this
- * property would give the user an opportunity to fail over to another server
- * and maintain the SSO.
- * </p>
- *
- * @param maxEmptyLife a non-negative number
- *
- * @throws IllegalArgumentException if <code>maxEmptyLife < 0</code>
- */
- public void setMaxEmptyLife(int maxEmptyLife)
- {
- if (maxEmptyLife < 0)
- throw new IllegalArgumentException("maxEmptyLife must be >= 0");
-
- this.maxEmptyLife = maxEmptyLife * 1000;
- }
-
-
- /**
- * Gets the minimum number of seconds since the start of the last check for overaged
- * SSO's with no active sessions before a new run is allowed.
- *
- * @return a positive number
- *
- * @see #DEFAULT_PROCESS_EXPIRES_INTERVAL
- * @see #setMaxEmptyLife()
- * @see #setProcessExpiresInterval(int)
- */
- public int getProcessExpiresInterval()
- {
- return processExpiresInterval / 1000;
- }
-
- /**
- * Sets the minimum number of seconds since the start of the last check for overaged
- * SSO's with no active sessions before a new run is allowed. During this check,
- * any such overaged SSOs will be invalidated.
- * <p>
- * Note that setting this value does not imply that a check will be performed
- * every <code>processExpiresInterval</code> seconds, only that it will not
- * be performed more often than that.
- * </p>
- *
- * @param processExpiresInterval a non-negative number. <code>0</code> means
- * the overage check can be performed whenever
- * the container wishes to.
- *
- * @throws IllegalArgumentException if <code>processExpiresInterval < 1</code>
- *
- * @see #setMaxEmptyLife()
- */
- public void setProcessExpiresInterval(int processExpiresInterval)
- {
- if (processExpiresInterval < 0)
- throw new IllegalArgumentException("processExpiresInterval must be >= 0");
-
- this.processExpiresInterval = processExpiresInterval * 1000;
- }
-
-
- /**
- * Gets the timestamp of the start of the last check for overaged
- * SSO's with no active sessions.
- *
- * @see #setProcessExpiresInterval(int)
- */
- public long getLastProcessExpires()
- {
- return lastProcessExpires;
- }
-
-
// ------------------------------------------------------ Lifecycle Methods
@@ -433,112 +292,25 @@
if (ssoId == null)
return;
- try
+ // Was the session destroyed as the result of a timeout?
+ // 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 ((session.getMaxInactiveInterval() > 0)
+ && (System.currentTimeMillis() - session.getLastAccessedTime() >=
+ session.getMaxInactiveInterval() * 1000))
{
- // Was the session destroyed as the result of a timeout or
- // the undeployment of the containing webapp?
- // If so, we'll just remove the expired session from the
- // SSO. If the session was logged out, we'll log out
- // of all sessions associated with the SSO.
- if (isSessionTimedOut(session) || isManagerStopped(session))
- {
- removeSession(ssoId, session);
-
- // Quite poor. We hijack the caller thread (the Tomcat background thread)
- // to do our cleanup of expired sessions
- processExpires();
- }
- else
- {
- // The session was logged out.
- logout(ssoId);
- }
+ removeSession(ssoId, session);
}
- catch (Exception e)
+ else
{
- // Don't propagate back to the webapp; we don't want to disrupt
- // the session expiration process
- getContainer().getLogger().error("Caught exception updating SSO " + ssoId +
- " following destruction of session " +
- session.getId(), e);
+ // 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
@@ -596,11 +368,10 @@
}
// Look up the cached Principal associated with this cookie value
- String ssoId = cookie.getValue();
if (getContainer().getLogger().isDebugEnabled())
- getContainer().getLogger().debug(" Checking for cached principal for " + ssoId);
+ getContainer().getLogger().debug(" Checking for cached principal for " + cookie.getValue());
SingleSignOnEntry entry = getSingleSignOnEntry(cookie.getValue());
- if (entry != null && isValid(ssoId, entry))
+ if (entry != null)
{
Principal ssoPrinc = entry.getPrincipal();
// have to deal with the fact that the entry may not have an
@@ -662,48 +433,9 @@
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.getId() +
- " 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);
- }
+ // If we made a change, notify any cluster
+ if (added && ssoClusterManager != null)
+ ssoClusterManager.addSession(ssoId, session);
}
@@ -758,11 +490,6 @@
if (getContainer().getLogger().isDebugEnabled())
getContainer().getLogger().debug("Deregistering sso id '" + ssoId + "'");
-
- // It's possible we don't have the SSO locally but it's in
- // the emptySSOs map; if so remove it
- emptySSOs.remove(ssoId);
-
// Look up and remove the corresponding SingleSignOnEntry
SingleSignOnEntry sso = null;
synchronized (cache)
@@ -777,8 +504,8 @@
Session sessions[] = sso.findSessions();
for (int i = 0; i < sessions.length; i++)
{
- if (getContainer().getLogger().isDebugEnabled())
- getContainer().getLogger().debug(" Invalidating session " + sessions[i]);
+ if (getContainer().getLogger().isTraceEnabled())
+ getContainer().getLogger().trace(" Invalidating session " + sessions[i]);
// Remove from reverse cache first to avoid recursion
synchronized (reverse)
{
@@ -950,12 +677,12 @@
reverse.remove(session);
}
-// // If there are no sessions left in the SingleSignOnEntry,
-// // deregister the entry.
-// if (entry.getSessionCount() == 0)
-// {
-// deregister(ssoId);
-// }
+ // If there are no sessions left in the SingleSignOnEntry,
+ // deregister the entry.
+ if (entry.getSessionCount() == 0)
+ {
+ deregister(ssoId);
+ }
}
@@ -1119,35 +846,6 @@
}
}
-
- /**
- * Callback from the SSOManager when it detects an SSO without
- * any active sessions across the cluster
- */
- void notifySSOEmpty(String ssoId)
- {
- Object obj = emptySSOs.put(ssoId, new Long(System.currentTimeMillis()));
-
- if (obj == null && getContainer().getLogger().isDebugEnabled())
- {
- getContainer().getLogger().debug("Notified that SSO " + ssoId + " is empty");
- }
- }
-
- /**
- * Callback from the SSOManager when it detects an SSO that
- * has active sessions across the cluster
- */
- void notifySSONotEmpty(String ssoId)
- {
- Object obj = emptySSOs.remove(ssoId);
-
- if (obj != null && getContainer().getLogger().isDebugEnabled())
- {
- getContainer().getLogger().debug("Notified that SSO " + ssoId +
- " is no longer empty");
- }
- }
// ------------------------------------------------------- Private Methods
@@ -1203,66 +901,5 @@
}
}
}
-
-
- private void processExpires()
- {
- long now = 0L;
- synchronized (mutex)
- {
- now = System.currentTimeMillis();
-
- if (now - lastProcessExpires > processExpiresInterval)
- {
- lastProcessExpires = now;
- }
- else
- {
- return;
- }
- }
-
- clearExpiredSSOs(now);
- }
-
- private synchronized void clearExpiredSSOs(long now)
- {
- for (Iterator iter = emptySSOs.entrySet().iterator(); iter.hasNext();)
- {
- Map.Entry entry = (Map.Entry) iter.next();
- if ( (now - ((Long) entry.getValue()).longValue()) > maxEmptyLife)
- {
- String ssoId = (String) entry.getKey();
- if (getContainer().getLogger().isDebugEnabled())
- {
- getContainer().getLogger().debug("Invalidating expired SSO " + ssoId);
- }
- logout(ssoId);
- }
- }
- }
-
- private boolean isValid(String ssoId, SingleSignOnEntry entry)
- {
- boolean valid = true;
- if (entry.getSessionCount() == 0)
- {
- Long expired = (Long) emptySSOs.get(ssoId);
- if (expired != null
- && (System.currentTimeMillis() - expired.longValue()) > maxEmptyLife)
- {
- valid = false;
-
- if (getContainer().getLogger().isDebugEnabled())
- {
- getContainer().getLogger().debug("Invalidating expired SSO " + ssoId);
- }
-
- logout(ssoId);
- }
- }
-
- return valid;
- }
}
\ No newline at end of file
Modified: branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/TreeCacheSSOClusterManager.java
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/TreeCacheSSOClusterManager.java 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/TreeCacheSSOClusterManager.java 2006-12-08 03:07:36 UTC (rev 58921)
@@ -1,29 +1,15 @@
/*
- * 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.
+ * JBoss, the OpenSource WebOS
*
- * 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.
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
*/
package org.jboss.web.tomcat.tc5.sso;
import java.io.Serializable;
import java.security.Principal;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.Set;
import javax.management.MBeanServer;
@@ -105,19 +91,20 @@
// ------------------------------------------------------- Instance Fields
/**
- * SSO id which the thread is currently storing to the cache
+ * List of SSO ids which this object is currently storing to the cache
*/
- private ThreadLocal beingLocallyAdded = new ThreadLocal();
+ private LinkedList beingLocallyAdded = new LinkedList();
/**
- * SSO id which a thread is currently removing from the cache
+ * List of SSO ids which this object is currently removing from the cache
*/
- private ThreadLocal beingLocallyRemoved = new ThreadLocal();
+ private LinkedList beingLocallyRemoved = new LinkedList();
/**
- * SSO id which the thread is deregistering due to removal on another node
+ * List of SSO ids which are being deregistered due to removal on another
+ * node
*/
- private ThreadLocal beingRemotelyRemoved = new ThreadLocal();
+ private LinkedList beingRemotelyRemoved = new LinkedList();
/**
* ObjectName of the TreeCache
@@ -130,6 +117,12 @@
private String cacheName = null;
/**
+ * CredentialUpdater used to allow asynchronous updates of
+ * SSO credentials
+ */
+ private CredentialUpdater credentialUpdater = null;
+
+ /**
* Transaction Manager
*/
private TransactionManager tm = null;
@@ -174,11 +167,6 @@
*/
private boolean missingCacheErrorLogged = false;
- /**
- * Our node's address in the cluster.
- */
- private Serializable localAddress = null;
-
// ---------------------------------------------------------- Constructors
@@ -189,21 +177,9 @@
{
// 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()
@@ -240,7 +216,7 @@
return;
}
- removeAsTreeCacheListener();
+ removeAsTreeCacheListener(cacheObjectName);
this.tm = null;
this.cacheObjectName = objectName;
@@ -262,11 +238,16 @@
"before ClusteredSingleSignOn can handle requests");
}
}
+ else if (started)
+ {
+ // Confirm the cache has a transaction manager
+ // and fail if not found
+ findTransactionManager();
+ }
}
// ----------------------------------------------------- SSOClusterManager
-
/**
* Notify the cluster of the addition of a Session to an SSO session.
*
@@ -280,8 +261,9 @@
return;
}
- if (!checkTreeCacheAvailable())
+ if (false == isTreeCacheAvailable(false))
{
+ logMissingCacheError();
return;
}
@@ -295,11 +277,6 @@
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)
@@ -309,7 +286,7 @@
tm.begin();
Set sessions = getSessionSet(fqn, true);
- sessions.add(new SessionAddress(session.getId(), localAddress));
+ sessions.add(session.getId());
putInTreeCache(fqn, sessions);
}
catch (Exception e)
@@ -319,7 +296,7 @@
if(doTx)
tm.setRollbackOnly();
}
- catch (Exception ignored)
+ catch (Exception x)
{
}
String sessId = (session == null ? "NULL" : session.getId());
@@ -368,20 +345,23 @@
*/
public void logout(String ssoId)
{
- if (!checkTreeCacheAvailable())
+ if (false == isTreeCacheAvailable(false))
{
+ logMissingCacheError();
return;
}
// Check whether we are already handling this removal
- if (ssoId.equals(beingLocallyRemoved.get()))
+ //synchronized (beingLocallyRemoved)
{
- return;
- }
-
- // Add this SSO to our list of in-process local removals so
- // this.nodeRemoved() will ignore the removal
- beingLocallyRemoved.set(ssoId);
+ if (beingLocallyRemoved.contains(ssoId))
+ {
+ return;
+ }
+ // Add this SSO to our list of in-process local removals so
+ // this.nodeRemoved() will ignore the removal
+ beingLocallyRemoved.add(ssoId);
+ }
if (log.isTraceEnabled())
{
@@ -402,7 +382,10 @@
}
finally
{
- beingLocallyRemoved.set(null);
+ //synchronized (beingLocallyRemoved)
+ {
+ beingLocallyRemoved.remove(ssoId);
+ }
}
}
@@ -418,16 +401,20 @@
*/
public SingleSignOnEntry lookup(String ssoId)
{
- if (!checkTreeCacheAvailable())
+ if (false == isTreeCacheAvailable(false))
{
+ logMissingCacheError();
return null;
}
SingleSignOnEntry entry = null;
// Find the latest credential info from the cluster
Fqn fqn = getCredentialsFqn(ssoId);
+ //UserTransaction tx = null;
try
{
+ //tx = getNewTransaction();
+ //tx.begin();
SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn);
if (data != null)
{
@@ -436,9 +423,20 @@
data.getUsername(),
data.getPassword());
}
+ //tx.commit();
}
catch (Exception e)
{
+ /*
+ if (tx != null)
+ {
+ try
+ {
+ tx.rollback();
+ }
+ catch (Exception x) {}
+ }
+ */
log.error("caught exception looking up SSOCredentials for SSO id " +
ssoId, e);
}
@@ -458,8 +456,9 @@
public void register(String ssoId, String authType,
String username, String password)
{
- if (!checkTreeCacheAvailable())
+ if (false == isTreeCacheAvailable(false))
{
+ logMissingCacheError();
return;
}
@@ -480,32 +479,33 @@
*/
public void removeSession(String ssoId, Session session)
{
- if (ssoId == null || session == null)
+ if (false == isTreeCacheAvailable(false))
{
+ logMissingCacheError();
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()))
+ //synchronized(beingRemotelyRemoved)
{
- return;
+ if (beingRemotelyRemoved.contains(ssoId))
+ {
+ 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;
+ 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.
@@ -518,29 +518,23 @@
Set sessions = getSessionSet(fqn, false);
if (sessions != null)
{
- sessions.remove(new SessionAddress(session.getId(), localAddress));
-
- if (log.isTraceEnabled())
+ sessions.remove(session.getId());
+ if (sessions.size() == 0)
{
- log.trace("removed session " + session.getId() +
- " from cached session set for SSO " + ssoId + " -- " +
- sessions.size() + " sessions remain");
+ // Add this SSO to our list of in-process local removals so
+ // this.nodeRemoved() will ignore the removal
+ //synchronized (beingLocallyRemoved)
+ {
+ beingLocallyRemoved.add(ssoId);
+ }
+ removing = true;
+ // No sessions left; remove node
+ removeFromTreeCache(getSingleSignOnFqn(ssoId));
}
-
-// 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
-// {
+ else
+ {
putInTreeCache(fqn, sessions);
-// }
+ }
}
}
catch (Exception e)
@@ -560,18 +554,21 @@
}
finally
{
-// try
-// {
-// if (removing)
-// {
-// beingLocallyRemoved.set(null);
-// }
-// }
-// finally
-// {
+ try
+ {
+ if (removing)
+ {
+ //synchronized (beingLocallyRemoved)
+ {
+ beingLocallyRemoved.remove(ssoId);
+ }
+ }
+ }
+ finally
+ {
if (doTx)
endTransaction();
-// }
+ }
}
}
@@ -589,8 +586,9 @@
public void updateCredentials(String ssoId, String authType,
String username, String password)
{
- if (!checkTreeCacheAvailable())
+ if (false == isTreeCacheAvailable(false))
{
+ logMissingCacheError();
return;
}
@@ -662,17 +660,20 @@
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()))
+ //synchronized(beingLocallyRemoved)
{
- return;
+ if (beingLocallyRemoved.contains(ssoId))
+ {
+ return;
+ }
}
- beingRemotelyRemoved.set(ssoId);
+ //synchronized (beingRemotelyRemoved)
+ {
+ beingRemotelyRemoved.add(ssoId);
+ }
try
{
@@ -685,7 +686,10 @@
}
finally
{
- beingRemotelyRemoved.set(null);
+ //synchronized(beingRemotelyRemoved)
+ {
+ beingRemotelyRemoved.remove(ssoId);
+ }
}
}
@@ -708,30 +712,21 @@
*/
public void nodeModified(Fqn fqn)
{
- String type = getTypeFromFqn(fqn);
- if (CREDENTIALS.equals(type))
+ // We are only interested in changes to the CREDENTIALS node
+ if (CREDENTIALS.equals(getTypeFromFqn(fqn)) == false)
{
- handleCredentialUpdate(fqn);
+ return;
}
- else if (SESSIONS.equals(type))
- {
- handleSessionSetChange(fqn);
- }
- }
+ String ssoId = getIdFromFqn(fqn);
- /**
- *
- * @param fqn an Fqn that points to the CREDENTIALS node of an SSO
- */
- private void handleCredentialUpdate(Fqn fqn)
- {
- String ssoId = getIdFromFqn(fqn); // won't be null per the API contract
-
// Ignore invocations that come as a result of our additions
- if (ssoId.equals(beingLocallyAdded.get()))
+ //synchronized(beingLocallyAdded)
{
- return;
+ if (beingLocallyAdded.contains(ssoId))
+ {
+ return;
+ }
}
SingleSignOnEntry sso = ssoValve.localLookup(ssoId);
@@ -747,72 +742,11 @@
}
// 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();
+ credentialUpdater.enqueue(sso, ssoId);
+ }
- if (log.isTraceEnabled())
- {
- log.trace("CredentialUpdater: Updating credentials for SSO " + sso);
- }
- synchronized (sso)
- {
- // Use the existing principal
- Principal p = sso.getPrincipal();
- sso.updateCredentials(p, authType, username, password);
- }
- }
- }
- catch (Exception e)
- {
- log.error("failed to update credentials for SSO " + ssoId, e);
- }
- }
-
/**
- *
- * @param fqn an Fqn that points to the SESSIONS node of an SSO
- */
- private void handleSessionSetChange(Fqn fqn)
- {
- // Here we *want* to handle local activity as well as remote,
- // as the distributed cache set of sessions is the only
- // complete representation
-
- String ssoId = getIdFromFqn(fqn);
- try
- {
- Set sessions = (Set) getFromTreeCache(fqn);
- if (sessions == null)
- {
- // don't think we'd get this event;
- // in any case we don't deal with this
- }
- else if (sessions.size() == 0)
- {
- ssoValve.notifySSOEmpty(ssoId);
- }
- else
- {
- ssoValve.notifySSONotEmpty(ssoId);
- }
- }
- catch (Exception e)
- {
- log.error("failed in check for empty SSO " + ssoId, e);
- }
- }
-
- /**
* Does nothing
*/
public void viewChange(View new_view)
@@ -884,12 +818,15 @@
throw new LifecycleException
("TreeCacheSSOClusterManager already Started");
}
+
+ // Start the thread we use to clear nodeModified events
+ credentialUpdater = new CredentialUpdater();
try
{
- if (isTreeCacheAvailable(true))
+ if (tm == null && isTreeCacheAvailable(true))
{
- integrateWithCache();
+ findTransactionManager();
}
}
catch (Exception e)
@@ -922,7 +859,9 @@
throw new LifecycleException
("TreeCacheSSOClusterManager not Started");
}
-
+
+ credentialUpdater.stop();
+
started = false;
// Notify our interested LifecycleListeners
@@ -964,12 +903,7 @@
*/
private String getIdFromFqn(Fqn fqn)
{
- String id = null;
- if (fqn.size() > 1 && SSO.equals(fqn.get(0)))
- {
- id = (String) fqn.get(1);
- }
- return id;
+ return (String) fqn.get(1);
}
private Set getSessionSet(Fqn fqn, boolean create)
@@ -988,30 +922,25 @@
* 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.
+ * @return the last element in the Fqn -- either
+ * {@link #CREDENTIALS CREDENTIALS} or {@link #SESSIONS SESSIONS}.
*/
private String getTypeFromFqn(Fqn fqn)
{
- String type = null;
- if (fqn.size() > 2 && SSO.equals(fqn.get(0)))
- type = (String) fqn.get(2);
- return type;
+ return (String) fqn.get(fqn.size() - 1);
}
/**
- * Obtains needed configuration information from the tree cache.
* Invokes "getTransactionManager" on the tree cache, caching the
* result or throwing an IllegalStateException if one is not found.
- * Also get our cluster-wide unique local address from the cache.
*
* @throws Exception
*/
- private void configureFromCache() throws Exception
- {
- tm = (TransactionManager) server.getAttribute(getCacheObjectName(),
- "TransactionManager");
+ private void findTransactionManager() throws Exception
+ {
+ tm = (TransactionManager) server.invoke(getCacheObjectName(),
+ "getTransactionManager",
+ null, new String[] {});
if (tm == null)
{
@@ -1020,15 +949,6 @@
"configure a valid " +
"TransactionManagerLookupClass");
}
-
- // Find out our address
- Object address = server.getAttribute(cacheObjectName, "LocalAddress");
- // In reality this is a JGroups IpAddress, but the API says
- // "Object" so we have to be sure its Serializable
- if (address instanceof Serializable)
- localAddress = (Serializable) address;
- else
- localAddress = address.toString();
}
@@ -1074,19 +994,13 @@
{
try
{
- // If Tomcat5 overrides the default cache name, it will do so
- // after we are started. So we need to configure ourself here
- // and throw an exception if there is a problem. Having this
- // here also allows us to recover if our cache is started
- // after we are
- if (started)
- integrateWithCache();
+ registerAsTreeCacheListener(cacheObjectName);
setMissingCacheErrorLogged(false);
}
catch (Exception e)
{
- log.error("Caught exception configuring from cache " +
- cacheObjectName, e);
+ log.error("Caught exception registering as listener to " +
+ cacheObjectName, e);
available = false;
}
}
@@ -1095,14 +1009,6 @@
}
return treeCacheAvailable;
}
-
- private boolean checkTreeCacheAvailable()
- {
- boolean avail = isTreeCacheAvailable(false);
- if (!avail)
- logMissingCacheError();
- return avail;
- }
private void putInTreeCache(Fqn fqn, Object data) throws Exception
{
@@ -1110,26 +1016,16 @@
server.invoke(getCacheObjectName(), "put", args, PUT_SIGNATURE);
}
- private void integrateWithCache() throws Exception
- {
- // Ensure we have a transaction manager and a cluster-wide unique address
- configureFromCache();
-
- registerAsTreeCacheListener();
-
- log.debug("Successfully integrated with cache service " + cacheObjectName);
- }
-
-
/**
* Invokes an operation on the JMX server to register ourself as a
* listener on the TreeCache service.
*
* @throws Exception
*/
- private void registerAsTreeCacheListener() throws Exception
+ private void registerAsTreeCacheListener(ObjectName listenTo)
+ throws Exception
{
- server.invoke(cacheObjectName, "addTreeCacheListener",
+ server.invoke(listenTo, "addTreeCacheListener",
new Object[]{this},
new String[]{TreeCacheListener.class.getName()});
registeredAsListener = true;
@@ -1142,11 +1038,12 @@
*
* @throws Exception
*/
- private void removeAsTreeCacheListener() throws Exception
+ private void removeAsTreeCacheListener(ObjectName removeFrom)
+ throws Exception
{
- if (registeredAsListener && cacheObjectName != null)
+ if (registeredAsListener && removeFrom != null)
{
- server.invoke(cacheObjectName, "removeTreeCacheListener",
+ server.invoke(removeFrom, "removeTreeCacheListener",
new Object[]{this},
new String[]{TreeCacheListener.class.getName()});
}
@@ -1179,23 +1076,41 @@
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);
-
+ //synchronized (beingLocallyAdded)
+ {
+ beingLocallyAdded.add(ssoId);
+ }
+ //UserTransaction tx = null;
try
{
+ //tx = getNewTransaction();
+ //tx.begin();
putInTreeCache(getCredentialsFqn(ssoId), data);
+ //tx.commit();
}
catch (Exception e)
{
+ /*
+ if (tx != null)
+ {
+ try
+ {
+ tx.rollback();
+ }
+ catch (Exception x) {}
+ }
+ */
log.error("Exception attempting to add TreeCache nodes for SSO " +
ssoId, e);
}
finally
{
- beingLocallyAdded.set(null);
+ //synchronized (beingLocallyAdded)
+ {
+ beingLocallyAdded.remove(ssoId);
+ }
}
}
@@ -1229,6 +1144,219 @@
}
}
+ // --------------------------------------------------------- Inner Classes
+
+ /**
+ * Spawns a thread to handle updates of credentials
+ *
+ * TODO This was added in the early days of JBossCache when invoking
+ * a get() on a node in the middle of a TreeCacheListener
+ * notification would cause a deadlock. Should not be necessary now.
+ */
+ private class CredentialUpdater
+ implements Runnable
+ {
+ private HashSet awaitingUpdate = new HashSet();
+ private Thread updateThread;
+ private boolean updateThreadSleeping = false;
+ private boolean queueEmpty = true;
+ private boolean stopped = false;
+
+ private CredentialUpdater()
+ {
+ updateThread =
+ new Thread(this, "SSOClusterManager.CredentialUpdater");
+ updateThread.setDaemon(true);
+ updateThread.start();
+ }
+
+ // ------------------------------------------------------ Runnable
+
+ public void run()
+ {
+ while (!stopped)
+ {
+ // Ensure that no runtime exceptions kill this thread
+ try
+ {
+ updateThreadSleeping = false;
+ // Get the current list of ids awaiting processing
+ SSOWrapper[] ssos = null;
+ synchronized (awaitingUpdate)
+ {
+ ssos = new SSOWrapper[awaitingUpdate.size()];
+ ssos = (SSOWrapper[]) awaitingUpdate.toArray(ssos);
+ awaitingUpdate.clear();
+ queueEmpty = true;
+ }
+
+ // Handle the credential update
+ for (int i = 0; i < ssos.length; i++)
+ {
+ processUpdate(ssos[i]);
+ }
+
+ // Wait for another invocation of enqueue(). But,
+ // first have to check in case it was invoked while we
+ // were processing the previous bunch
+ if (queueEmpty)
+ {
+ try
+ {
+ // There is a slight possibility here of a race condition
+ // between the above check for queueEmpty and another
+ // thread accessing enqueue()'s check of
+ // updateThreadSleeping. If this happens, the update
+ // will not be processed by the local node until the
+ // updateThread wakes up (30 secs) or is interrupted by
+ // another update. This situation is quite unlikely,
+ // as updates only happen 1) in odd configurations where
+ // CLIENT-CERT authentication is used for some apps and
+ // FORM or BASIC are used for others and 2) the user has
+ // first logged in to a CLIENT-CERT app and later logs in
+ // to a FORM/BASIC app. If such a race condition were to
+ // occur, the only downside would be that if the user
+ // accessed a FORM/BASIC app on this node before the local
+ // update is processed, they would have to log in again.
+ updateThreadSleeping = true;
+ updateThread.sleep(30000);
+ }
+ catch (InterruptedException e)
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("CredentialUpdater: interrupted");
+ }
+ // process the next bunch
+ }
+ }
+ else if (log.isTraceEnabled())
+ {
+ log.trace("CredentialUpdater: more updates added while " +
+ "handling existing updates");
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("CredentialUpdater thread caught an exception", e);
+ }
+ }
+ }
+
+ // ------------------------------------------------- Private Methods
+
+ /**
+ * Adds an SSO id to the set of those awaiting credential updating, and
+ * interrupts the update handler thread to notify it of the addition.
+ *
+ * @param sso the id of the SSO session whose local credentials
+ * are to be updated
+ */
+ private void enqueue(SingleSignOnEntry sso, String ssoId)
+ {
+ synchronized (awaitingUpdate)
+ {
+ awaitingUpdate.add(new SSOWrapper(sso, ssoId));
+ queueEmpty = false;
+ }
+ // Interrupt the update thread so it wakes up to process
+ // the enqueued update. Only do this if its "sleeping" flag
+ // is set so we don't inadvertently interrupt it while its
+ // blocked waiting for a TreeCache lock to clear
+ if (updateThreadSleeping)
+ {
+ updateThread.interrupt();
+ }
+ }
+
+ private void processUpdate(SSOWrapper wrapper)
+ {
+ if (wrapper.sso.getCanReauthenticate())
+ {
+ // No need to update
+ return;
+ }
+
+ Fqn fqn = getCredentialsFqn(wrapper.id);
+ //UserTransaction tx = null;
+ try
+ {
+ //tx = getNewTransaction();
+ //tx.begin();
+ 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();
+ //tx.commit();
+
+ if (log.isTraceEnabled())
+ {
+ log.trace("CredentialUpdater: Updating credentials for SSO " +
+ wrapper.sso);
+ }
+
+ synchronized (wrapper.sso)
+ {
+ // Use the existing principal
+ Principal p = wrapper.sso.getPrincipal();
+ wrapper.sso.updateCredentials(p, authType, username, password);
+ }
+ }
+ /*
+ else
+ {
+ tx.commit();
+ }
+ */
+
+ }
+ catch (Exception e)
+ {
+ /*
+ if (tx != null)
+ {
+ try
+ {
+ tx.rollback();
+ }
+ catch (Exception x) {}
+ }
+ */
+ log.error("Exception attempting to get SSOCredentials from " +
+ "TreeCache node " + fqn.toString(), e);
+ }
+ }
+
+ /**
+ * Stops the update handler thread.
+ */
+ private void stop()
+ {
+ stopped = true;
+ }
+
+ } // end CredentialUpdater
+
+
+ /**
+ * Wrapper class that holds a SingleSignOnEntry and its id
+ */
+ private class SSOWrapper
+ {
+ private SingleSignOnEntry sso = null;
+ private String id = null;
+
+ private SSOWrapper(SingleSignOnEntry entry, String ssoId)
+ {
+ this.sso = entry;
+ this.id = ssoId;
+ }
+ }
+
// --------------------------------------------------------- Outer Classes
/**
@@ -1293,135 +1421,6 @@
}
} // end SSOCredentials
-
- static class SessionAddress implements Serializable
- {
- /** The serialVersionUID */
- private static final long serialVersionUID = -3702932999380140004L;
-
- Serializable address;
- String sessionId;
-
- SessionAddress(String sessionId, Serializable address)
- {
- this.sessionId = sessionId;
- this.address = address;
- }
- public boolean equals(Object obj)
- {
- if (this == obj)
- return true;
-
- if (!(obj instanceof SessionAddress))
- return false;
-
- SessionAddress other = (SessionAddress) obj;
-
- return (sessionId.equals(other.sessionId)
- && address.equals(other.address));
- }
-
- public int hashCode()
- {
- int total = (19 * 43) + sessionId.hashCode();
- return ((total * 43) + address.hashCode());
- }
-
-
- }
-
-// DISABLED UNTIL 4.0.6.CR1
-// /**
-// * Runnable that's run when the removal of a node from the cluster has been detected.
-// * Removes any SessionAddress objects associated with dead members from the
-// * session set of each SSO. Operates locally only so each node can independently clean
-// * its SSOs without concern about replication lock conflicts.
-// */
-// private class DeadMemberCleaner implements Runnable
-// {
-// public void run()
-// {
-// synchronized (cleanupMutex)
-// {
-// try
-// {
-// // Ensure we have a TransactionManager
-// if (tm == null)
-// configureFromCache();
-//
-// Set ids = getSSOIds();
-//
-// for (Iterator iter = ids.iterator(); iter.hasNext();)
-// {
-// cleanDeadMembersFromSSO((String) iter.next());
-//
-// }
-// }
-// catch (Exception e)
-// {
-// log.error("Caught exception cleaning sessions from dead cluster members from SSOs ", e);
-// }
-// }
-// }
-//
-// private void cleanDeadMembersFromSSO(String ssoId)
-// {
-// Fqn fqn = getSessionsFqn(ssoId);
-// boolean doTx = false;
-// try
-// {
-// // Don't start tx if there is already one associated with this thread.
-// if(tm.getTransaction() == null)
-// doTx = true;
-//
-// if(doTx)
-// tm.begin();
-//
-// Set sessions = getSessionSet(fqn, false, true);
-// if (sessions != null && sessions.size() > 0)
-// {
-// boolean changed = false;
-// for (Iterator iter = sessions.iterator(); iter.hasNext();)
-// {
-// SessionAddress session = (SessionAddress) iter.next();
-// if (!currentView.containsMember((Address) session.address))
-// {
-// iter.remove();
-// changed = true;
-// }
-// }
-//
-// if (changed)
-// {
-// if (sessions.size() == 0)
-// {
-// ssoValve.notifySSOEmpty(ssoId);
-// }
-//
-// putInTreeCache(fqn, sessions, true);
-// }
-// }
-// }
-// catch (Exception e)
-// {
-// try
-// {
-// if(doTx)
-// tm.setRollbackOnly();
-// }
-// catch (Exception ignored)
-// {
-// }
-// log.error("caught exception cleaning dead members from SSO " + ssoId, e);
-// }
-// finally
-// {
-// if (doTx)
-// endTransaction();
-// }
-// }
-// }
-
} // end TreeCacheSSOClusterManager
Modified: branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/mbeans-descriptors.xml
===================================================================
--- branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/mbeans-descriptors.xml 2006-12-08 01:43:14 UTC (rev 58920)
+++ branches/JBoss_4_0_3_SP1_CP/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/mbeans-descriptors.xml 2006-12-08 03:07:36 UTC (rev 58921)
@@ -20,19 +20,6 @@
description="Should we attempt to reauthenticate each request against the security Realm?"
type="boolean"/>
- <attribute name="maxEmptyLife"
- description="The maximum number of seconds an SSO with no active sessions will be usable by a request"
- type="int"/>
-
- <attribute name="processExpiresInterval"
- description="The maximum number of seconds an SSO with no active sessions will be usable by a request"
- type="int"/>
-
- <attribute name="lastProcessExpires"
- writeable="false"
- description="The timestamp of the start of the last check for overaged SSO's with no active sessions."
- type="long"/>
-
<attribute name="clusterManager"
description="SSOClusterManager to use for cluster support"
type="org.jboss.web.tomcat.tc5.sso.SSOClusterManager"/>
More information about the jboss-cvs-commits
mailing list