/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache;

import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.buddyreplication.BuddyReplicationTestsBase;

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

/**
 * Stress test for buddy replication to try and detect deadlocks.
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class BuddyReplicationStressTest extends BuddyReplicationTestsBase
{
   private static Log log = LogFactory.getLog(BuddyReplicationStressTest.class);
   private static final int NUM_NODES = 10;
   private static final int NUM_BUDDIES = 2;
   private static final int NUM_REQ_THREADS = 100;
   private static final int NUM_REQ_LOOPS = 5000;
   private static final int MIN_SLEEP = 2000;
   private static final int MAX_SLEEP = 5000;

   private TreeCache[] caches = new TreeCache[NUM_NODES];
   private Requestor[] requestors = new Requestor[NUM_REQ_THREADS];
   private LoadBalancer loadBalancer;
   private NodeKiller nodeKiller;
   private NodeReviver nodeReviver;

   private Random random = new Random();

   public class Requestor extends Thread
   {
      private List exceptions = new ArrayList();
      private String session;

      public Requestor(int i)
      {
         super("Requestor-" + i);
      }

      public void run()
      {
         session = loadBalancer.getSession();
         log.warn(getName() + " got SESSION ID " + session);

         for (int i = 0; i < NUM_REQ_LOOPS; i++)
         {
            TreeCache cache = loadBalancer.getCache(session);

            try
            {
               cache.put("/JSESSIONID/" + session + "/stuff", "key" + i, "value");
            }
            catch (ReplicationException re)
            {
               // these will happen all the time - ignore them
               //re.printStackTrace();
            }
            catch (CacheException e)
            {
               exceptions.add(e);
            }
         }
      }

      public void checkResults() throws Exception
      {
         TreeCache cache = loadBalancer.getCache(session);
         for (int i = 0; i < NUM_REQ_LOOPS; i++)
         {
            assertEquals("Thread " + getName() + " in loop " + i, "value", cache.get("/JSESSIONID/" + session + "/stuff", "key" + i));
         }
      }

   }

   public class LoadBalancer
   {
      private Map sessionMap = new ConcurrentHashMap();

      public TreeCache getCache(String session)
      {
         int subscript = -1;
         boolean doPut = false;

         if (sessionMap.containsKey(session)) subscript = ((Integer) sessionMap.get(session)).intValue();

         while (subscript == -1 || caches[subscript] == null)
         {
            subscript = random.nextInt(caches.length);
            doPut = true;
         }

         if (doPut) sessionMap.put(session, new Integer(subscript));

         return caches[subscript];
      }

      public synchronized String getSession()
      {

         return UUIDGenerator.getRandomUUID();
      }
   }

   public class NodeKiller extends Thread
   {
      private boolean running = true;

      public void run()
      {
         while (running)
         {
            try
            {
               sleep(getRandomSleep());
            }
            catch (InterruptedException e)
            {
            }

            int victim = random.nextInt(caches.length);
            if (caches[victim] != null)
            {
               log.warn("*** Killing cache subscript " + victim);
               caches[victim].stopService();
               caches[victim] = null;
            }
         }
      }
   }

   public class NodeReviver extends Thread
   {
      private boolean running = true;

      public void run()
      {
         while (running)
         {
            try
            {
               sleep(getRandomSleep());
            }
            catch (InterruptedException e)
            {
            }

            for (int i = 0; i < caches.length; i++)
            {
               if (caches[i] == null)
               {
                  try
                  {
                     log.warn("*** Reviving cache subscript " + i);
                     caches[i] = createCache(NUM_BUDDIES, null, true, true);
                  }
                  catch (Exception e)
                  {
                     log.warn("Caught exception reviving cache " + i, e);
                  }
                  break;
               }
            }
         }
      }

   }


   public void setUp() throws Exception
   {
      /*loadBalancer = new LoadBalancer();
     nodeKiller = new NodeKiller();
     nodeReviver = new NodeReviver();

     caches = createCaches(NUM_BUDDIES, NUM_NODES, false, true);

     for (int i=0; i<NUM_REQ_THREADS; i++) requestors[i] = new Requestor(i);

     TestingUtil.blockUntilViewsReceived(caches, 30000);*/
   }

   public void tearDown() throws Exception
   {
      cleanup(caches);
   }

