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

import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.jboss.cache.xml.XmlHelper;
import org.w3c.dom.Element;

import javax.transaction.TransactionManager;
import java.io.File;

/**
 * Tests ignoreModifications and tests contents of individual loaders
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class ChainingCacheLoaderFullTest extends TestCase
{
    private String loc1 = System.getProperty("java.io.tmpdir", ".") + File.separator + "JBossCache-ChainingCacheLoaderBasicTest-1";
    private String loc2 = System.getProperty("java.io.tmpdir", ".") + File.separator + "JBossCache-ChainingCacheLoaderBasicTest-2";

    private TreeCache cache;
    private ChainingCacheLoader chainingCacheLoader;
    private CacheLoader loader1, loader2;
    private Fqn fqn = Fqn.fromString("/a/b");
    private String key = "key";
    private String value = "value";


    public ChainingCacheLoaderFullTest()
    {
        File dir1 = new File(loc1);
        File dir2 = new File(loc2);

        if (!dir1.exists()) dir1.mkdirs();
        if (!dir2.exists()) dir2.mkdirs();
    }

    protected void startCache(boolean ignoreMods1, boolean ignoreMods2) throws Exception
    {
        cache = new TreeCache();
        cache.setCacheLoaderConfiguration(getCacheLoaderConfig(loc1, loc2, ignoreMods1, ignoreMods2));
        cache.setCacheMode("local");
        cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
        cache.startService();

        chainingCacheLoader = (ChainingCacheLoader) cache.getCacheLoader();
        loader1 = (CacheLoader) chainingCacheLoader.getCacheLoaders().get(0);
        loader2 = (CacheLoader) chainingCacheLoader.getCacheLoaders().get(1);
    }

    protected void cleanup() throws Exception
    {
        cache.remove("/");
        cache.stopService();
        cache = null;
    }

    protected Element getCacheLoaderConfig(String loc1, String loc2, boolean ignoreMods1, boolean ignoreMods2) throws Exception
    {
        String xml = "<config>\n" +
                "<passivation>false</passivation>\n" +
                "<preload></preload>\n" +
                "<cacheloader>\n" +
                "<class>org.jboss.cache.loader.FileCacheLoader</class>\n" +
                "<properties>" +
                " location=" + loc1 + "\n" +
                "</properties>\n" +
                "<async>false</async>\n" +
                "<fetchPersistentState>true</fetchPersistentState>\n" +
                "<ignoreModifications>"+ignoreMods1+"</ignoreModifications>\n" +
                "</cacheloader>\n" +
                "<cacheloader>\n" +
                "<class>org.jboss.cache.loader.FileCacheLoader</class>\n" +
                "<properties>" +
                " location=" + loc2 + "\n" +
                "</properties>\n" +
                "<async>false</async>\n" +
                "<fetchPersistentState>false</fetchPersistentState>\n" +
                "<ignoreModifications>" + ignoreMods2 + "</ignoreModifications>\n" +
                "</cacheloader>\n" +
                "</config>";
        return XmlHelper.stringToElement(xml);
    }

    public void testCruds() throws Exception
    {
        startCache(false, false);

        // put something in the cache.
        cache.put(fqn, key, value);

        Assert.assertEquals(value, cache.get(fqn, key));
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value, loader1.get(fqn).get(key));
        Assert.assertEquals(value, loader2.get(fqn).get(key));

        // evict
        cache.evict(fqn);
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value, loader1.get(fqn).get(key));
        Assert.assertEquals(value, loader2.get(fqn).get(key));
        Assert.assertEquals(value, cache.get(fqn, key));

        // remove
        cache.remove(fqn);
        Assert.assertNull(value, cache.get(fqn, key));
        Assert.assertNull(value, chainingCacheLoader.get(fqn));
        Assert.assertNull(value, loader1.get(fqn));
        Assert.assertNull(value, loader2.get(fqn));

        cleanup();
    }

    public void testGets() throws Exception
    {
        startCache(false, false);

        cache.put(fqn, key, value);

        // test that loader1 is always looked up first.
        cache.evict(fqn);
        loader1.put(fqn, key, value + 2);
        Assert.assertEquals(value, loader2.get(fqn).get(key));
        Assert.assertEquals(value + 2, loader1.get(fqn).get(key));
        Assert.assertEquals(value + 2, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value + 2, cache.get(fqn, key));

        cache.remove("/");
        cache.put(fqn, key, value);

        // test that loader2 is NOT checked if loader1 has the value
        cache.evict(fqn);
        loader2.put(fqn, key, value + 2);
        Assert.assertEquals(value + 2, loader2.get(fqn).get(key));
        Assert.assertEquals(value, loader1.get(fqn).get(key));
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value, cache.get(fqn, key));

        cache.remove("/");
        cache.put(fqn, key, value);

        // test that loader2 is checked if loader1 returns a null
        cache.evict(fqn);
        loader1.remove(fqn);
        Assert.assertNull(loader1.get(fqn));
        Assert.assertEquals(value, loader2.get(fqn).get(key));
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value, cache.get(fqn, key));

        cleanup();
    }

    public void testIgnoreMods() throws Exception
    {
        startCache(false, true);

        // initialise the loaders
        loader1.put(fqn, key, value);
        loader2.put(fqn, key, value);

        // check contents
        Assert.assertEquals(value, cache.get(fqn, key));
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value, loader1.get(fqn).get(key));
        Assert.assertEquals(value, loader2.get(fqn).get(key));

        // do a put
        cache.put(fqn, key, value + 2);
        Assert.assertEquals(value + 2, cache.get(fqn, key));
        Assert.assertEquals(value + 2, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value + 2, loader1.get(fqn).get(key));
        Assert.assertEquals(value, loader2.get(fqn).get(key));

        // remove
        cache.remove(fqn);
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertNull(loader1.get(fqn));
        Assert.assertEquals(value, loader2.get(fqn).get(key));
        Assert.assertEquals(value, cache.get(fqn, key));

        cleanup();
    }

    public void testIgnoreModsTransactional() throws Exception
    {
        startCache(false, true);
        TransactionManager mgr = cache.getTransactionManager();

        // initialise the loaders
        loader1.put(fqn, key, value);
        loader2.put(fqn, key, value);

        // check contents
        Assert.assertEquals(value, cache.get(fqn, key));
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value, loader1.get(fqn).get(key));
        Assert.assertEquals(value, loader2.get(fqn).get(key));

        // do a put
        mgr.begin();
        cache.put(fqn, key, value + 2);
        Assert.assertEquals(value + 2, cache.get(fqn, key));
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value, loader1.get(fqn).get(key));
        Assert.assertEquals(value, loader2.get(fqn).get(key));
        mgr.commit();
        Assert.assertEquals(value + 2, cache.get(fqn, key));
        Assert.assertEquals(value + 2, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value + 2, loader1.get(fqn).get(key));
        Assert.assertEquals(value, loader2.get(fqn).get(key));

        // remove - not in a tx, see http://jira.jboss.com/jira/browse/JBCACHE-352
//        mgr.begin();
        cache.remove(fqn);

//        Assert.assertNull(cache.get(fqn, key));
//        Assert.assertEquals(value + 2, chainingCacheLoader.get(fqn).get(key));
//        Assert.assertEquals(value + 2, loader1.get(fqn).get(key));
//        Assert.assertEquals(value, loader2.get(fqn).get(key));
//        mgr.commit();
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertNull(loader1.get(fqn));
        Assert.assertEquals(value, loader2.get(fqn).get(key));
        Assert.assertEquals(value, cache.get(fqn, key));

        cleanup();
    }

    public void testCrudsTransactional() throws Exception
    {
        startCache(false, false);
        TransactionManager mgr = cache.getTransactionManager();

        // assert that the loaders ae empty
        chainingCacheLoader.remove(cache.getRoot().getFqn());
        Assert.assertNull(loader1.get(fqn));
        Assert.assertNull(loader2.get(fqn));
        Assert.assertNull(chainingCacheLoader.get(fqn));


        // put something in the cache.
        mgr.begin();
        cache.put(fqn, key, value);
        Assert.assertEquals(value, cache.get(fqn, key));
        Assert.assertNull(chainingCacheLoader.get(fqn));
        Assert.assertNull(loader1.get(fqn));
        Assert.assertNull(loader2.get(fqn));
        mgr.commit();
        Assert.assertEquals(value, cache.get(fqn, key));
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value, loader1.get(fqn).get(key));
        Assert.assertEquals(value, loader2.get(fqn).get(key));

        // evict
        cache.evict(fqn);
        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
        Assert.assertEquals(value, loader1.get(fqn).get(key));
        Assert.assertEquals(value, loader2.get(fqn).get(key));
        Assert.assertEquals(value, cache.get(fqn, key));

        // remove - not in a tx, see http://jira.jboss.com/jira/browse/JBCACHE-352
//        mgr.begin();
        cache.remove(fqn);
//        Assert.assertEquals(value, chainingCacheLoader.get(fqn).get(key));
//        Assert.assertEquals(value, loader1.get(fqn).get(key));
//        Assert.assertEquals(value, loader2.get(fqn).get(key));
//        Assert.assertNull(value, cache.get(fqn, key));
//        mgr.commit();
        Assert.assertNull(value, cache.get(fqn, key));
        Assert.assertNull(value, chainingCacheLoader.get(fqn));
        Assert.assertNull(value, loader1.get(fqn));
        Assert.assertNull(value, loader2.get(fqn));

        cleanup();
    }

    public static Test suite()
    {
        return new TestSuite(ChainingCacheLoaderFullTest.class);
    }

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