/*****************************************
 *                                       *
 *  JBoss Portal: The OpenSource Portal  *
 *                                       *
 *   Distributable under LGPL license.   *
 *   See terms of license at gnu.org.    *
 *                                       *
 *****************************************/
package org.jboss.cache.pojo.rollback;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.transaction.TransactionManager;

import org.jboss.cache.CacheSPI;
import org.jboss.cache.ConsoleListener;
import org.jboss.cache.notifications.annotation.CacheListener;
import org.jboss.cache.notifications.annotation.NodeActivated;
import org.jboss.cache.notifications.annotation.NodeCreated;
import org.jboss.cache.notifications.annotation.NodeModified;
import org.jboss.cache.notifications.annotation.NodePassivated;
import org.jboss.cache.notifications.annotation.NodeRemoved;
import org.jboss.cache.notifications.annotation.NodeVisited;
import org.jboss.cache.notifications.event.NodeEvent;
import org.jboss.cache.notifications.event.NodeModifiedEvent;
import org.jboss.cache.notifications.event.NodeVisitedEvent;
import org.jboss.cache.pojo.PojoCache;
import org.jboss.cache.pojo.PojoCacheFactory;
import org.jboss.cache.transaction.DummyTransactionManager;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * @author
 */
@Test(groups = {"functional"})
@SuppressWarnings("unchecked")
public class PojoCollectionRollbackTest
{
   private static final String ID = "id";
   private static final String CONTAINER_FQN = "/objsIndex";
   TransactionManager tx_mgr;

