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

import org.jboss.cache.Fqn;
import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.TreeCache;
import org.jboss.cache.TreeCacheMBean;
import org.jboss.cache.config.Option;
import org.jboss.cache.misc.TestingUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Tests handling of the buddy backup region during region 
 * activation and inactivation
 *
 * @author Brian Stansberry
 */
public class BuddyBackupActivationInactivationTest extends BuddyReplicationTestsBase
{
   public static final Fqn A = Fqn.fromString("/a");
   public static final Fqn A_B = Fqn.fromString("/a/b");
   public static final String JOE = "JOE";

   protected Map caches;
   private ClassLoader orig_TCL;
   
   public void testBuddyBackupActivation() throws Exception
   {
      TreeCacheMBean[] caches = new TreeCacheMBean[2];
      caches[0] = createCache("cache1", true, true, true);
      caches[1] = createCache("cache2", true, true, true);
      
      TestingUtil.blockUntilViewsReceived(caches, VIEW_BLOCK_TIMEOUT);
      
      caches[0].activateRegion("/a");
      
      caches[0].put(A_B, "name", JOE);
      
      TestingUtil.sleepThread(getSleepTimeout());
      
      caches[1].activateRegion("/a");
      
      Fqn fqn = new Fqn(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, 
            BuddyManager.getGroupNameFromAddress(caches[0].getLocalAddress()));
      fqn = new Fqn(fqn, A_B);
      
      assertEquals("State transferred with activation", JOE, caches[1].get(fqn, "name"));
   }
   
   public void testReplToInactiveRegion() throws Exception
   {
      TreeCacheMBean[] caches = new TreeCacheMBean[2];
      caches[0] = createCache("cache1", true, true, true);
      caches[1] = createCache("cache2", true, true, true);
      
      TestingUtil.blockUntilViewsReceived(caches, VIEW_BLOCK_TIMEOUT);
      
      caches[0].activateRegion("/a");
      
      // Activate the buddy backup subtree in the recipient so any
      // repl message doesn't get rejected due to that tree being inactive
      caches[1].activateRegion(BuddyManager.BUDDY_BACKUP_SUBTREE);
      
      caches[0].put(A_B, "name", JOE);
      
      TestingUtil.sleepThread(getSleepTimeout());
      
      assertNull("No replication to inactive region", caches[1].get(A_B, "name"));
      
      Fqn fqn = new Fqn(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, 
            BuddyManager.getGroupNameFromAddress(caches[0].getLocalAddress()));
      fqn = new Fqn(fqn, A_B);
      
      assertNull("No replication to inactive backup region", caches[1].get(fqn, "name"));
   }
   
   public void testBuddyBackupInactivation() throws Exception
   {
      TreeCacheMBean cache1 = createCache("cache1", true, true, true);
      
      cache1.activateRegion("/a");
      
      Option option = new Option();
      option.setCacheModeLocal(true);
      
      Fqn fqn = new Fqn(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, "test");
      fqn = new Fqn(fqn, A_B);
      cache1.put(fqn, "name", JOE, option);
      
      assertEquals("Put OK", JOE, cache1.get(fqn, "name"));
      
      cache1.inactivateRegion("/a");
      
      assertNull("Inactivation cleared region", cache1.get(fqn, "name"));
      
   }

   protected TreeCacheMBean createCache(String cacheID, 
                                        boolean sync, 
                                        boolean useMarshalling, 
                                        boolean startCache) 
      throws Exception
   {
      if (caches.get(cacheID) != null)
         throw new IllegalStateException(cacheID + " already created");
      
      TreeCacheMBean tree=new TreeCache();
      PropertyConfigurator config=new PropertyConfigurator();
      String configFile = sync ? "META-INF/replSync-service.xml"
                               : "META-INF/replAsync-service.xml";
      config.configure(tree, configFile); // read in generic replAsync xml
      tree.setClusterName("Test");
      if (useMarshalling) {
         tree.setUseRegionBasedMarshalling(true);
         tree.setInactiveOnStartup(true);
      }
      tree.setBuddyReplicationConfig(getBuddyConfig());
      // Put the cache in the map before starting, so if it fails in
      // start it can still be destroyed later
      caches.put(cacheID, tree);
      
      if (startCache)
      {
         tree.createService();
         tree.startService();
      }
      
      return tree;
   }

   protected void setUp() throws Exception
   {
      super.setUp();
      
      caches = new HashMap();
      
      // Save the TCL in case a test changes it
      orig_TCL = Thread.currentThread().getContextClassLoader();
   }

   protected void tearDown() throws Exception
   {
      super.tearDown();
      
      // Restore the TCL in case a test changed it
      Thread.currentThread().setContextClassLoader(orig_TCL);
      
      Set keys = caches.keySet();
      String[] cacheIDs = new String[keys.size()];
      cacheIDs = (String[]) keys.toArray(cacheIDs);
      for (int i = 0; i < cacheIDs.length; i++)
      {
         stopCache((TreeCache) caches.get(cacheIDs[i]));
      }
   }

   protected void stopCache(TreeCache cache)
   {
      if (cache != null)
      {
         try {
            cache.stopService();
            cache.destroyService();
         }
         catch (Exception e) {
            System.out.println("Exception stopping cache " + e.getMessage());
            e.printStackTrace(System.out);
         }
      }
   }
   
   private Element getBuddyConfig() throws Exception
   {
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder db = dbf.newDocumentBuilder();
      Document doc = db.newDocument();
      Element config = doc.createElement("config");
      doc.appendChild(config);
      Element replEnabled = doc.createElement("buddyReplicationEnabled");
      replEnabled.appendChild(doc.createTextNode("true"));
      config.appendChild(replEnabled);
      Element gravDisabled = doc.createElement("autoDataGravitation");
      gravDisabled.appendChild(doc.createTextNode("false"));
      config.appendChild(gravDisabled);
      return config;
   }
}
