Package org.jboss.ejb3.core.test.ejbthree1549.unit

Source Code of org.jboss.ejb3.core.test.ejbthree1549.unit.PassivationDoesNotPreventNewActivityUnitTestCase

/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
  *
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ejb3.core.test.ejbthree1549.unit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import junit.framework.TestCase;

import org.jboss.ejb3.cache.CacheFactoryRegistry;
import org.jboss.ejb3.cache.persistence.PersistenceManagerFactoryRegistry;
import org.jboss.ejb3.common.registrar.spi.Ejb3RegistrarLocator;
import org.jboss.ejb3.core.test.common.AbstractEJB3TestCase;
import org.jboss.ejb3.core.test.ejbthree1549.BlockingPersistenceManager;
import org.jboss.ejb3.core.test.ejbthree1549.BlockingPersistenceManagerFactory;
import org.jboss.ejb3.core.test.ejbthree1549.ForceEventsCache;
import org.jboss.ejb3.core.test.ejbthree1549.ForceEventsCacheFactory;
import org.jboss.ejb3.core.test.ejbthree1549.MyStatefulBean;
import org.jboss.ejb3.core.test.ejbthree1549.MyStatefulLocal;
import org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandler;
import org.jboss.ejb3.stateful.StatefulContainer;
import org.jboss.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

/**
* PassivationDoesNotPreventNewActivityUnitTestCase
*
* Contains tests to ensure that performing passivation does not
* block either new session creation or invocation in other sessions
*
* @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
* @version $Revision: $
*/
public class PassivationDoesNotPreventNewActivityUnitTestCase extends AbstractEJB3TestCase
{
   // --------------------------------------------------------------------------------||
   // Class Members ------------------------------------------------------------------||
   // --------------------------------------------------------------------------------||

   private static StatefulContainer container;

   private static final Logger log = Logger.getLogger(PassivationDoesNotPreventNewActivityUnitTestCase.class);

   // --------------------------------------------------------------------------------||
   // Tests --------------------------------------------------------------------------||
   // --------------------------------------------------------------------------------||

   /**
    * Tests that removal may take place during the course
    * of passivation
    */
   @Test
   public void testSessionRemovalDuringPassivation() throws Throwable
   {
      /*
       * First we invoke upon a new session
       */

      // Initialize
      final String sfsbJndiName = MyStatefulLocal.JNDI_NAME;

      // Create a new session, which should be allowed during passivation
      MyStatefulLocal sfsb = (MyStatefulLocal) container.getInitialContext().lookup(sfsbJndiName);

      // If we've got nothing
      if (sfsb == null)
      {
         // Fail
         TestCase.fail("Lookup did not succeed");
      }

      // Invoke upon the new Session
      final int next = sfsb.getNextCounter();

      // Test the value is expected
      TestCase.assertEquals("Next counter received was not expected", 0, next);

      /*
       * Mark this session as eligible for removal
       */

      // Get the cache
      ForceEventsCache cache = (ForceEventsCache) container.getCache();

      // Get the Session ID
      Serializable sessionId = this.getSessionId(sfsb);

      // Mark
      cache.makeSessionEligibleForRemoval(sessionId);

      /*
       * All's OK with traditional invocation, so define a task to force removal
       * of the SFSB, but don't invoke it yet (we're going to fire this off
       * *during* passivation
       */

      // Define the task to invoke upon a SFSB, and trigger removal while passivation is suspended
      Callable<Boolean> invokeDuringPasssivationTest = new Callable<Boolean>()
      {
         /**
          * Force removal
          */
         public Boolean call()
         {

            /*
             * Force removal
             */

            // Force
            ForceEventsCache.forceRemoval();

            // Clear the barrier
            try
            {
               log.info("Test is waiting on the pre-remove barrier");
               ForceEventsCache.PRE_REMOVE_BARRIER.await();
               log.info("Test has cleared the pre-remove barrier");
            }
            catch (InterruptedException e)
            {
               throw new RuntimeException(e);
            }
            catch (BrokenBarrierException e)
            {
               throw new RuntimeException(e);
            }

            // Return OK
            return true;

         }
      };

      /*
       * Force passivation, but block it from completing (because we don't
       * await on the PM)
       */

      // Force passivation
      ForceEventsCache.forcePassivation();
      ForceEventsCache.PRE_PASSIVATE_BARRIER.await();

      /*
       * Spawn off the test in another Thread
       */

      ExecutorService executor = Executors.newFixedThreadPool(1);
      Future<Boolean> futureResult = executor.submit(invokeDuringPasssivationTest);
      Boolean result = null;

      /*
       * Try to get the result of the test to remove, which
       * should not be blocked by passivation already in progress
       */

      // Get result
      log.info("Attempting to get the result of the removal task...");
      result = futureResult.get(5, TimeUnit.SECONDS);

      // Make sure the result is expected
      TestCase.assertTrue("Removal task completed when expected, but got wrong result", result);
   }

