package org.jboss.cache.marshall;

import org.jboss.cache.Cache;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.Region;
import org.jboss.cache.util.TestingUtil;
import org.jboss.cache.config.CacheLoaderConfig;
import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.EvictionConfig;
import org.jboss.cache.config.EvictionRegionConfig;
import org.jboss.cache.eviction.LRUConfiguration;
import org.jboss.cache.eviction.LRUPolicy;
import org.jboss.cache.loader.JDBCCacheLoaderConfig;
import static org.testng.AssertJUnit.assertEquals;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

/**
 * Tests marshalling/unmarshalling during cache loader operations involving types
 * not visible to the cache's default classloader.
 *
 * @author <a href="mailto:brian.stansberry@jboss.org">Brian Stansberry</a>
 * @since 2.1.0
 */
@Test(groups = "functional")
public class CacheLoaderMarshallingJDBCTest extends RegionBasedMarshallingTestBase
{
   private static final String className = "org.jboss.cache.marshall.MyUUID";

   private Cache<Object, Object> cache;
   private Fqn fqn = Fqn.fromString("/a");

   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      originalClassLoader = Thread.currentThread().getContextClassLoader();
   }

   @AfterMethod(alwaysRun = true)
   protected void tearDown()
   {
      resetContextClassLoader();
      TestingUtil.killCaches(cache);
   }

   @Override
   protected ClassLoader getClassLoader()
   {
      String[] includesClasses = {className};
      String[] excludesClasses = {};
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
      return new SelectedClassnameClassLoader(includesClasses, excludesClasses, cl);
   }

   public void testCacheLoaderMarshalling() throws Exception
   {
      cacheLoaderMarshallingTest(false);
   }

   public void testCacheLoaderRegionBasedMarshalling() throws Exception
   {
      cacheLoaderMarshallingTest(true);
   }

   private void cacheLoaderMarshallingTest(boolean useRegionBased) throws Exception
   {
      cache = createCache(useRegionBased);
      cache.start();

      FooClassLoader loader = new FooClassLoader(originalClassLoader);

      if (useRegionBased)
      {
         Region r = cache.getRegion(Fqn.ROOT, true);
         r.registerContextClassLoader(loader);
         r.activate();
      }

      Class clazz = loader.loadFoo();
      Object obj = clazz.newInstance();

      Thread.currentThread().setContextClassLoader(loader);
      cache.put(fqn, "key", obj);

      this.resetContextClassLoader();
      cache.evict(fqn);

      Thread.currentThread().setContextClassLoader(loader);
      assertEquals(obj, cache.get(fqn, "key"));
   }

   private Cache createCache(boolean useRegionBased) throws Exception
   {
      Properties prop = getProperties();

      // ensure cleanup after each test
      prop.setProperty("cache.jdbc.table.drop", "true");

      Cache cache = new DefaultCacheFactory<Object, Object>().createCache(false);
      Configuration config = cache.getConfiguration();
      config.setUseRegionBasedMarshalling(useRegionBased);
      config.setInactiveOnStartup(useRegionBased);

      EvictionConfig ec = new EvictionConfig();
      ec.setDefaultEvictionPolicyClass(LRUPolicy.class.getName());
      ec.setWakeupIntervalSeconds(1000);  // a long time; really disabled
      EvictionRegionConfig erc = new EvictionRegionConfig();
      erc.setRegionFqn(Fqn.ROOT);
      erc.setRegionName("_default_");
      LRUConfiguration epc = new LRUConfiguration();
      epc.setMaxNodes(1000);
      epc.setTimeToLiveSeconds(1000);
      erc.setEvictionPolicyConfig(epc);
      List<EvictionRegionConfig> ercs = new ArrayList<EvictionRegionConfig>();
      ercs.add(erc);
      ec.setEvictionRegionConfigs(ercs);
      config.setEvictionConfig(ec);

      CacheLoaderConfig clc = new CacheLoaderConfig();
      clc.setPassivation(true);
      clc.setShared(false);
      JDBCCacheLoaderConfig jdbc_clc = new JDBCCacheLoaderConfig();
      jdbc_clc.setProperties(prop);

      clc.setIndividualCacheLoaderConfigs(Collections.<IndividualCacheLoaderConfig>singletonList(jdbc_clc));
      config.setCacheLoaderConfig(clc);

      return cache;
   }

   private Properties getProperties() throws Exception
   {
      Properties properties = new Properties();
      try
      {
         properties.load(this.getClass().getClassLoader().getResourceAsStream("cache-jdbc.properties"));
         return properties;
      }
      catch (Exception e)
      {
         throw new Exception("Error loading jdbc properties ", e);
      }
   }

}