/*
 *
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.test.cache.test.local;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.jboss.cache.Node;
import org.jboss.cache.TreeCache;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.lock.LockStrategyFactory;
import org.jboss.cache.transaction.DummyTransactionManager;
import org.jboss.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.transaction.UserTransaction;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Tests transactional access to a local TreeCache.
 * Note: we use DummpyTranasctionManager to replace jta
 *
 * @version $Id: TxUnitTestCase.java,v 1.4.2.4 2004/09/23 18:43:49 bwang00 Exp $
 */
public class TxUnitTestCase extends TestCase {
   TreeCache cache=null;
   UserTransaction tx=null;
   Logger log;
   Properties p=null;
   String old_factory=null;
   final String FACTORY="org.jboss.cache.transaction.DummyContextFactory";


   public TxUnitTestCase(String name) {
      super(name);
   }

   public void setUp() throws Exception {
      super.setUp();
      old_factory=System.getProperty(Context.INITIAL_CONTEXT_FACTORY);
      System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY);
      DummyTransactionManager.getInstance();
      if(p == null) {
         p=new Properties();
         p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
      }

      // It'd better to set it from TreeCache. But this place can do as well.
      LockStrategyFactory.setIsolationLevel(IsolationLevel.REPEATABLE_READ);

      tx=(UserTransaction)new InitialContext(p).lookup("UserTransaction");

