package org.jboss.cache.eviction;

import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.notifications.annotation.CacheListener;
import org.jboss.cache.notifications.annotation.NodeEvicted;
import org.jboss.cache.notifications.event.Event;
import org.jboss.cache.transaction.DummyTransactionManagerLookup;
import org.jboss.cache.util.TestingUtil;
import static org.testng.AssertJUnit.*;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * @author Ben Wang, Feb 11, 2004
 */
@Test(groups = {"functional"})
public class ReplicatedLRUPolicyTest
{
   CacheSPI<Object, Object> cache1, cache2, cache3;
   int wakeupIntervalMillis_ = 0;
   EvictionListener listener_ = new EvictionListener();

   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception
   {
      cache1 = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, true), false);
      cache1.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
      cache1.getConfiguration().setUseRegionBasedMarshalling(true);
      cache1.start();
      cache1.getNotifier().addCacheListener(listener_);
      listener_.resetCounter();

      cache3 = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false);
      cache3.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");
      cache3.getConfiguration().setUseRegionBasedMarshalling(true);
      cache3.start();

      wakeupIntervalMillis_ = cache1.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000;
      log("wakeupInterval is " + wakeupIntervalMillis_);
      if (wakeupIntervalMillis_ <= 0)
      {
         fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis_);
      }
   }

   @AfterMethod(alwaysRun = true)
   public void tearDown() throws Exception
   {
      TestingUtil.killCaches(cache1, cache2, cache3);
   }

   /**
    * Test local eviction policy that failed for eviction event.
    */
   public void testBasic() throws Exception
   {
      String rootStr = "/org/jboss/test/data/";
      String str = rootStr + "0";
      cache1.put(str, str, str);

      TestingUtil.sleepThread(30000);
      Object node = cache1.peek(Fqn.fromString(str), false);
      assertNull("DataNode should be evicted already ", node);
      assertEquals("Eviction counter ", 1, listener_.getCounter());
      String val = (String) cache3.get(str, str);
      assertNotNull("DataNode should not be evicted here ", val);
      assertEquals("Eviction counter ", 1, listener_.getCounter());
   }

   public void testEviction() throws Exception
   {
      String rootStr = "/org/jboss/test/data/";
      for (int i = 0; i < 10; i++)
      {
         String str = rootStr + i;
         Fqn fqn = Fqn.fromString(str);
         cache1.put(fqn, str, str);
      }

      TestingUtil.sleepThread(2 * wakeupIntervalMillis_);
      String val = (String) cache1.get(rootStr + "3", rootStr + "3");
      assertNull("DataNode should be evicted already ", val);
      val = (String) cache3.get(rootStr + "3", rootStr + "3");
      assertNotNull("DataNode should not be evicted here ", val);
   }

   public void testEvictionReplication() throws Exception
   {
      String rootStr = "/org/jboss/test/data/";
      for (int i = 0; i < 10; i++)
      {
         String str = rootStr + i;
         Fqn fqn = Fqn.fromString(str);
         cache1.put(fqn, str, str);
      }

      TestingUtil.sleepThread(wakeupIntervalMillis_ - 1000);
      String str = rootStr + "7";
      Fqn fqn = Fqn.fromString(str);
      cache1.get(fqn, str);
      TestingUtil.sleepThread(wakeupIntervalMillis_);

      String val = (String) cache1.get(rootStr + "3", rootStr + "3");
      assertNull("DataNode should be empty ", val);
      val = (String) cache3.get(rootStr + "7", rootStr + "7");
      assertNotNull("DataNode should not be null", val);
   }

   void log(String msg)
   {
      System.out.println("-- " + msg);
   }

   @CacheListener
   public class EvictionListener
   {
      int counter = 0;

      public int getCounter()
      {
         return counter;
      }

      public void resetCounter()
      {
         counter = 0;
      }

      @NodeEvicted
      public void nodeEvicted(Event e)
      {
         System.out.println(e);
         if (e.isPre()) counter++;
      }
   }
}