   @Test
   public void testInvokeSameSessionDuringPassivation() throws Throwable
   {
      final MyStatefulLocal bean = lookup(MyStatefulLocal.JNDI_NAME, MyStatefulLocal.class);

      // Get our bean's Session ID
      Serializable sessionId = this.getSessionId(bean);

      // Invoke upon our bean
      int next = bean.getNextCounter();
      log.info("Got counter from " + sessionId + ": " + next);
      TestCase.assertEquals("SFSB did not return expected next counter", 0, next);

      // Get the Cache
      ForceEventsCache cache = (ForceEventsCache) container.getCache();

      // Get the lock to block the PM, now
      boolean gotLock = BlockingPersistenceManager.PASSIVATION_LOCK.tryLock();

      Future<Integer> result;
      // Once PM lock is acquired, everything is in "try" so we release in "finally"
      try
      {
         // Ensure we got the PM lock, else fail the test
         TestCase.assertTrue("Test was not able to immediately get the lock to block the PersistenceManager", gotLock);
         log.info("Locked " + BlockingPersistenceManager.class.getSimpleName());

         // Mark
         cache.makeSessionEligibleForPassivation(sessionId);

         /*
          * Passivate
          */

         // Trigger Passivation
         ForceEventsCache.forcePassivation();
         log.info("Passivation forced, carrying out test");

         ForceEventsCache.PRE_PASSIVATE_BARRIER.await(5, TimeUnit.SECONDS);

         // Block until the PM is ready to passivate
         log.info("Waiting on common barrier for PM to run...");
         BlockingPersistenceManager.BARRIER.await(5, TimeUnit.SECONDS);
         log.info("PM and test have met barrier, passivation running (but will be blocked to complete by test)");

         Callable<Integer> task = new Callable<Integer>()
         {
            public Integer call() throws Exception
            {
               return bean.getNextCounter();
            }
         };
         ExecutorService executor = Executors.newFixedThreadPool(1);
         result = executor.submit(task);

         // TODO: there is no way to know where we are in StatefulInstanceInterceptor
         Thread.sleep(5000);
      }
      finally
      {
         // Allow the Persistence Manager to finish up
         log.info("Letting the PM perform passivation...");
         BlockingPersistenceManager.PASSIVATION_LOCK.unlock();
      }

      // We need to allow time to let the Cache finish passivation, so block until it's done
      log.info("Waiting on Cache to tell us passivation is completed...");
      ForceEventsCache.POST_PASSIVATE_BARRIER.await(5, TimeUnit.SECONDS);
      log.info("Test sees Cache reports passivation completed.");

      int duringPassivation = result.get(5, TimeUnit.SECONDS);
      log.info("Got counter from " + sessionId + ": " + duringPassivation);

      int postPassivation = bean.getNextCounter();
      log.info("Got counter from " + sessionId + ": " + postPassivation);

      assertEquals("the postPassivation counter should be 1 higher than the previous (during passivation)",
            duringPassivation + 1, postPassivation);
   }

