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

package org.jboss.cache.aop.memory;

import junit.framework.TestCase;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.aop.PojoCache;
import org.jboss.cache.aop.test.Person;
import org.jboss.cache.aop.test.Address;
import org.jboss.cache.aop.test.SerializedAddress;
import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.marshall.SelectedClassnameClassLoader;
import org.jboss.cache.misc.TestingUtil;

import javax.naming.Context;
import java.lang.ref.WeakReference;
import java.util.Properties;
import java.util.ArrayList;

/**
 *
 * @author Ben Wang
 */

public class ReplicatedAopTest extends TestCase
{
   Log log_= LogFactory.getLog(ReplicatedAopTest.class);
   PojoCache cache_;
   PojoCache cache1_;

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

   protected void setUp() throws Exception
   {
      super.setUp();
      Properties prop = new Properties();
      prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
      cache_ = new PojoCache();
      PropertyConfigurator config = new PropertyConfigurator(); // configure tree cache.
      config.configure(cache_, "META-INF/replSync-service.xml");

      cache1_ = new PojoCache();
      config.configure(cache1_, "META-INF/replSync-service.xml");
      cache_.start();
      cache1_.start();
   }

   protected void tearDown() throws Exception
   {
      super.tearDown();
      cache_.stop();
      cache1_.stop();
   }

//   public void testDummy() {}

   /**
    * Test replication with classloaders.
    * @throws Exception
    */
   public void testCLLeakageBasic() throws Exception
   {
      SerializedAddress add = new SerializedAddress();
      add.setCity("Taipei");

      ClassLoader cla = getClassLoader();
      WeakReference refa = new WeakReference(cla);
      cache_.registerClassLoader("/aop", cla);
      ClassLoader clb = getClassLoader();
      WeakReference refb = new WeakReference(clb);
      cache1_.registerClassLoader("/aop", clb);

      cache_.put("/aop", "add", add);

      TestingUtil.sleepThread(100);
      try
      {
         Object ben = cache1_.get("/aop", "add");
         assertEquals(add.toString(), ben.toString());
         ben = null;
      } catch (Exception ex)
      {
         fail("Test fails with exception " +ex);
      }

      cache_.remove("/aop", "add");

      ClassLoader clc = getClassLoader();
      cla=null;
      clb=null;
      cache_.registerClassLoader("/aop", clc); // replace cla
      cache1_.registerClassLoader("/aop", clc); // replace cla
      System.gc(); // force gc
      Thread.currentThread().sleep(1000);
      assertNull("Classloader should be gced ", refa.get());
      assertNull("Classloader should be gced ", refb.get());
   }

   private static void forceOutOfMemoryError() throws Exception
   {
      ArrayList list = new ArrayList();
      try
      {

         long i=0;
         while(true)
         {
            list.add("BigBigBigBigBigBigBigBigBigBigBigBigBigBigBigBigBigBigBigBigBigBigBigBig" + (i++));
         }
      }
      catch (Throwable ignored)
      {
      }
      list.clear();
      list=null;
      System.gc();
      Thread.currentThread().sleep(1000);
   }
   /**
    * Test replication with classloaders.
    * @throws Exception
    */
   public void testCLLeakage() throws Exception
   {
      Person p = new Person();
      p.setName("Ben");
      Address add = new Address();
      add.setCity("Taipei");

      ClassLoader cla = getClassLoader();
      WeakReference refa = new WeakReference(cla);
      cache_.registerClassLoader("/aop", cla);
      ClassLoader clb = getClassLoader();
      cache1_.registerClassLoader("/aop", clb);
      WeakReference refb = new WeakReference(clb);

      cache_.putObject("/aop", p);

      TestingUtil.sleepThread(100);
      try
      {
         Object ben = cache1_.getObject("/aop");
         assertEquals(p.toString(), ben.toString());
         ben = null;
      } catch (Exception ex)
      {
         fail("Test fails with exception " +ex);
      }

      cache_.removeObject("/aop");
      ClassLoader clc = getClassLoader();
      cache_.registerClassLoader("/aop", clc); // replace cla
      cache1_.registerClassLoader("/aop", clc); // replace cla
      cla = null;
      clb = null;
      forceOutOfMemoryError();

      assertNull("Classloader should be gced ", refa.get());
      assertNull("Classloader should be gced ", refb.get());
   }

   protected ClassLoader getClassLoader() throws Exception {
      String[] includesClasses = { "org.jboss.cache.aop.test.Person",
                                   "org.jboss.cache.aop.test.Address" };
      String [] excludesClasses = {};
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
      return new SelectedClassnameClassLoader(includesClasses, excludesClasses, cl);
   }

   public static Test suite() throws Exception
   {
      return new TestSuite(ReplicatedAopTest.class);
   }


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

}
