/* * 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. */ package org.jboss.test.cluster.defaultcfg.simpleweb.test; import java.util.Vector; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.ObjectName; import org.apache.catalina.Container; import org.apache.catalina.LifecycleException; import org.apache.catalina.Manager; import org.apache.catalina.Session; import org.apache.catalina.core.StandardContext; import org.apache.catalina.session.StandardManager; import org.apache.catalina.session.StandardSession; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.buddyreplication.NextMemberBuddyLocatorConfig; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationRegistry; import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.jmx.CacheJmxWrapper; import org.jboss.cache.notifications.event.EventImpl; import org.jboss.cache.notifications.event.Event.Type; import org.jboss.cache.pojo.PojoCache; import org.jboss.cache.pojo.PojoCacheFactory; import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper; import org.jboss.cache.transaction.BatchModeTransactionManager; import org.jboss.ha.cachemanager.CacheManager; import org.jboss.ha.cachemanager.DependencyInjectedConfigurationRegistry; import org.jboss.ha.framework.server.JChannelFactory; import org.jboss.test.JBossTestCase; import org.jboss.test.cluster.testutil.DelegatingMockCache; import org.jboss.test.cluster.testutil.MockTransactionManagerLookup; import org.jboss.test.cluster.web.mocks.MockEngine; import org.jboss.test.cluster.web.mocks.MockHost; import org.jboss.util.threadpool.BasicThreadPool; import org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn; import org.jboss.web.tomcat.service.sso.jbc.JBossCacheSSOClusterManager; import org.jboss.web.tomcat.service.sso.spi.FullyQualifiedSessionId; import org.jgroups.Address; import org.jgroups.View; import org.jgroups.ViewId; import org.jgroups.stack.IpAddress; /** * Test of the JBossCacheSSOClusterManager class. * * @author Brian Stansberry */ public class TreeCacheSSOClusterManagerUnitTestCase extends JBossTestCase { /** * Default global value for the cacheName property */ public static final String CACHE_OBJECT_NAME = "jboss.cache:service=ClusteredSSOCache"; private static IpAddress LOCAL_ADDRESS; private static IpAddress REMOTE_ADDRESS; private String bindAddress; private MBeanServer mbeanServer; private CacheManager cacheManager; public TreeCacheSSOClusterManagerUnitTestCase(String name) { super(name); } @Override protected void setUp() throws Exception { super.setUp(); bindAddress = System.getProperty("jgroups.bind.address"); System.setProperty("jgroups.bind.address", "127.0.0.1"); LOCAL_ADDRESS = new IpAddress("127.0.0.1", 11111); REMOTE_ADDRESS = new IpAddress("192.168.0.1", 11111); } @Override protected void tearDown() throws Exception { super.tearDown(); if (bindAddress == null) System.clearProperty("jgroups.bind.address"); else System.setProperty("jgroups.bind.address", bindAddress); if (cacheManager != null) { cacheManager.stop(); cacheManager = null; } if (mbeanServer != null) { MBeanServerFactory.releaseMBeanServer(mbeanServer); mbeanServer = null; } } public void testCacheJmxIntegration() throws Exception { log.debug("+++ testCacheJmxIntegration()"); mbeanServer = MBeanServerFactory.createMBeanServer("cacheJmxTest"); CacheJmxWrapper wrapper = null; try { // Register a cache wrapper = new CacheJmxWrapper(); // JBAS-4097 -- don't use a TransactionManagerLookup that will // bind DummyTransactionManager into JNDI, as that will screw // up other tests wrapper.setTransactionManagerLookupClass(MockTransactionManagerLookup.class.getName()); wrapper.setCacheMode("REPL_SYNC"); mbeanServer.registerMBean(wrapper, new ObjectName(CACHE_OBJECT_NAME)); wrapper.create(); wrapper.start(); // Build up an SSO infrastructure based on LOCAL_ADDRESS JBossCacheSSOClusterManager localSSOManager = new JBossCacheSSOClusterManager(); MockSSOValve localValve = new MockSSOValve(mbeanServer); localValve.setCacheConfig(CACHE_OBJECT_NAME); localValve.setClusterManager(localSSOManager); localSSOManager.setSSOLocalManager(localValve); localSSOManager.start(); // Create an SSO that will have two sessions from local valve localSSOManager.register("1", "FORM", "Brian", "password"); Manager localSessMgr1 = getSessionManager("A"); Session sess1 = new MockSession(localSessMgr1, "1"); localSSOManager.addSession("1", getFullyQualifiedSessionId(sess1)); Manager localSessMgr2 = getSessionManager("B"); Session sess2 = new MockSession(localSessMgr2, "2"); localSSOManager.addSession("1", getFullyQualifiedSessionId(sess2)); // Confirm that data is cached properly assertEquals("SSO 1 has correct number of sessions", 2, localSSOManager.getSessionCount("1")); } finally { try { if (wrapper != null) { wrapper.stop(); wrapper.destroy(); } } catch (Exception ignored) { } } } public void testPojoCacheJmxIntegration() throws Exception { log.debug("+++ testPojoCacheJmxIntegration()"); mbeanServer = MBeanServerFactory.createMBeanServer("pojoCacheTest"); PojoCacheJmxWrapper wrapper = null; try { // Register a cache wrapper = new PojoCacheJmxWrapper(); // JBAS-4097 -- don't use a TransactionManagerLookup that will // bind DummyTransactionManager into JNDI, as that will screw // up other tests wrapper.setTransactionManagerLookupClass(MockTransactionManagerLookup.class.getName()); wrapper.setCacheMode("REPL_SYNC"); mbeanServer.registerMBean(wrapper, new ObjectName(CACHE_OBJECT_NAME)); wrapper.create(); wrapper.start(); // Build up an SSO infrastructure based on LOCAL_ADDRESS JBossCacheSSOClusterManager localSSOManager = new JBossCacheSSOClusterManager(); MockSSOValve localValve = new MockSSOValve(mbeanServer); localValve.setCacheConfig(CACHE_OBJECT_NAME); localValve.setClusterManager(localSSOManager); localSSOManager.setSSOLocalManager(localValve); localSSOManager.start(); // Create an SSO that will have two sessions from local valve localSSOManager.register("1", "FORM", "Brian", "password"); Manager localSessMgr1 = getSessionManager("A"); Session sess1 = new MockSession(localSessMgr1, "1"); localSSOManager.addSession("1", getFullyQualifiedSessionId(sess1)); Manager localSessMgr2 = getSessionManager("B"); Session sess2 = new MockSession(localSessMgr2, "2"); localSSOManager.addSession("1", getFullyQualifiedSessionId(sess2)); // Confirm that data is cached properly assertEquals("SSO 1 has correct number of sessions", 2, localSSOManager.getSessionCount("1")); } finally { try { if (wrapper != null) { wrapper.stop(); wrapper.destroy(); } } catch (Exception ignored) { } } } public void testPojoCacheIntegration() throws Exception { log.debug("+++ testPojoCacheIntegration()"); CacheManager cacheManager = getCacheManager(); // Register a cache Configuration config = new Configuration(); // JBAS-4097 -- don't use a TransactionManagerLookup that will // bind DummyTransactionManager into JNDI, as that will screw // up other tests config.setTransactionManagerLookupClass(MockTransactionManagerLookup.class.getName()); config.setCacheMode("REPL_SYNC"); PojoCache pc = PojoCacheFactory.createCache(config, false); cacheManager.registerPojoCache(pc, ClusteredSingleSignOn.DEFAULT_CACHE_NAME); // Build up an SSO infrastructure based on LOCAL_ADDRESS JBossCacheSSOClusterManager localSSOManager = new JBossCacheSSOClusterManager(); MockSSOValve localValve = new MockSSOValve(mbeanServer); localValve.setCacheConfig(ClusteredSingleSignOn.DEFAULT_CACHE_NAME); localValve.setClusterManager(localSSOManager); localSSOManager.setSSOLocalManager(localValve); localSSOManager.start(); // Create an SSO that will have two sessions from local valve localSSOManager.register("1", "FORM", "Brian", "password"); Manager localSessMgr1 = getSessionManager("A"); Session sess1 = new MockSession(localSessMgr1, "1"); localSSOManager.addSession("1", getFullyQualifiedSessionId(sess1)); Manager localSessMgr2 = getSessionManager("B"); Session sess2 = new MockSession(localSessMgr2, "2"); localSSOManager.addSession("1", getFullyQualifiedSessionId(sess2)); // Confirm that data is cached properly assertEquals("SSO 1 has correct number of sessions", 2, localSSOManager.getSessionCount("1")); } // Disabled; buddy replication is acceptable /* public void testDisallowBuddyReplication() throws Exception { log.debug("+++ testDisallowBuddyReplication()"); buddyReplicationConfigTest(true); // Flush the cache manager cacheManager.stop(); cacheManager = null; buddyReplicationConfigTest(false); } private void buddyReplicationConfigTest(boolean enabled) throws Exception { CacheManager cacheManager = getCacheManager(); // Register a cache Cache cache = new DefaultCacheFactory().createCache(false); // JBAS-4097 -- don't use a TransactionManagerLookup that will // bind DummyTransactionManager into JNDI, as that will screw // up other tests cache.getConfiguration().setTransactionManagerLookupClass(MockTransactionManagerLookup.class.getName()); cache.getConfiguration().setCacheMode("REPL_SYNC"); // Configure buddy replication BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(enabled); brc.setBuddyPoolName("clusteredsso"); BuddyLocatorConfig blc = new NextMemberBuddyLocatorConfig(); brc.setBuddyLocatorConfig(blc); cache.getConfiguration().setBuddyReplicationConfig(brc); cacheManager.registerCache(cache, ClusteredSingleSignOn.DEFAULT_CACHE_NAME); // Build up an SSO infrastructure based on LOCAL_ADDRESS JBossCacheSSOClusterManager localSSOManager = new JBossCacheSSOClusterManager(); MockSSOValve localValve = new MockSSOValve(null); localValve.setCacheConfig(ClusteredSingleSignOn.DEFAULT_CACHE_NAME); localValve.setClusterManager(localSSOManager); localSSOManager.setSSOLocalManager(localValve); try { localSSOManager.start(); if (enabled) { fail("JBossCacheSSOClusterManager should not start with buddy replication enabled"); } } catch (LifecycleException le) { if (!enabled) { String msg = "Caught exception starting with BR disabled " + le.getLocalizedMessage(); log.error(msg, le); fail(msg); } // else we shouldn't start } } */ public void testDeadMemberCleanupWithPool() throws Exception { log.debug("+++ testDeadMemberCleanupWithPool()"); deadMemberCleanupTest(true); } public void testDeadMemberCleanupWithoutPool() throws Exception { log.debug("+++ testDeadMemberCleanupWithoutPool()"); deadMemberCleanupTest(false); } private void deadMemberCleanupTest(boolean usePool) throws Exception { mbeanServer = usePool ? MBeanServerFactory.createMBeanServer("deadMemberTest") : null; CacheManager cacheManager = getCacheManager(); // Register a cache Cache<Object, Object> delegate = new DefaultCacheFactory<Object, Object>().createCache(false); MockTreeCache cache = new MockTreeCache(delegate); // JBAS-4097 -- don't use a TransactionManagerLookup that will // bind DummyTransactionManager into JNDI, as that will screw // up other tests cache.getConfiguration().getRuntimeConfig().setTransactionManager(new BatchModeTransactionManager()); cacheManager.registerCache(cache, ClusteredSingleSignOn.DEFAULT_CACHE_NAME); if (usePool) { BasicThreadPool pool = new BasicThreadPool(); mbeanServer.registerMBean(pool, new ObjectName(JBossCacheSSOClusterManager.DEFAULT_THREAD_POOL_NAME)); } // Build up an SSO infrastructure based on LOCAL_ADDRESS JBossCacheSSOClusterManager localSSOManager = new JBossCacheSSOClusterManager(); MockSSOValve localValve = new MockSSOValve(mbeanServer); localValve.setCacheConfig(ClusteredSingleSignOn.DEFAULT_CACHE_NAME); localValve.setClusterManager(localSSOManager); localSSOManager.setSSOLocalManager(localValve); localSSOManager.start(); assertEquals("Thread pool usage as expected", usePool, localSSOManager.isUsingThreadPool()); // Build up a second SSO infrastructure based on LOCAL_ADDRESS // It uses the same mock cache, but we change the cache address // so it thinks it's a different address when it starts cache.setOurAddress(REMOTE_ADDRESS); JBossCacheSSOClusterManager remoteSSOManager = new JBossCacheSSOClusterManager(); MockSSOValve remoteValve = new MockSSOValve(mbeanServer); remoteValve.setCacheConfig(ClusteredSingleSignOn.DEFAULT_CACHE_NAME); remoteValve.setClusterManager(remoteSSOManager); remoteSSOManager.setSSOLocalManager(localValve); remoteSSOManager.start(); // Create an SSO that will have sessions from both valves localSSOManager.register("1", "FORM", "Brian", "password"); Manager localSessMgr1 = getSessionManager("A"); Session sess1 = new MockSession(localSessMgr1, "1"); localSSOManager.addSession("1", getFullyQualifiedSessionId(sess1)); Manager remoteSessMgr1 = getSessionManager("B"); Session sess2 = new MockSession(remoteSessMgr1, "2"); remoteSSOManager.addSession("1", getFullyQualifiedSessionId(sess2)); // Create another SSO with sessions only from remote remoteSSOManager.register("2", "FORM", "Brian", "password"); Manager remoteSessMgr2 = getSessionManager("C"); Session sess3 = new MockSession(remoteSessMgr2, "3"); remoteSSOManager.addSession("2", getFullyQualifiedSessionId(sess3)); Manager remoteSessMgr3 = getSessionManager("D"); Session sess4 = new MockSession(remoteSessMgr3, "4"); remoteSSOManager.addSession("2", getFullyQualifiedSessionId(sess4)); // Create a third SSO that will have sessions from both valves // with the same session id localSSOManager.register("3", "FORM", "Brian", "password"); Manager localSessMgr2 = getSessionManager("E"); Session sess5 = new MockSession(localSessMgr2, "5"); localSSOManager.addSession("3", getFullyQualifiedSessionId(sess5)); Manager remoteSessMgr4 = getSessionManager("E"); Session sess6 = new MockSession(remoteSessMgr4, "5"); remoteSSOManager.addSession("3", getFullyQualifiedSessionId(sess6)); // Create a fourth SSO that will have two sessions from local valve localSSOManager.register("4", "FORM", "Brian", "password"); Manager localSessMgr3 = getSessionManager("F"); Session sess7 = new MockSession(localSessMgr3, "7"); localSSOManager.addSession("4", getFullyQualifiedSessionId(sess7)); Manager localSessMgr4 = getSessionManager("G"); Session sess8 = new MockSession(localSessMgr4, "8"); localSSOManager.addSession("4", getFullyQualifiedSessionId(sess8)); // Create a fifth SSO with sessions only from remote, same session id // but different managers remoteSSOManager.register("5", "FORM", "Brian", "password"); Manager remoteSessMgr5 = getSessionManager("H"); Session sess9 = new MockSession(remoteSessMgr5, "9"); remoteSSOManager.addSession("5", getFullyQualifiedSessionId(sess9)); Manager remoteSessMgr6 = getSessionManager("I"); Session sess10 = new MockSession(remoteSessMgr6, "9"); remoteSSOManager.addSession("5", getFullyQualifiedSessionId(sess10)); // Confirm that data is cached properly assertEquals("SSO 1 has correct number of sessions", 2, localSSOManager.getSessionCount("1")); assertEquals("SSO 1 has correct number of sessions", 2, remoteSSOManager.getSessionCount("1")); assertEquals("SSO 2 has correct number of sessions", 2, localSSOManager.getSessionCount("2")); assertEquals("SSO 2 has correct number of sessions", 2, remoteSSOManager.getSessionCount("2")); assertEquals("SSO 3 has correct number of sessions", 2, localSSOManager.getSessionCount("3")); assertEquals("SSO 3 has correct number of sessions", 2, remoteSSOManager.getSessionCount("3")); assertEquals("SSO 4 has correct number of sessions", 2, localSSOManager.getSessionCount("4")); assertEquals("SSO 4 has correct number of sessions", 2, remoteSSOManager.getSessionCount("4")); assertEquals("SSO 5 has correct number of sessions", 2, localSSOManager.getSessionCount("5")); assertEquals("SSO 5 has correct number of sessions", 2, remoteSSOManager.getSessionCount("5")); // Put in a new view with REMOTE_ADDRESS dead ViewId viewId = new ViewId(LOCAL_ADDRESS, 1); Vector v = new Vector(); v.add(LOCAL_ADDRESS); EventImpl event = new EventImpl(); event.setNewView(new View(viewId, v)); event.setType(Type.VIEW_CHANGED); event.setPre(false); localSSOManager.viewChange(event); // Give the cleanup thread time to finish Thread.sleep(2000); // Confirm that cached data is properly cleaned up assertEquals("SSO 1 has correct number of sessions", 1, localSSOManager.getSessionCount("1")); assertEquals("SSO 2 has correct number of sessions", 0, localSSOManager.getSessionCount("2")); assertEquals("SSO 3 has correct number of sessions", 1, localSSOManager.getSessionCount("3")); assertEquals("SSO 4 has correct number of sessions", 2, localSSOManager.getSessionCount("4")); assertEquals("SSO 5 has correct number of sessions", 0, localSSOManager.getSessionCount("5")); } /** * Test for JBAS-5609 -- confirm that if sessions from different managers * but with the same session id are registered, the removal of one leaves * the SSO in the proper state. */ public void testSameSessionId() throws Exception { CacheManager cacheManager = getCacheManager(); // Register a cache Cache<Object, Object> delegate = new DefaultCacheFactory<Object, Object>().createCache(false); MockTreeCache cache = new MockTreeCache(delegate); // JBAS-4097 -- don't use a TransactionManagerLookup that will // bind DummyTransactionManager into JNDI, as that will screw // up other tests cache.getConfiguration().getRuntimeConfig().setTransactionManager(new BatchModeTransactionManager()); cacheManager.registerCache(cache, ClusteredSingleSignOn.DEFAULT_CACHE_NAME); // Build up an SSO infrastructure based on LOCAL_ADDRESS JBossCacheSSOClusterManager localSSOManager = new JBossCacheSSOClusterManager(); MockSSOValve localValve = new MockSSOValve(mbeanServer); localValve.setCacheConfig(ClusteredSingleSignOn.DEFAULT_CACHE_NAME); localValve.setClusterManager(localSSOManager); localSSOManager.setSSOLocalManager(localValve); localSSOManager.start(); // Create an SSO that will have sessions from both valves localSSOManager.register("1", "FORM", "Brian", "password"); Manager localSessMgr1 = getSessionManager("A"); Session sess1 = new MockSession(localSessMgr1, "1"); localSSOManager.addSession("1", getFullyQualifiedSessionId(sess1)); Manager localSessMgr2 = getSessionManager("B"); Session sess2 = new MockSession(localSessMgr2, "1"); localSSOManager.addSession("1", getFullyQualifiedSessionId(sess2)); assertEquals(2, localSSOManager.getSessionCount("1")); localSSOManager.removeSession("1", getFullyQualifiedSessionId(sess2)); assertEquals(1, localSSOManager.getSessionCount("1")); } private CacheManager getCacheManager() throws Exception { if (cacheManager == null) { JChannelFactory channelFactory = new JChannelFactory(); ConfigurationRegistry registry = new DependencyInjectedConfigurationRegistry(); cacheManager = new CacheManager(registry, channelFactory); cacheManager.start(); } return cacheManager; } private Manager getSessionManager(String contextName) { StandardManager mgr = new StandardManager(); MockEngine engine = new MockEngine(); MockHost host = new MockHost(); engine.addChild(host); host.setName("localhost"); StandardContext container = new StandardContext(); container.setName(contextName); host.addChild(container); container.setManager(mgr); return mgr; } private FullyQualifiedSessionId getFullyQualifiedSessionId(Session session) { String id = session.getIdInternal(); Container context = session.getManager().getContainer(); String contextName = context.getName(); Container host = context.getParent(); String hostName = host.getName(); return new FullyQualifiedSessionId(id, contextName, hostName); } static class MockTreeCache extends DelegatingMockCache { private IpAddress ourAddress = LOCAL_ADDRESS; public MockTreeCache(Cache delegate) throws Exception { super(delegate); getConfiguration().setCacheMode(CacheMode.LOCAL); } @Override public Address getLocalAddress() { return ourAddress; } void setOurAddress(IpAddress address) { ourAddress = address; } @Override public Vector getMembers() { Vector v = new Vector(); v.add(LOCAL_ADDRESS); v.add(REMOTE_ADDRESS); return v; } } /** * Override ClusteredSingleSignOn to suppress the empty SSO callbacks */ static class MockSSOValve extends ClusteredSingleSignOn { private final MBeanServer mbeanServer; MockSSOValve(MBeanServer server) { this.mbeanServer = server; } @Override public MBeanServer getMBeanServer() { return mbeanServer; } @Override public void notifySSOEmpty(String ssoId) { // no-op } @Override public void notifySSONotEmpty(String ssoId) { // no-op } } static class MockSession extends StandardSession { private static final long serialVersionUID = 1L; private String ourId; MockSession(Manager manager, String id) { super(manager); ourId = id; } @Override public String getId() { return ourId; } @Override public String getIdInternal() { return ourId; } } }