//    public void testBuddyReplication() throws Exception
//    {
//        nodeKiller.start();
//        nodeReviver.start();
//
//        for (int i=0; i<NUM_REQ_THREADS; i++) requestors[i].start();
//
//        // wait for threads to complete
//        for (int i=0; i<NUM_REQ_THREADS; i++) requestors[i].join();
//
//        // kill nodeKiller and nodeReviver
//        nodeKiller.running = false;
//        nodeReviver.running = false;
//        nodeKiller.interrupt();
//        nodeReviver.interrupt();
//
//        // test for errors
//        for (int i=0; i<NUM_REQ_THREADS; i++)
//        {
//            Iterator es = requestors[i].exceptions.iterator();
//            while (es.hasNext())
//            {
//                Exception e = (Exception) es.next();
//                log.warn("Found error in requestor " + i, e);
//            }
//        }
//
//        for (int i=0; i<NUM_REQ_THREADS; i++) assertEquals("Should have no exceptions", 0, requestors[i].exceptions.size());
//
//        // dump data in caches
//        for (int i=0; i<caches.length; i++)
//        {
//            if (caches[i] != null) log.warn("*** Cache contents: cache["+i+"] - " + caches[i].printLockInfo());
//        }
//
//        // test for data loss
//        for (int i=0; i<NUM_REQ_THREADS; i++) requestors[i].checkResults();
//    }

   public void testNothing()
   {
      assertTrue(true); // do nothing, should not be run in cruise control
   }

   private int getRandomSleep()
   {
      int sleep = -1;
      while (sleep < MIN_SLEEP) sleep = random.nextInt(MAX_SLEEP);
      return sleep;
   }
}

