package org.jboss.cache.aop.loader;

import junit.framework.Test;
import junit.framework.TestSuite;
import org.jboss.cache.Fqn;
import org.jboss.cache.aop.PojoCache;
import org.jboss.cache.aop.test.Person;
import org.jboss.cache.loader.CacheLoader;
import org.jboss.cache.transaction.DummyTransactionManager;

import javax.transaction.Transaction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Commons tests for all CacheLoaders. Aop version.
 *
 * @author bwang
 * @version $Id: CacheLoaderTestsBase.java 3389 2007-01-10 20:55:25Z msurtani $
 */
abstract public class CacheLoaderTestsBase extends AbstractCacheLoaderTestBase {
   PojoCache cache;
   CacheLoader loader = null;
   Transaction tx = null;
   static final Fqn FQN = new Fqn("key");

   protected void setUp() throws Exception {
      super.setUp();
      startCache();
   }

   protected void startCache() throws Exception {
      cache = new PojoCache();
      cache.setCacheMode("local");
      configureCache();
      cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
      cache.createService();
      cache.startService();
      loader = cache.getCacheLoader();
   }

   abstract protected void configureCache() throws Exception;

   protected void tearDown() throws Exception {
      super.tearDown();
      if (tx != null) {
         try {
            tx.commit();
         } catch (Throwable e) {
            e.printStackTrace();
         }
      }
      cache.remove("/");
      loader.remove(Fqn.fromString("/"));
      cache.stopService();
      cache.destroyService();
   }

   protected void stopCache() throws Exception {
      cache.stopService();
      cache.destroyService();
   }

   protected void addDelay() {
      ; // returns immediately in this case.  Subclasses may override where a delay is needed.
   }

   public void testEviction() throws Exception {
      cache.putObject("/first/second/third", "val1");   // stored in cache loader
      cache.evict(Fqn.fromString("/first/second/third"));  // removes node, because there are no children
      addDelay();
      assertFalse(cache.exists("/first/second/third"));
      assertTrue(cache.exists("/first/second"));
      assertTrue(cache.exists("/first"));
      String val = (String) cache.getObject("/first/second/third"); // should be loaded from cache loader
      assertEquals("val1", val);
      assertTrue(cache.exists("/first/second/third"));
      assertTrue(cache.exists("/first/second"));
      assertTrue(cache.exists("/first"));
   }


   public void testPojoEviction() throws Exception {
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache.putObject("/first/second/third", test);   // stored in cache loader
      cache.evict(Fqn.fromString("/first/second/third"));  // removes node, because there are no children
      addDelay();
      assertFalse(cache.exists("/first/second/third"));
      assertTrue(cache.exists("/first/second"));
      assertTrue(cache.exists("/first"));
      Person val = (Person) cache.getObject("/first/second/third"); // should be loaded from cache loader
      assertNotNull(val);
      // This will use both original the new pojo to get.
      assertEquals(test.getName(), val.getName());
      assertTrue(cache.exists("/first/second/third"));
      assertTrue(cache.exists("/first/second"));
      assertTrue(cache.exists("/first"));
   }

   public void testPojoRemove() throws Exception {
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache.putObject("/first/second/third", test);   // stored in cache loader
      cache.remove(Fqn.fromString("/first/second/third"));  // removes node, because there are no children
      addDelay();
      assertFalse(cache.exists("/first/second/third"));
      assertTrue(cache.exists("/first/second"));
      assertTrue(cache.exists("/first"));
      Person val = (Person) cache.getObject("/first/second/third"); // should be loaded from cache loader
      assertNull(val);
   }

   public void testSimpleRestart() throws Exception {
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache.putObject("/first/second/third", test);   // stored in cache loader
      assertNotNull(loader.get(Fqn.fromString("/first/second/third")));
      stopCache();
      startCache();
      assertNotNull(loader.get(Fqn.fromString("/first/second/third")));
      assertFalse(cache.exists("/first/second/third"));
      Person val = (Person) cache.getObject("/first/second/third"); // should be loaded from cache loader
      assertNotNull(val);
      assertEquals("Age ", 10, val.getAge());
   }

   public void testRemoveObject() throws Exception {
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache.putObject("/first/second/third", test);   // stored in cache loader
      addDelay();
      cache.removeObject("/first/second/third");
      addDelay();
      assertFalse(cache.exists("/first/second/third"));
      assertTrue(cache.exists("/first/second"));
      assertTrue(cache.exists("/first"));
      Person val = (Person) cache.getObject("/first/second/third"); // should be loaded from cache loader
      assertNull(val);
   }