   @Test
   public void testInvokeSameSessionDuringPrePassivation() throws Throwable
   {
      final MyStatefulLocal bean = lookup(MyStatefulLocal.JNDI_NAME, MyStatefulLocal.class);

      // Get our bean's Session ID
      Serializable sessionId = this.getSessionId(bean);

      // Invoke upon our bean
      int next = bean.getNextCounter();
      log.info("Got counter from " + sessionId + ": " + next);
      TestCase.assertEquals("SFSB did not return expected next counter", 0, next);

      // Get the Cache
      ForceEventsCache cache = (ForceEventsCache) container.getCache();

      // Get the lock to block the PM, now
      boolean gotLock = BlockingPersistenceManager.PASSIVATION_LOCK.tryLock();

      Future<Integer> result;
      // Once PM lock is acquired, everything is in "try" so we release in "finally"
      try
      {
         // Ensure we got the PM lock, else fail the test
         TestCase.assertTrue("Test was not able to immediately get the lock to block the PersistenceManager", gotLock);
         log.info("Locked " + BlockingPersistenceManager.class.getSimpleName());

         // Mark
         cache.makeSessionEligibleForPassivation(sessionId);

         /*
          * Passivate
          */

         // Trigger Passivation
         ForceEventsCache.forcePassivation();
         log.info("Passivation forced, carrying out test");

         Callable<Integer> task = new Callable<Integer>()
         {
            public Integer call() throws Exception
            {
               return bean.getNextCounter();
            }
         };
         ExecutorService executor = Executors.newFixedThreadPool(1);
         result = executor.submit(task);

         // TODO: there is no way to know where we are in StatefulInstanceInterceptor
         Thread.sleep(5000);

         ForceEventsCache.PRE_PASSIVATE_BARRIER.await(5, TimeUnit.SECONDS);

         // Block until the PM is ready to passivate
         /* we're not passivating, we yanked it out
         log.info("Waiting on common barrier for PM to run...");
         BlockingPersistenceManager.BARRIER.await(5, TimeUnit.SECONDS);
         log.info("PM and test have met barrier, passivation running (but will be blocked to complete by test)");
         */
      }
      finally
      {
         // Allow the Persistence Manager to finish up
         log.info("Letting the PM perform passivation...");
         BlockingPersistenceManager.PASSIVATION_LOCK.unlock();
      }

      // We need to allow time to let the Cache finish passivation, so block until it's done
      log.info("Waiting on Cache to tell us passivation is completed...");
      ForceEventsCache.POST_PASSIVATE_BARRIER.await(5, TimeUnit.SECONDS);
      log.info("Test sees Cache reports passivation completed.");

      int duringPassivation = result.get(5, TimeUnit.SECONDS);
      log.info("Got counter from " + sessionId + ": " + duringPassivation);

      int postPassivation = bean.getNextCounter();
      log.info("Got counter from " + sessionId + ": " + postPassivation);

      assertEquals("the postPassivation counter should be 1 higher than the previous (during passivation)",
            duringPassivation + 1, postPassivation);
   }

   /**
    * Tests that a new session may be created while another is being passivated
    */
   @Test
   public void testNewSessionMayBeCreatedDuringPassivation() throws Throwable
   {
      // Initialize
      final String sfsbJndiName = MyStatefulLocal.JNDI_NAME;

      // Define the task to lookup a new session
      Callable<Boolean> lookupNewSessionTest = new Callable<Boolean>()
      {
         /**
          * Lookup a new session, and set that the test succeeded if we're able
          */
         public Boolean call()
         {
            try
            {
               // Create a new session, which should be allowed during passivation
               MyStatefulLocal bean2 = (MyStatefulLocal) container.getInitialContext().lookup(sfsbJndiName);

               // If we've don't have a new session
               if (bean2 == null)
               {
                  throw new RuntimeException("Got back null SFSB");
               }

            }
            // We can't fail the unit test from here, so log the error
            catch (Exception e)
            {
               log.error("Test encountered an error", e);
            }

            // Test is good
            return true;
         }
      };

      // Run the Test
      this.runTestDuringPassivation(lookupNewSessionTest);
   }