   private PojoCache cache_;

   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
   }

   private void startTest() throws Exception
   {
      String configFile = "META-INF/local-service.xml";
      boolean toStart = false;
      cache_ = PojoCacheFactory.createCache(configFile, toStart);
      cache_.start();
      tx_mgr = DummyTransactionManager.getInstance();
   }

   @AfterMethod(alwaysRun = true)
   protected void tearDown() throws Exception
   {
      cache_.stop();
   }

   public void testNestedMapAndIndexWithModifyRollback() throws Exception
   {
      System.out.println("testNestedMapAndIndexWithModifyRollback");
      startTest();
      // create cached data objects
      Map<String, String> obj1 = new HashMap<String, String>();
      obj1.put(ID, "1");
      cache_.attach("/objs/1", obj1);
      obj1 = (Map<String, String>) cache_.find("/objs/1");

      // create cached collection of data objects
      // initialize collection by adding a data object
      Map<String, Map> indexMap = null;
      final String KEY = "KEY";
      Object beforeModify;
      Object afterRollback;

      indexMap = new HashMap<String, Map>();
      cache_.attach(CONTAINER_FQN, indexMap);
      indexMap = (Map<String, Map>) cache_.find(CONTAINER_FQN);
      indexMap.put(KEY, obj1);

      beforeModify = indexMap.get(KEY);
      Object idBeforeModify = ((Map) beforeModify).get(ID);

      System.out.println("beforeModify: " + beforeModify + ", data object id: " + ((Map) beforeModify).get(ID));

      // modify the collection by replacing the first data object with the second
      // and then roll-back the transaction
      tx_mgr.begin();
      obj1.put(ID, "newID");
      indexMap.remove(KEY);
      tx_mgr.rollback();

      indexMap = (Map<String, Map>) cache_.find(CONTAINER_FQN);
      afterRollback = indexMap.get(KEY);
      System.out.println("afterRollback: " + afterRollback + ", data object id: " + ((Map) afterRollback).get(ID));

      // check if state of collection was restored
      assertEquals(beforeModify, afterRollback);
      assertTrue(beforeModify == afterRollback);
      assertEquals(1, indexMap.size());

      assertEquals("1", obj1.get(ID));
      Object idAfterRollback = ((Map) afterRollback).get(ID);
      System.out.println("idBeforeModify: " + idBeforeModify + " idAfterRollback: " + idAfterRollback);
      assertEquals(idBeforeModify, idAfterRollback);
   }

   public void testNestedMapWithModifyRollback() throws Exception
   {
      System.out.println("testNestedMapWithModifyRollback");
      startTest();

      // create cached data objects
      Map<String, String> obj1 = new HashMap<String, String>();
      obj1.put(ID, "1");
      cache_.attach("/objs/1", obj1);
      obj1 = (Map<String, String>) cache_.find("/objs/1");

      Map<String, String> obj2 = new HashMap<String, String>();
      obj2.put(ID, "2");
      cache_.attach("/objs/2", obj2);
      obj2 = (Map<String, String>) cache_.find("/objs/2");

      // create cached collection of data objects
      // initialize collection by adding a data object
      Map<String, Map> indexMap = null;
      final String KEY = "KEY";
      Object beforeModify;
      Object afterRollback;

      indexMap = new HashMap<String, Map>();
      cache_.attach(CONTAINER_FQN, indexMap);
      indexMap = (Map<String, Map>) cache_.find(CONTAINER_FQN);
      indexMap.put(KEY, obj1);
      beforeModify = indexMap.get(KEY);

      System.out.println("beforeModify: " + beforeModify + ", data object id: " + ((Map) beforeModify).get(ID));

      // modify the collection by replacing the first data object with the second
      // and then roll-back the transaction
      tx_mgr.begin();
      Object removedByModify = indexMap.put(KEY, obj2);
      System.out.println("removedByModify: " + removedByModify + ", data object id: " + ((Map) removedByModify).get(ID));
      assertEquals(removedByModify, beforeModify);
      tx_mgr.rollback();

      indexMap = (Map<String, Map>) cache_.find(CONTAINER_FQN);
      afterRollback = indexMap.get(KEY);
      System.out.println("afterRollback: " + afterRollback + ", data object id: " + ((Map) afterRollback).get(ID));

      // check if state of collection was restored
      assertEquals(beforeModify, afterRollback);
      assertEquals(1, indexMap.size());
      // check that map entry can now be modified
      indexMap.put(KEY, obj2);
      assertEquals(obj2, indexMap.get(KEY));
   }

   public void testNestedMapWithRemoveRollback() throws Exception
   {
      System.out.println("testNestedMapWithRemoveRollback");
      startTest();

      // create cache_d data objects
      Map<String, String> obj1 = new HashMap<String, String>();
      obj1.put(ID, "1");
      cache_.attach("/objs/1", obj1);
      obj1 = (Map<String, String>) cache_.find("/objs/1");

      Map<String, String> obj2 = new HashMap<String, String>();
      obj2.put(ID, "2");
      cache_.attach("/objs/2", obj2);
      obj2 = (Map<String, String>) cache_.find("/objs/2");

      // create cached collection of data objects
      Map<String, Map<String, String>> indexMap = new HashMap<String, Map<String, String>>();
      cache_.attach(CONTAINER_FQN, indexMap);
      indexMap = (Map<String, Map<String, String>>) cache_.find(CONTAINER_FQN);

      // initialize collection by adding a data object
      final String KEY = "KEY";
      indexMap.put(KEY, obj1);

      Object beforeModify = indexMap.get(KEY);
      System.out.println("beforeModify: " + beforeModify + ", data object id: " + ((Map) beforeModify).get(ID));

      // modify the collection by replacing the first data object with the second
      // and then roll-back the transaction
      tx_mgr.begin();
      Object removedByRemove = indexMap.remove(KEY);
      System.out.println("removedByRemove: " + removedByRemove + ", data object id: " + ((Map) removedByRemove).get(ID));
      assertEquals(beforeModify, removedByRemove);
      tx_mgr.rollback();

      Object afterRollback = indexMap.get(KEY);
      System.out.println("afterRollback: " + afterRollback + ", data object id: " + ((Map) afterRollback).get(ID));

      // check if state of collection was restored
      assertEquals(beforeModify, afterRollback);
      assertEquals(1, indexMap.size());

      // check that map entry can now be modified
      indexMap.put(KEY, obj2);
      assertEquals(obj2, indexMap.get(KEY));
   }

   public void testNestedListWithModifyAddRollback() throws Exception
   {
      System.out.println("testNestedListWithModifyAddRollback");
      startTest();

      // create cached data objects
      Map<String, String> obj1 = new HashMap<String, String>();
      obj1.put(ID, "1");
      cache_.attach("/objs/1", obj1);
      obj1 = (Map<String, String>) cache_.find("/objs/1");

      Map<String, String> obj2 = new HashMap<String, String>();
      obj2.put(ID, "2");
      cache_.attach("/objs/2", obj2);
      obj2 = (Map<String, String>) cache_.find("/objs/2");
      assertFalse(obj1.equals(obj2));

      // create cached collection of data objects
      List<Map<String, String>> indexList = new ArrayList<Map<String, String>>();
      cache_.attach(CONTAINER_FQN, indexList);
      indexList = (List<Map<String, String>>) cache_.find(CONTAINER_FQN);

      // initialize collection by adding a data object
      indexList.add(obj1);

      Object beforeModify = indexList.get(0);
      System.out.println("beforeModify: " + beforeModify + ", data object id: " + ((Map) beforeModify).get(ID));
      int objIndex1, objIndex2;

      // modify the collection by replacing the first data object with the second
      // and then roll-back the transaction
      tx_mgr.begin();
      indexList.add(obj2);
      objIndex1 = indexList.indexOf(obj1);
      objIndex2 = indexList.indexOf(obj2);
      tx_mgr.rollback();

      // before rollback - object set
      assertFalse(obj1.equals(obj2));
      assertEquals(0, objIndex1);
      assertEquals(1, objIndex2);

      Object afterRollback = indexList.get(0);
      System.out.println("afterRollback: " + afterRollback + ", data object id: " + ((Map) afterRollback).get(ID));

      // check if state of collection was restored
      assertEquals(beforeModify, afterRollback);
      assertEquals(1, indexList.size());

      // check that list entry can now be modified
      indexList.set(0, obj2);
      assertEquals(obj2, indexList.get(0));
   }

   public void testNestedListWithModifySetRollback() throws Exception
   {
      System.out.println("testNestedListWithModifySetRollback");
      startTest();

      // create cached data objects
      Map<String, String> obj1 = new HashMap<String, String>();
      obj1.put(ID, "1");
      cache_.attach("/objs/1", obj1);
      obj1 = (Map<String, String>) cache_.find("/objs/1");

      Map<String, String> obj2 = new HashMap<String, String>();
      obj2.put(ID, "2");
      cache_.attach("/objs/2", obj2);
      obj2 = (Map<String, String>) cache_.find("/objs/2");

      // create cached collection of data objects
      List<Map<String, String>> indexList = new ArrayList<Map<String, String>>();
      cache_.attach(CONTAINER_FQN, indexList);
      indexList = (List<Map<String, String>>) cache_.find(CONTAINER_FQN);

      // initialize collection by adding a data object
      indexList.add(obj1);

      Object beforeModify = indexList.get(0);
      System.out.println("beforeModify: " + beforeModify + ", data object id: " + ((Map) beforeModify).get(ID));
      int objIndex;

      // modify the collection by replacing the first data object with the second
      // and then roll-back the transaction
      tx_mgr.begin();
      Object removedBySet = indexList.set(0, obj2);
      System.out.println("removedBySet: " + removedBySet + ", data object id: " + ((Map) removedBySet).get(ID));
      assertEquals(beforeModify, removedBySet);
      objIndex = indexList.indexOf(obj2);
      tx_mgr.rollback();

      // before rollback - object set
      assertEquals(0, objIndex);

      Object afterRollback = indexList.get(0);
      System.out.println("afterRollback: " + afterRollback + ", data object id: " + ((Map) afterRollback).get(ID));

      // check if state of collection was restored
      assertEquals(beforeModify, afterRollback);
      assertEquals(1, indexList.size());

      // check that list entry can now be modified
      indexList.set(0, obj2);
      assertEquals(obj2, indexList.get(0));
   }

   public void testNestedListWithRemoveRollback() throws Exception
   {
      System.out.println("testNestedListWithRemoveRollback");
      startTest();

      // create cached data objects
      Map<String, String> obj1 = new HashMap<String, String>();
      obj1.put(ID, "1");
      cache_.attach("/objs/1", obj1);
      obj1 = (Map<String, String>) cache_.find("/objs/1");

      Map<String, String> obj2 = new HashMap<String, String>();
      obj2.put(ID, "2");
      cache_.attach("/objs/2", obj2);
      obj2 = (Map<String, String>) cache_.find("/objs/2");

      // create cached collection of data objects
      List<Map<String, String>> indexList = new ArrayList<Map<String, String>>();
      cache_.attach(CONTAINER_FQN, indexList);
      indexList = (List<Map<String, String>>) cache_.find(CONTAINER_FQN);

      // initialize collection by adding a data object
      indexList.add(obj1);

      Object beforeModify = indexList.get(0);
      System.out.println("beforeModify: " + beforeModify + ", data object id: " + ((Map) beforeModify).get(ID));
      int objIndex;

      // modify the collection by replacing the first data object with the second
      // and then roll-back the transaction
      tx_mgr.begin();
      indexList.remove(obj1);
      objIndex = indexList.indexOf(obj1);
      tx_mgr.rollback();

      // before rollback - object removed
      assertEquals(-1, objIndex);

      Object afterRollback = indexList.get(0);
      System.out.println("afterRollback: " + afterRollback + ", data object id: " + ((Map) afterRollback).get(ID));

      // check if state of collection was restored
      assertEquals(beforeModify, afterRollback);
      assertEquals(1, indexList.size());

      // check that list entry can now be modified
      indexList.set(0, obj2);
      assertEquals(obj2, indexList.get(0));
   }


}