   /**
    * This test illustrates the problem in JBCACHE-477. That is transactional remove under
    * cacheloader using pessimistic locking.
    */
   public void testTxCollectionRemove() throws Exception {
      Person joe = new Person();
      joe.setName("Joe");
      joe.setAge(10);
      Person ben = new Person();
      ben.setName("Ben");
      ben.setAge(10);

      ArrayList list =new ArrayList();
      list.add(joe);
      list.add(ben);

      cache.putObject("/list", list);   // stored in cache loader
      list = (ArrayList)cache.getObject("/list");

      DummyTransactionManager mgr=DummyTransactionManager.getInstance();
      try {
         mgr.begin();
         tx=mgr.getTransaction();
         list.remove(joe);
      } catch (Exception ex)
      {
        fail("This is currently a known problem (JBCACHE-477) that we need to fix.");
      } finally
      {
         tx.commit();
      }
   }

   public void testMap() throws Exception
   {
      Map map = new HashMap();
      map.put("1", "1");
      map.put("2", "2");

      cache.putObject("/test/map", map);
      map = (Map)cache.getObject("/test/map");
      map.put("3", "3");
      assertEquals("Map size ", 3, map.size());

      cache.evict(Fqn.fromString("/test/map"));
      addDelay();
      map = (Map)cache.getObject("/test/map");
      assertEquals("Map size ", 3, map.size());
      assertEquals("Map 1", "1", map.get("1"));
      assertEquals("Map 2", "2", map.get("2"));
      assertEquals("Map 3", "3", map.get("3"));
   }

   public void testMapRestart() throws Exception
   {
      Map map = new HashMap();
      map.put("1", "1");
      map.put("2", "2");

      cache.putObject("/test/map", map);
      map = (Map)cache.getObject("/test/map");
      map.put("3", "3");
      assertEquals("Map size ", 3, map.size());

      stopCache();
      startCache();

      addDelay();
      assertFalse(cache.exists("/test/map"));
      map = (Map)cache.getObject("/test/map");
      assertEquals("Map size", 3, map.size());

      assertEquals("Map 1", "1", map.get("1"));
      assertEquals("Map 2", "2", map.get("2"));
      assertEquals("Map 3", "3", map.get("3"));
   }

   public void testList() throws Exception
   {
      List list = new ArrayList();
      list.add("1");
      list.add("2");

      cache.putObject("/test/list", list);
      list = (List)cache.getObject("/test/list");
      list.add("3");
      assertEquals("List size ", 3, list.size());

      cache.evict(Fqn.fromString("/test/list"));
      addDelay();
      list = (List)cache.getObject("/test/list");
      assertEquals("List size ", 3, list.size());
      assertEquals("List 1", "1", list.get(0));
      assertEquals("List 2", "2", list.get(1));
      assertEquals("List 3", "3", list.get(2));
   }

   public void testListRestart() throws Exception
   {
      List list = new ArrayList();
      list.add("1");
      list.add("2");

      cache.putObject("/test/list", list);
      list = (List)cache.getObject("/test/list");
      list.add("3");
      assertEquals("List size ", 3, list.size());

      stopCache();
      startCache();

      addDelay();
      list = (List)cache.getObject("/test/list");
      assertEquals("List size ", 3, list.size());
      assertEquals("List 1", "1", list.get(0));
      assertEquals("List 2", "2", list.get(1));
      assertEquals("List 3", "3", list.get(2));
   }

   public void testSet() throws Exception
   {
      Set set = new HashSet();
      set.add("1");
      set.add("2");

      cache.putObject("/test/set", set);
      set = (Set)cache.getObject("/test/set");
      set.add("3");
      assertEquals("Set size ", 3, set.size());

      cache.evict(Fqn.fromString("/test/set"));
      addDelay();
      set = (Set)cache.getObject("/test/set");
      assertEquals("Set size ", 3, set.size());
      Iterator it = set.iterator();
      assertEquals("Set 1", "3", it.next());
      assertEquals("Set 2", "2", it.next());
      assertEquals("Set 3", "1", it.next());
   }

   public void testSetRestart() throws Exception
   {
      Set set = new HashSet();
      set.add("1");
      set.add("2");

      cache.putObject("/test/set", set);
      set = (Set)cache.getObject("/test/set");
      set.add("3");
      assertEquals("Set size ", 3, set.size());

      stopCache();
      startCache();

      addDelay();
      set = (Set)cache.getObject("/test/set");
      assertEquals("Set size ", 3, set.size());
      Iterator it = set.iterator();
      assertEquals("Set 1", "3", it.next());
      assertEquals("Set 2", "2", it.next());
      assertEquals("Set 3", "1", it.next());
   }

   public static Test suite() {
      return new TestSuite(CacheLoaderTestsBase.class);
   }

   public static void main(String[] args) {
      junit.textui.TestRunner.run(suite());
   }

}