   /**
    * Tests that a one session may carry out an invocation while another session
    * is undergoing passivation
    */
   @Test
   public void testSessionMayBeInvokedWhileAnotherIsPassivating() throws Throwable
   {
      // Initialize
      final String sfsbJndiName = MyStatefulLocal.JNDI_NAME;

      // Define the task to invoke upon a SFSB
      Callable<Boolean> invokeDuringPasssivationTest = new Callable<Boolean>()
      {
         /**
          * Lookup a new session, invoke upon it, and set that the test succeeded if we're able
          */
         public Boolean call()
         {
            try
            {
               // Create a new session, which should be allowed during passivation
               MyStatefulLocal bean2 = (MyStatefulLocal) container.getInitialContext().lookup(sfsbJndiName);

               // If we've got nothing
               if (bean2 == null)
               {
                  // Let test fail, logging along the way
                  log.error("Lookup did not succeed");
               }

               // Invoke upon the new Session
               int next = bean2.getNextCounter();

               // Test the value
               if (next == 0)
               {
                  return true;
               }

               // Value was not expected, let the test fail and log
               log.error("Invocation succeeded, but expected next counter of 0 and got: " + next);

            }
            // We can't fail the unit test from here, so log the error
            catch (Exception e)
            {
               log.error("Test encountered an error", e);
            }

            // Fail, should have gotten expected value and returned above
            return false;
         }
      };

      // Run the Test
      this.runTestDuringPassivation(invokeDuringPasssivationTest);
   }

   // --------------------------------------------------------------------------------||
   // Internal Helper Methods --------------------------------------------------------||
   // --------------------------------------------------------------------------------||

   /**
    * Performs the following common test actions:
    *
    * 1) Looks up and invokes upon a new SFSB instance
    * 2) Blocks passivation from completing
    * 3) Waits for the SFSB instance to becomes eligible for passivation
    * 4) Forces passivation to run
    * 5) Runs the specified Test Thread
    * 6) Allows the test to complete either successfully or unsuccessfully, or times out if stuck
    * 7) Reports failure or success overall
    */
   protected void runTestDuringPassivation(Callable<Boolean> testThread) throws Throwable
   {
      // Initialize
      final String sfsbJndiName = MyStatefulLocal.JNDI_NAME;
      boolean testSucceeded = false;

      // Lookup an instance
      MyStatefulLocal bean1 = (MyStatefulLocal) container.getInitialContext().lookup(sfsbJndiName);

      // Invoke upon our bean
      int next = bean1.getNextCounter();
      log.info("Got counter from bean1 : " + next);
      TestCase.assertEquals("SFSB did not return expected next counter", 0, next);

      // Get our bean's Session ID
      Serializable sessionId = this.getSessionId(bean1);

      // Get the Cache
      ForceEventsCache cache = (ForceEventsCache) container.getCache();

      // Get the lock to block the PM, now
      boolean gotLock = BlockingPersistenceManager.PASSIVATION_LOCK.tryLock();

      // Once PM lock is acquired, everything is in "try" so we release in "finally"
      try
      {
         // Ensure we got the PM lock, else fail the test
         TestCase.assertTrue("Test was not able to immediately get the lock to block the PersistenceManager", gotLock);
         log.info("Locked " + BlockingPersistenceManager.class.getSimpleName());

         /*
          * Mark our session as expired
          */

         // Mark
         cache.makeSessionEligibleForPassivation(sessionId);

         /*
          * Passivate
          */

         // Trigger Passivation
         ForceEventsCache.forcePassivation();
         log.info("Passivation forced, carrying out test");

         ForceEventsCache.PRE_PASSIVATE_BARRIER.await(5, TimeUnit.SECONDS);

         // Block until the PM is ready to passivate
         log.info("Waiting on common barrier for PM to run...");
         BlockingPersistenceManager.BARRIER.await(5, TimeUnit.SECONDS);
         log.info("PM and test have met barrier, passivation running (but will be blocked to complete by test)");

         /*
          * At this point, we've told the passivation Thread to start, and have
          * locked it from completing.  So let's try our test in another Thread
          * so we can detect a deadlock or permanent blocking after a timeout
          */
         ExecutorService executor = Executors.newFixedThreadPool(1);
         Future<Boolean> futureResult = executor.submit(testThread);
         boolean result = futureResult.get(5, TimeUnit.SECONDS);

         /*
          * Assert on the result
          */

         // If the test has not succeeded
         if (!result)
         {
            // Fail
            TestCase.fail("The test has completed without success");
         }

         // If the test has been successful
         else
         {
            log.info("Test Succeeded");
            testSucceeded = true;
         }
      }
      finally
      {

         // Allow the Persistence Manager to finish up
         log.info("Letting the PM perform passivation...");
         BlockingPersistenceManager.PASSIVATION_LOCK.unlock();
      }

      // We need to allow time to let the Cache finish passivation, so block until it's done
      log.info("Waiting on Cache to tell us passivation is completed...");
      ForceEventsCache.POST_PASSIVATE_BARRIER.await(5, TimeUnit.SECONDS);
      log.info("Test sees Cache reports passivation completed.");

      /*
       * Here we ensure that the session was removed from the internal cacheMap
       */
      boolean beanIsInCache = cache.doesCacheMapContainKey(sessionId);
      assertFalse("bean " + sessionId + " was not removed from cache", beanIsInCache);

      // Ensure we're good
      TestCase.assertTrue("The test did not succeed", testSucceeded);
   }

