/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2006, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file 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.cache.eviction;


import org.jboss.cache.CacheFactory;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.RegionManager;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.EvictionConfig;
import org.jboss.cache.config.EvictionRegionConfig;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.factories.XmlConfigurationParser;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.util.TestingUtil;
import org.jboss.cache.xml.XmlHelper;
import static org.testng.AssertJUnit.*;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.w3c.dom.Element;

/**
 * Unit tests for programmatic configuration of LRU policy
 *
 * @author Ben Wang, Oct, 2006
 * @version $Revision: 5906 $
 */
@Test(groups = {"functional"})
public class ProgrammaticLRUPolicyTest
{
   CacheSPI<Object, Object> cache_;
   int wakeupIntervalMillis_ = 0;

   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception
   {
      initCaches();
      wakeupIntervalMillis_ = cache_.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000;
      log("wakeupInterval is " + wakeupIntervalMillis_);
      if (wakeupIntervalMillis_ < 0)
      {
         fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis_);
      }

   }

   private void initCaches()
   {
      Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true);
      CacheFactory<Object, Integer> instance = new DefaultCacheFactory();
      cache_ = (CacheSPI) instance.createCache(conf, false);
      conf.getEvictionConfig().setWakeupIntervalSeconds(5);
      cache_.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");
      cache_.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE);

      cache_.create();
      cache_.start();
   }

   @AfterMethod(alwaysRun = true)
   public void tearDown() throws Exception
   {
      cache_.stop();
   }

   private void addStringBasedRegion() throws Exception
   {
      // region name is ignored here.
      String xml = "<region name=\"/dummy\">" +
            "<attribute name=\"maxNodes\">10000</attribute>" +
            "<attribute name=\"timeToLiveSeconds\">4</attribute>" +
            "</region>";
      Element element = XmlHelper.stringToElement(xml);
      RegionManager regionManager = cache_.getRegionManager();
      EvictionConfig topConfig = cache_.getConfiguration().getEvictionConfig();
      EvictionRegionConfig erc = XmlConfigurationParser.parseEvictionRegionConfig(element, topConfig.getDefaultEvictionPolicyClass(), topConfig.getDefaultEventQueueSize());
      regionManager.setEvictionConfig(topConfig);
      // Fqn is the region name
      regionManager.getRegion("/programmatic", true).setEvictionPolicy(erc.getEvictionPolicyConfig());
   }

   public void testStringBasedFqnEviction() throws Exception
   {
      addStringBasedRegion();

      String rootStr = "/programmatic/";
      for (int i = 0; i < 10; i++)
      {
         String str = rootStr + i;
         Fqn<String> fqn = Fqn.fromString(str);
         cache_.put(fqn, str, str);
      }

      String val = (String) cache_.get(rootStr + "3", rootStr + "3");
      assertNotNull("DataNode should be empty ", val);

      System.out.println(cache_.toString());
      TestingUtil.sleepThread(2 * wakeupIntervalMillis_ + 500);
      System.out.println(cache_.toString());
      val = (String) cache_.get(rootStr + "3", rootStr + "3");
      assertNull("DataNode should be empty ", val);
   }

   private void addObjectBasedRegion() throws Exception
   {
      // region name is ignored here.
      String xml = "<region name=\"/dummy\">" +
            "<attribute name=\"maxNodes\">10000</attribute>" +
            "<attribute name=\"timeToLiveSeconds\">4</attribute>" +
            "</region>";
      Element element = XmlHelper.stringToElement(xml);
      RegionManager regionManager = cache_.getRegionManager();
      EvictionConfig topEC = cache_.getConfiguration().getEvictionConfig();
      EvictionRegionConfig erc = XmlConfigurationParser.parseEvictionRegionConfig(element,
            topEC.getDefaultEvictionPolicyClass(),
            topEC.getDefaultEventQueueSize());
      // Fqn is the region name
      Integer ii = 1;
      Fqn fqn = Fqn.fromElements(ii);
      regionManager.getRegion(fqn, true).setEvictionPolicy(erc.getEvictionPolicyConfig());
   }

   public void testObjectBasedFqnEviction1() throws Exception
   {
      addStringBasedRegion();

      String rootStr = "programmatic";
      for (int i = 0; i < 10; i++)
      {
         String str = rootStr;
         Integer in = i;
         Fqn fqn = Fqn.fromElements(rootStr, in);
         try
         {
            cache_.put(fqn, str, str);
         }
         catch (Exception e)
         {
            fail("Failed to insert data" + e);
            e.printStackTrace();
         }
      }

      Integer in = 3;
      Fqn fqn = Fqn.fromElements(rootStr, in);
      try
      {
         String val = (String) cache_.get(fqn, in);
         assertNull("DataNode should be empty ", val);
      }
      catch (Exception e)
      {
         e.printStackTrace();
         fail("Failed to get" + e);
      }

      System.out.println(cache_.toString());
      TestingUtil.sleepThread(2 * wakeupIntervalMillis_ + 500);
      System.out.println(cache_.toString());

      try
      {
         String val = (String) cache_.get(fqn, in);
         assertNull("DataNode should be empty ", val);
      }
      catch (Exception e)
      {
         e.printStackTrace();
         fail("Failed to get" + e);
      }
   }

   public void testObjectBasedFqnEviction2() throws Exception
   {
      addObjectBasedRegion();

      Integer ii = 1;
      Fqn<Integer> rootfqn = Fqn.fromElements(ii);
      for (int i = 0; i < 10; i++)
      {
         Integer in = i;
         Fqn<Object> fqn = Fqn.fromRelativeElements(rootfqn, in);
         try
         {
            cache_.put(fqn, in, in);
         }
         catch (Exception e)
         {
            fail("Failed to insert data" + e);
            e.printStackTrace();
         }
      }

      try
      {
         Integer in = 3;
         Fqn<Object> fqn = Fqn.fromRelativeElements(rootfqn, in);
         Object val = cache_.get(fqn, in);
         assertNotNull("DataNode should be empty ", val);
      }
      catch (Exception e)
      {
         e.printStackTrace();
         fail("Failed to get" + e);
      }

      System.out.println(cache_.toString());
      TestingUtil.sleepThread(2 * wakeupIntervalMillis_ + 500);
      System.out.println(cache_.toString());
      try
      {
         Integer in = 3;
         Fqn<Object> fqn = Fqn.fromRelativeElements(rootfqn, in);
         Object val = cache_.get(fqn, in);
         assertNull("DataNode should be empty ", val);
      }
      catch (Exception e)
      {
         e.printStackTrace();
         fail("Failed to get" + e);
      }
   }

   private void log(String msg)
   {
      System.out.println("-- " + msg);
   }

}
