/*
* 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();
      host.setName("localhost");
      engine.addChild(host);
      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;
      }      
      
   }

}