class UUIDGenerator
{
   private static List uuids = new ArrayList();
   private static Random r = new Random();
   static
   {
      uuids.add("560ea1d6-d3c2-409b-a358-4d6c7c450ba7");
      uuids.add("bed3844a-dc52-408b-94e9-64e7787f35e4");
      uuids.add("d59fcf53-00f7-456b-a817-1e64b5231e07");
      uuids.add("b5f5f6f2-4d82-4508-9daf-82f425101238");
      uuids.add("64936e40-950f-4bfd-9f0b-fe7b4dd95747");
      uuids.add("c95dcff4-f061-4809-b5de-5dd1908f471b");
      uuids.add("5d86509a-efec-4183-b3e2-746b7beeba13");
      uuids.add("1ffb2418-759a-4ef2-b3c0-51639276fe90");
      uuids.add("707a9c18-7e1b-404a-9eb3-6f9b5f30ccb7");
      uuids.add("00dff929-eadf-4d71-913c-7b560417bcb1");
      uuids.add("75353c75-7074-432e-bd2b-75a0ff222735");
      uuids.add("cfb5a497-33da-4d00-8b7a-39bc0c0104f9");
      uuids.add("c46b3382-cdf8-4d6d-8abe-22f53cf90860");
      uuids.add("b1c923b9-ed80-4bef-b8fc-59cf351acb17");
      uuids.add("6b6da8d5-862d-4010-a706-aad1ee79b6f2");
      uuids.add("f3a71142-59fc-4165-9a33-d4d480cef4bb");
      uuids.add("e8495312-a3ba-4610-8b11-3a425a27fa6f");
      uuids.add("a64c139d-2970-469d-8c8e-6415a0d20443");
      uuids.add("ba4d28b9-e652-42e9-841a-da7fb1648c41");
      uuids.add("d09e2ab1-74dc-430a-a3aa-4313b551bb97");
      uuids.add("2e9e326c-39ba-493f-a918-468a2ff81553");
      uuids.add("056cde88-329f-4d7d-957b-9fd7fb8d5242");
      uuids.add("624aa14e-ec90-4d4a-bd1b-4a46df6b7328");
      uuids.add("1958b764-1077-4b9c-9884-707dc530e7e0");
      uuids.add("432d508c-3e95-48d5-ae85-6825aeafe4ae");
      uuids.add("3712fbea-a218-4309-94c2-923de7f3e3e1");
      uuids.add("cf4ab42f-0323-49a8-8238-230c3ac4180a");
      uuids.add("82e1ecdd-2ddf-4fb4-9883-eabfe219b91d");
      uuids.add("d7d360f3-4576-4291-bfc6-aa1645dbff88");
      uuids.add("803ed2e9-8131-4801-9c26-2f403bb2b866");
      uuids.add("295d6b05-2069-42e0-8b47-782088becb12");
      uuids.add("65849b2d-8a64-4b6c-98e1-9997d2a06404");
      uuids.add("52232355-d790-42ef-9e82-d005f33b18bb");
      uuids.add("d1fc926c-c8fa-4b36-88c2-f323dd123358");
      uuids.add("d09188f6-907d-4a6f-930d-cbf363e27d6c");
      uuids.add("a6ad0287-e877-4cec-8566-a8dfd9156dda");
      uuids.add("a6e97a6f-b382-455d-8984-b1419ada6c83");
      uuids.add("993ca2b2-917d-4764-9814-2934ab27ebf4");
      uuids.add("6603d650-a03f-450d-9172-22965f0dbec2");
      uuids.add("850270ed-58cd-4a4d-8866-c9478d0c8e26");
      uuids.add("c4232601-9579-4b8d-8edd-2623c0985e5d");
      uuids.add("d760d9db-2f4c-45eb-9b77-93218795c490");
      uuids.add("50592596-675b-4f63-b20e-10ed8bf1177b");
      uuids.add("64ead256-7330-4afe-862d-9eae6241f59b");
      uuids.add("b70579f8-9e5f-411d-bbbf-07e2966af2b5");
      uuids.add("8c8377a5-7f23-4bc6-918b-008ee8e03baf");
      uuids.add("c832c342-1bcc-4e14-bef7-459f90afcf06");
      uuids.add("7c1d777e-d2ab-4ab4-9d2c-5e4705d06441");
      uuids.add("047a60b8-350a-4ded-8094-4996b4aa0eae");
      uuids.add("b2b167b8-98fc-4406-a62b-f694def42814");
      uuids.add("447312be-605d-4b85-a05b-9290fa8f04be");
      uuids.add("3b0e01d7-fe75-49f2-b9f6-c091f44e8fd3");
      uuids.add("78bc553b-b034-4296-a536-457d64d9c2e1");
      uuids.add("113f6054-cd3d-471f-972d-f57994a7e144");
      uuids.add("84e55627-8869-4512-a57c-01c71065e060");
      uuids.add("9f6b3a55-65b8-4a59-ab43-85b89e16c9b1");
      uuids.add("aa1e25ea-6a71-417a-8929-3103731c0ff3");
      uuids.add("de1e2439-ab37-4815-bdf8-ecc3b68f7460");
      uuids.add("7403a1a2-d37e-4461-8a23-ec9d9c3d1ff4");
      uuids.add("ca0e0ea8-848d-4241-890f-773742fdd28e");
      uuids.add("d5ff545a-bb57-4747-8813-58dff5704aaf");
      uuids.add("408ef6f5-59c2-4d7e-90b7-e33146eb08b8");
      uuids.add("219b321e-688f-44c2-9cfa-55fcffec0fe9");
      uuids.add("e8362302-0c99-4fad-8bb0-19064eae3826");
      uuids.add("85da5e60-38c1-4663-9b02-4990101f1721");
      uuids.add("c5cc78c8-31d3-4508-bf43-fe0b23d121e9");
      uuids.add("934f0fa6-b778-4eba-9e21-f49c776f66d2");
      uuids.add("3cba764a-61d5-44c0-a95b-3d0dfd7163d1");
      uuids.add("e25e7d3e-fa84-4130-a66f-2623583baa71");
      uuids.add("4cf1283f-5894-4b8f-8e5b-79bd8b84a573");
      uuids.add("795b40c6-327c-4142-90ce-ab6a57e4cdba");
      uuids.add("37aaf8c5-6754-407a-a140-e83f4365f609");
      uuids.add("649121cf-ac57-4b89-be34-2d5616f791ae");
      uuids.add("4e11ad91-436a-4b91-99a5-d17f5f3fdf80");
      uuids.add("562ce0c2-6184-412d-9750-eef5577d3e8b");
      uuids.add("b5fab0fc-04a1-44ef-8c52-61dffc2302ea");
      uuids.add("cb424bbc-77c2-4219-92c3-fefac58220b5");
      uuids.add("a631f37d-8761-49f3-abed-2655186703dc");
      uuids.add("7e650ffc-0418-4b6c-9057-aa026d2caf06");
      uuids.add("fcf2f2d8-4a43-4ad3-9fbe-4fb1b220a411");
      uuids.add("d4d66033-5c8d-4b0b-856e-e5937669aa89");
      uuids.add("c85e18b5-2dfe-4f98-a532-3396f67f2556");
      uuids.add("1cd53a18-5d01-464a-bedd-098e86ba910b");
      uuids.add("8588a619-03e5-4d27-a30b-b883a71acde5");
      uuids.add("2f77bc44-ced1-4389-a1d1-ee27e151d48d");
      uuids.add("48b0d50e-db29-4e33-9ee5-9701147503d2");
      uuids.add("8a41f6da-23d6-47d9-9037-f1773a10419b");
      uuids.add("5990c525-7a77-4639-bfd6-ebf2f67167e1");
      uuids.add("2a6a0dfe-4348-4ca4-b433-666c77457218");
      uuids.add("142087db-d88c-48cd-9731-56aee756b51f");
      uuids.add("63bf705b-40f3-406f-ac00-cf8ad42ce3a5");
      uuids.add("fb9ceb15-d7c1-4202-bd60-e50b9242b6d0");
      uuids.add("16a21b7d-ba39-439c-ab08-0b1967a45cb5");
      uuids.add("7b5a52cb-cb49-4459-9e23-4bd22e5ac49a");
      uuids.add("26dac214-bd72-40d8-97a9-b3fefc15a6c1");
      uuids.add("576c9dd7-5410-4c92-9e91-c07ff3da0ccd");
      uuids.add("16c7fa87-f782-4918-98ae-024b17cfe65e");
      uuids.add("0b890234-6c1a-4278-b02f-8a9f580cbb03");
      uuids.add("67385a01-18c9-4858-8fb7-76a0ff9346d0");
      uuids.add("611f12ad-bf67-426b-8134-3e1e389ffcee");
   }

   static String getRandomUUID()
   {
      return (String) uuids.get(r.nextInt(uuids.size()));
   }
}