   // --------------------------------------------------------------------------------||
   // Lifecycle Methods --------------------------------------------------------------||
   // --------------------------------------------------------------------------------||

   @BeforeClass
   public static void beforeClass() throws Exception
   {
      // Perform generic setup of MC, default beans, etc
      AbstractEJB3TestCase.beforeClass();

      // Add the Blocking PersistenceManager
      PersistenceManagerFactoryRegistry pmFactoryRegistry = Ejb3RegistrarLocator.locateRegistrar().lookup(
            "EJB3PersistenceManagerFactoryRegistry", PersistenceManagerFactoryRegistry.class);
      pmFactoryRegistry.getFactories().put(BlockingPersistenceManagerFactory.REGISTRY_BIND_NAME,
            BlockingPersistenceManagerFactory.class);

      // Add the Force Passivation Cache
      CacheFactoryRegistry cacheFactoryRegistry = Ejb3RegistrarLocator.locateRegistrar().lookup(
            "EJB3CacheFactoryRegistry", CacheFactoryRegistry.class);
      String forcePassivationCacheRegistryName = ForceEventsCacheFactory.REGISTRY_BIND_NAME;
      cacheFactoryRegistry.getFactories().put(forcePassivationCacheRegistryName, ForceEventsCacheFactory.class);
      log.info("Added " + forcePassivationCacheRegistryName);

      // Deploy the test SFSB
      Class<?> ejbImplClass = MyStatefulBean.class;
      log.info("Deploying SFSB: " + ejbImplClass.getName());
      container = (StatefulContainer) deploySessionEjb(ejbImplClass);
   }

   @After
   public void after()
   {
      log.info("Resetting all barriers and clearing the cache...");
      ForceEventsCache.POST_PASSIVATE_BARRIER.reset();
      ForceEventsCache.PRE_PASSIVATE_BARRIER.reset();
      ForceEventsCache.PRE_REMOVE_BARRIER.reset();
      BlockingPersistenceManager.BARRIER.reset();
      ForceEventsCache cache = (ForceEventsCache) container.getCache();
      cache.clear();
   }

   @AfterClass
   public static void afterClass() throws Exception
   {
      // Undeploy the test SFSB
      undeployEjb(container);

      AbstractEJB3TestCase.afterClass();
   }

   // --------------------------------------------------------------------------------||
   // Internal Helper Methods --------------------------------------------------------||
   // --------------------------------------------------------------------------------||

   /**
    * Returns the Session ID of the specified proxy
    */
   private Serializable getSessionId(MyStatefulLocal bean)
   {
      // Get our bean's Session ID
      InvocationHandler handler = Proxy.getInvocationHandler(bean);
      SessionProxyInvocationHandler sHandler = (SessionProxyInvocationHandler) handler;
      Serializable sessionId = (Serializable) sHandler.getTarget();
      return sessionId;
   }
}
TOP

Related Classes of org.jboss.ejb3.core.test.ejbthree1549.unit.PassivationDoesNotPreventNewActivityUnitTestCase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.