package org.jboss.cache.interceptors;

import org.jboss.cache.Cache;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.util.TestingUtil;
import org.jboss.cache.commands.write.PutKeyValueCommand;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * This is to test that "old-style" interceptors from 2.0.x and 2.1.x will work with the new interceptor structure.
 *
 * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
 * @since 2.2.0
 */
@Test(groups = "functional")
public class LegacyInterceptorTest
{
   Cache cache;
   static CountDownLatch interceptorResumeLatch, interceptorInvokedLatch;
   TestInterceptor testInterceptor;
   Executor testRunner;

   @BeforeTest
   public void setUp()
   {
      cache = new DefaultCacheFactory().createCache();
      testInterceptor = new TestInterceptor();

      ((CacheSPI) cache).addInterceptor(testInterceptor, TxInterceptor.class);
      testRunner = Executors.newSingleThreadExecutor();
   }

   @BeforeMethod
   public void createLatches()
   {
      interceptorResumeLatch = new CountDownLatch(1);
      interceptorInvokedLatch = new CountDownLatch(1);
   }

   @AfterTest
   public void tearDown()
   {
      TestingUtil.killCaches(cache);
   }

   public void testPut() throws Exception
   {
      testRunner.execute(new Runnable()
      {
         public void run()
         {
            cache.put("/a", "k", "v");
         }
      });

      interceptorInvokedLatch.await();

      // check that the context on the test interceptor is correct.
      InvocationContext ctx = testInterceptor.ctx;
      interceptorResumeLatch.countDown();

      assert ctx.getMethodCall().getMethodId() == PutKeyValueCommand.METHOD_ID;
      assert ctx.getMethodCall().getArgs()[0] == null; // gtx
      assert ctx.getMethodCall().getArgs()[1].equals(Fqn.fromString("/a")); // fqn
      assert ctx.getMethodCall().getArgs()[2].equals("k"); // key
      assert ctx.getMethodCall().getArgs()[3].equals("v"); // value
      assert ctx.getMethodCall().getArgs()[4] == Boolean.FALSE; //last boolean value
   }

   public static class TestInterceptor extends Interceptor
   {
      InvocationContext ctx;

      @Override
      public Object invoke(InvocationContext ctx) throws Throwable
      {
         if (ctx.isOriginLocal())
         {
            // copy the context so tests can inspect it
            this.ctx = ctx;

            // signal to the test that this has been invoked.
            interceptorInvokedLatch.countDown();

            // wait for tests to finish
            interceptorResumeLatch.await();

            // wipe class-level context variable
            this.ctx = null;
         }

         // the "old-style" of passing up the interceptor chain
         return super.invoke(ctx);
      }
   }

}

