/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.test.cache.test.local;

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

import java.util.*;

/**
 * Unit test for local TreeCache. Use locking and multiple threads to test
 * concurrent access to the tree.
 *
 * @version $Id: ConcurrentUnitTestCase.java,v 1.5.2.6 2005/12/01 19:50:26 dimitris Exp $
 */
public class ConcurrentUnitTestCase extends TestCase
{
   TreeCache cache;
   private Logger logger_ = Logger.getLogger(ConcurrentUnitTestCase.class);
   static Throwable thread_ex=null;

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

   public void setUp() throws Exception
   {
      super.setUp();
   }

   private void createCache(IsolationLevel level) throws Exception
   {
      cache = new TreeCache();
      cache.setCacheMode(TreeCache.LOCAL);
      cache.setIsolationLevel(level);
      cache.createService();
      cache.startService();
      cache.put("/a/b/c", null);
   }

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

   /** Commented out the test cases here tmeporarily because locking
    * is not wokring without tx in 1.1. Will fix this in 1.2.
    */
   public void testDummy() {
   }

   public void XtestConcurrentAccessWithRWLock() throws Throwable
   {
      createCache(IsolationLevel.REPEATABLE_READ);
      work_();
   }

   public void XtestConcurrentAccessWithSimpleLock() throws Throwable
   {
      createCache(IsolationLevel.SERIALIZABLE);
      work_();
   }




   private void work_() throws Throwable {
      Updater one, two;
      try {
         one = new Updater("Thread one");
         two = new Updater("Thread two");
         one.start();
         two.start();
         one.join();
         two.join();
         if(thread_ex != null)
            throw thread_ex;

         System.out.println("cache content: " + cache.toString());
         Set keys = cache.getKeys("/a/b/c");
         log("number of keys=" + keys.size());

         if(keys.size() != 1000) {
            try {
               System.out.println("size=" + keys.size());
               List l=new LinkedList(keys);
               Collections.sort(l);
               System.out.println("keys: " + l);
               for(int i=0; i < 1000; i++) {
                  if(!l.contains(new Integer(i)))
                     System.out.println("missing: " + i);
               }

               LinkedList duplicates=new LinkedList();
               for(Iterator it=l.iterator(); it.hasNext();) {
                  Integer integer=(Integer)it.next();
                  if(duplicates.contains(integer)) {
                     System.out.println(integer + " is a duplicate");
                  }
                  else
                     duplicates.add(integer);
               }
            }
            catch(Exception e1) {
               e1.printStackTrace();
            }
         }

         assertEquals(1000, keys.size());
         log("lock info:\n" + cache.printLockInfo());

      } catch (Exception e) {
         e.printStackTrace();
         fail(e.toString());
      }
   }


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

   void log(String msg)
   {
//        System.out.println("-- [" + Thread.currentThread() + "]: " + msg);
      logger_.debug(" [" + Thread.currentThread() + "]: " + msg);
   }


   class Updater extends Thread {
      String name=null;

      Updater(String name) {
         this.name=name;
      }
      public void run() {
         try {

            log("adding data");
            for(int i=0; i < 1000; i++) {
               log("adding data i=" + i);
               cache.put("/a/b/c", new Integer(i), name);
               yield();
            }
         }
         catch(Throwable t) {
            t.printStackTrace();
            thread_ex=t;
         }
      }
   }


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

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


}