      cache=new TreeCache("test", null, 10000);
      cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
      cache.createService();
      cache.startService();
   }

   public void tearDown() throws Exception {
      if(cache != null)
         cache.stopService();

      // BW. kind of a hack to destroy jndi binding and thread local tx before next run.
      DummyTransactionManager.destroy();
      if(old_factory != null) {
         System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory);
         old_factory=null;
      }

      if(tx != null) {
         try {
            tx.rollback();
         }
         catch(Throwable t) {
         }
         tx=null;
      }
   }


   public void testPutTx() {
      try {
         tx.begin();
         cache.put("/a/b/c", "age", new Integer(38));
         assertEquals(new Integer(38), cache.get("/a/b/c", "age"));

         cache.put("/a/b/c", "age", new Integer(39));
         tx.commit();

         // This test is done outside the TX, it wouldn't work if someone else
         // modified "age". This works because we're the only TX running.
         assertEquals(new Integer(39), cache.get("/a/b/c", "age"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }


   public void testRollbackTx1() {
      try {
         tx.begin();
         cache.put("/a/b/c", "age", new Integer(38));
         cache.put("/a/b/c", "age", new Integer(39));
         tx.rollback();

         // This test is done outside the TX, it wouldn't work if someone else
         // modified "age". This works because we're the only TX running.
         assertNull(cache.get("/a/b/c", "age"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }


   public void testRollbackTx2() {
      try {
         tx.begin();
         cache.put("/a/b/c", "age", new Integer(38));
         cache.remove("/a/b/c", "age");
         tx.rollback();

         // This test is done outside the TX, it wouldn't work if someone else
         // modified "age". This works because we're the only TX running.
         assertNull(cache.get("/a/b/c", "age"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }

   public void testRollbackTx2a() {
      try {
         cache.put("/a/b/c", "age", new Integer(38));
         tx.begin();
         cache.remove("/a/b/c", "age");
         tx.rollback();

         // This test is done outside the TX, it wouldn't work if someone else
         // modified "age". This works because we're the only TX running.
         assertEquals(new Integer(38), cache.get("/a/b/c", "age"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }

   public void testRollbackTx3() {
      try {
         java.util.Map map1=new java.util.HashMap();
         map1.put("age", new Integer(38));
         java.util.Map map2=new java.util.HashMap();
         map2.put("age", new Integer(39));
         tx.begin();
         cache.put("/a/b/c", map1);
         cache.put("/a/b/c", map2);
         tx.rollback();

         // This test is done outside the TX, it wouldn't work if someone else
         // modified "age". This works because we're the only TX running.
         assertNull(cache.get("/a/b/c", "age"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }


   public void testRollbackTx4() {
      try {
         Map map=new HashMap();
         map.put("age", new Integer(38));
         tx.begin();
         cache.put("/a/b/c", map);
         cache.remove("/a/b/c");
         tx.rollback();

         // This test is done outside the TX, it wouldn't work if someone else
         // modified "age". This works because we're the only TX running.
         assertNull(cache.get("/a/b/c", "age"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }

   public void testNodeCreationRollback() {
      try {
         tx.begin();

         cache.put("/bela/ban", null);
         tx.rollback();

         assertNull("node should be not existent", cache.get("/bela/ban"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }

   public void testNodeCreationRollback2() {
      try {
         cache.put("/bela/ban", null);
         tx.begin();
         cache.put("/bela/ban/michelle", null);
         tx.rollback();
         assertNotNull("node should be not null", cache.get("/bela/ban"));
         assertNull("node should be not existent", cache.get("/bela/ban/michelle"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }

   public void testNodeDeletionRollback() {
      try {
         cache.put("/a/b/c", null);
         tx.begin();
         cache.remove("/a/b/c");
         assertNull(cache.get("/a/b/c"));
         cache.remove("/a/b");
         assertNull(cache.get("/a/b"));
         cache.remove("/a");
         assertNull(cache.get("/a"));
         tx.rollback();
         assertNotNull(cache.get("/a/b/c"));
         assertNotNull(cache.get("/a/b"));
         assertNotNull(cache.get("/a"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }


   public void testRemoveKeyRollback() {
      try {
         cache.put("/bela/ban", "name", "Bela");
         tx.begin();
         cache.remove("/bela/ban", "name");
         assertNull(cache.get("/bela/ban", "name"));
         tx.rollback();
         assertEquals("Bela", cache.get("/bela/ban", "name"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }


   public void testRemoveKeyRollback2() {
      try {
         Map m=new HashMap();
         m.put("name", "Bela");
         m.put("id", new Integer(322649));
         cache.put("/bela/ban", m);
         tx.begin();
         cache.remove("/bela/ban", "name");
         assertNull(cache.get("/bela/ban", "name"));
         tx.rollback();
         assertEquals("Bela", cache.get("/bela/ban", "name"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }

   public void testRemoveKeyRollback3() {
      try {
         cache.put("/bela/ban", "name", "Bela");
         tx.begin();
         cache.put("/bela/ban", "name", "Michelle");
         cache.remove("/bela/ban", "name");
         assertNull(cache.get("/bela/ban", "name"));
         tx.rollback();
         assertEquals("Bela", cache.get("/bela/ban", "name"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }

   /**
    * put(Fqn, Map) with a previous null map
    */
   public void testPutDataRollback1() {
      try {
         cache.put("/bela/ban", null); // create a node /bela/ban with a null map
         tx.begin();
         Map m=new HashMap();
         m.put("name", "Bela");
         m.put("id", new Integer(322649));
         cache.put("/bela/ban", m);
         tx.rollback();

         Node n=cache.get("/bela/ban");
         if(n.getData() == null) return;
         assertEquals("map should be empty", 0, n.getData().size());
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }

   /**
    * put(Fqn, Map) with a previous non-null map
    */
   public void testputDataRollback2() {
      Map m1, m2;
      m1=new HashMap();
      m1.put("name", "Bela");
      m1.put("id", new Integer(322649));
      m2=new HashMap();
      m2.put("other", "bla");
      m2.put("name", "Michelle");

      try {
         cache.put("/bela/ban", m1);
         tx.begin();

         cache.put("/bela/ban", m2);
         Map tmp=cache.get("/bela/ban").getData();
         assertTrue(tmp.size() == 3);
         assertTrue(tmp.get("name").equals("Michelle"));
         assertTrue(tmp.get("id").equals(new Integer(322649)));
         assertTrue(tmp.get("other").equals("bla"));
         tx.rollback();

         tmp=cache.get("/bela/ban").getData();
         assertTrue(tmp.size() == 2);
         assertTrue(tmp.get("name").equals("Bela"));
         assertTrue(tmp.get("id").equals(new Integer(322649)));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }


   public void testPutRollback() {
      try {
         cache.put("/bela/ban", null); // /bela/ban needs to exist
         tx.begin();
         cache.put("/bela/ban", "name", "Bela");
         assertEquals("Bela", cache.get("/bela/ban", "name"));
         tx.rollback();
         assertNull(cache.get("/bela/ban", "name"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }


   public void testPutRollback2() {
      try {
         cache.put("/bela/ban", "name", "Bela"); // /bela/ban needs to exist
         tx.begin();
         cache.put("/bela/ban", "name", "Michelle");
         assertEquals("Michelle", cache.get("/bela/ban", "name"));
         tx.rollback();
         assertEquals("Bela", cache.get("/bela/ban", "name"));
      }
      catch(Throwable t) {
         t.printStackTrace();
         fail(t.toString());
      }
   }

   void sleep(long timeout) {
      try {
         Thread.sleep(timeout);
      }
      catch(InterruptedException e) {
      }
   }

   void log(String msg) {
      log.info("-- [" + Thread.currentThread() + "]: " + msg);
   }


   public static Test suite() throws Exception {
      // return getDeploySetup(TxUnitTestCase.class, "cachetest.jar");
      return new TestSuite(TxUnitTestCase.class);
   }

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


}
