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

package org.jboss.cache.misc;

import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.jboss.cache.TreeCacheMBean;

import java.io.File;
import java.util.Vector;

/**
 * Utilities for unit testing JBossCache.
 * 
 * @author <a href="mailto://brian.stansberry@jboss.com">Brian Stansberry</a>
 * @version $Revision$
 */
public class TestingUtil
{

   /**
    * Kills a cache - stops it, clears any data in any cache loaders, and rolls back any associated txs
    */
   public static void killCaches(TreeCache[] caches)
   {
      if (caches == null) return;

      for (int i = 0; i<caches.length; i++)
      {
         TreeCache c = caches[i];
         if (c != null)
         {
            if (c.getTransactionManager() != null)
            {
               try
               {
                  c.getTransactionManager().rollback();
               }
               catch (Exception e)
               {
                  // don't care
               }
            }

            if (c.getCacheLoader() != null)
            {
               try
               {
                  c.getCacheLoader().remove(Fqn.ROOT);
               }
               catch (Exception e)
               {
                  // don't care
               }
            }

            c.stop();
            c.destroy();
         }
      }
   }

   /**
    * Loops, continually calling {@link #areCacheViewsComplete(TreeCache[])}
    * until it either returns true or <code>timeout</code> ms have elapsed.
    * 
    * @param caches     caches which must all have consistent views
    * @param timeout    max number of ms to loop
    * 
    * @throws RuntimeException  if <code>timeout</code> ms have elapse without
    *                           all caches having the same number of members.
    */
   public static void blockUntilViewsReceived(TreeCacheMBean[] caches, long timeout)
   {
      long failTime = System.currentTimeMillis() + timeout;
      
      while (System.currentTimeMillis() < failTime)
      {
         sleepThread(100);
         if (areCacheViewsComplete(caches))
            return;
      }
      
      throw new RuntimeException("timed out before caches had complete views");
   }
   
   /**
    * Loops, continually calling {@link #areCacheViewsComplete(TreeCache[])}
    * until it either returns true or <code>timeout</code> ms have elapsed.
    * 
    * @param caches     caches which must all have consistent views
    * @param groupSize  number of caches expected in the group
    * @param timeout    max number of ms to loop
    * 
    * @throws RuntimeException  if <code>timeout</code> ms have elapse without
    *                           all caches having the same number of members.
    */
   public static void blockUntilViewReceived(TreeCacheMBean cache, int groupSize, long timeout)
   {
      long failTime = System.currentTimeMillis() + timeout;
      
      while (System.currentTimeMillis() < failTime)
      {
         sleepThread(100);
         if (isCacheViewComplete(cache, groupSize))
            return;
      }
      
      throw new RuntimeException("timed out before caches had complete views");
   }
   
   /**
    * Checks each cache to see if the number of elements in the array
    * returned by {@link TreeCache#getMembers()} matches the size of 
    * the <code>caches</code> parameter.
    * 
    * @param caches caches that should form a View
    * @return   <code>true</code> if all caches have 
    *           <code>caches.length</code> members; false otherwise
    * 
    * @throws IllegalStateException if any of the caches have MORE view
    *                               members than caches.length
    */
   public static boolean areCacheViewsComplete(TreeCacheMBean[] caches)
   {
      int memberCount = caches.length;
      
      for (int i = 0; i < memberCount; i++)
      {
         TreeCacheMBean cache = caches[i];
         return isCacheViewComplete(cache, memberCount);
      }
      
      return true;
   }

   /**
    * 
    * @param cache
    * @param memberCount
    * @return
    */
   public static boolean isCacheViewComplete(TreeCacheMBean cache, int memberCount)
   {
      Vector members = cache.getMembers();
      if (members == null || memberCount > members.size())
      {
         return false;
      }
      else if (memberCount < members.size())
      {
         // This is an exceptional condition
         StringBuffer sb = new StringBuffer("Cache at address ");
         sb.append(cache.getLocalAddress());
         sb.append(" had ");
         sb.append(members.size());
         sb.append(" members; expecting ");
         sb.append(memberCount);
         sb.append(". Members were (");
         for (int j = 0; j < members.size(); j++)
         {
            if (j > 0)
               sb.append(", ");
            sb.append(members.elementAt(j));
         }
         sb.append(')');
         
         throw new IllegalStateException(sb.toString());
      }
      
      return true;
   }
   
   
   
   /**
    * Puts the current thread to sleep for the desired number of ms, suppressing
    * any exceptions.
    * 
    * @param sleeptime number of ms to sleep
    */
   public static void sleepThread(long sleeptime)
   {
      try
      {
         Thread.sleep(sleeptime);
      }
      catch (InterruptedException ie) {}
   }

   public static void recursiveRemove(File dir)
   {
      if (dir == null) return;
      if (!dir.isDirectory())
      {
         if (!dir.delete()) dir.deleteOnExit();
         return;
      }
      File[] files = dir.listFiles();
      if (files != null)
      {
         for (int i = 0; i < files.length; i++)
         {
            if (files[i].isDirectory())
            {
               recursiveRemove(files[i]);
            }
            else
            {
               if (!files[i].delete()) files[i].deleteOnExit();
            }
         }
      }
   }
}
