/*
 * 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 org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Map;
import java.util.Set;

/**
 * Tests ClusteredCacheLoader
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class ClusteredCacheLoaderTest extends AbstractCacheLoaderTestBase
{
    private static Log log = LogFactory.getLog(ClusteredCacheLoaderTest.class);
    private TreeCache cache1, cache2;
    private CacheLoader loader1, loader2;
    private Fqn fqn = Fqn.fromString("/a");
    private String key = "key";

    protected void setUp() throws Exception
    {
        if (cache1 != null || cache2 != null) tearDown();
        cache1 = new TreeCache("CCL-Test", null, 2000);
        cache2 = new TreeCache("CCL-Test", null, 2000);

        cache1.setCacheMode(TreeCache.REPL_SYNC);
        cache2.setCacheMode(TreeCache.REPL_SYNC);

        cache1.setCacheLoaderConfiguration( getSingleCacheLoaderConfig("", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=500", false, false, false) );
        cache2.setCacheLoaderConfiguration(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=500", false, false, false));

        cache1.startService();
        cache2.startService();

        loader1 = cache1.getCacheLoader();
        loader2 = cache2.getCacheLoader();
    }

    protected void tearDown()
    {
        if (cache1 != null)
        {
            cache1.stopService();
            cache1 = null;
            loader1 = null;
        }

        if (cache2 != null)
        {
            cache2.stopService();
            cache2 = null;
            loader2 = null;
        }
    }

    public void testGetKeyValue() throws Exception
    {
        cache1.put(fqn, key, "value");

        log.info("Finished put");
        // test that this has propagated.
        Assert.assertEquals("value", loader1.get(fqn).get(key));
        Assert.assertEquals("value", loader2.get(fqn).get(key));

        cache1.evict(fqn);

        // now cache 1 should not have this but cache 2 should.
        // loader1 looks at cache2 while loader2 looks at cache1
        Assert.assertEquals("value", loader1.get(fqn).get(key));
        Assert.assertNull("Expecting null", loader2.get(fqn));
//        // calling a get on cache1 should cause the loader to retrieve the node from cache2
        Assert.assertEquals("value", cache1.get(fqn, key));
//        // and now loader2 should see it
//        Assert.assertEquals("value", loader2.get(fqn).get(key));
    }

    public void testGet() throws Exception
    {
        cache1.put(fqn, key, "value");

        // test that this has propagated.
        Map map = loader1.get(fqn);
        Assert.assertTrue("Should contain key", map.containsKey(key));
        Assert.assertEquals("value", map.get(key));
        Assert.assertEquals(1, map.size());

        map = loader2.get(fqn);
        Assert.assertTrue("Should contain key", map.containsKey(key));
        Assert.assertEquals("value", map.get(key));
        Assert.assertEquals(1, map.size());

        cache1.evict(fqn);

        // now cache 1 should not have this but cache 2 should.
        // loader1 looks at cache2 while loader2 looks at cache1
        map = loader1.get(fqn);
        Assert.assertTrue(map.containsKey(key));
        Assert.assertEquals("value", map.get(key));
        Assert.assertEquals(1, map.size());

        Assert.assertNull("Expecting null", loader2.get(fqn));
        map = loader2.get(fqn);
        Assert.assertNull( "Should be null", map );

        // calling a get on cache1 should cause the loader to retrieve the node from cache2
        Assert.assertEquals("value", cache1.get(fqn, key));
        // and now loader2 should see it
        map = loader2.get(fqn);
        Assert.assertTrue(map.containsKey(key));
        Assert.assertEquals("value", map.get(key));
        Assert.assertEquals(1, map.size());
    }

    public void testGetChildrenNames() throws Exception
    {
        cache1.put(fqn, key, "value");
        Fqn child1 = new Fqn(fqn, "child1");
        Fqn child2 = new Fqn(fqn, "child2");
        Fqn child3 = new Fqn(fqn, "child3");
        cache1.put(child1, key, "value");
        cache1.put(child2, key, "value");
        cache1.put(child3, key, "value");

        // test that this has propagated.
        Set childNames = loader1.getChildrenNames(fqn);
        Assert.assertEquals(3, childNames.size());
        childNames = loader2.getChildrenNames(fqn);
        Assert.assertEquals(3, childNames.size());

        cache1.evict(child1);
        cache1.evict(child2);
        cache1.evict(child3);
        cache1.evict(fqn);

        // now cache 1 should not have this but cache 2 should.
        // loader1 looks at cache2 while loader2 looks at cache1
        childNames = loader1.getChildrenNames(fqn);
        Assert.assertEquals(3, childNames.size());

        childNames = loader2.getChildrenNames(fqn);
        Assert.assertNull("should be null", childNames);
        // calling a get on cache1 should cause the loader to retrieve the node from cache2
        Assert.assertEquals("value", cache1.get(fqn, key));
        // load up children
        Assert.assertEquals("value", cache1.get(child1, key));
        Assert.assertEquals("value", cache1.get(child2, key));
        Assert.assertEquals("value", cache1.get(child3, key));
        // and now loader2 should see it
        childNames = loader2.getChildrenNames(fqn);
        Assert.assertEquals(3, childNames.size());
    }

    public void testExists() throws Exception
    {
        cache1.put(fqn, key, "value");

        // test that this has propagated.
        Assert.assertTrue("should exist", loader1.exists(fqn));
        Assert.assertTrue("should exist", loader2.exists(fqn));

        cache1.evict(fqn);

        // now cache 1 should not have this but cache 2 should.
        // loader1 looks at cache2 while loader2 looks at cache1
        Assert.assertTrue("should exist", loader1.exists(fqn));
        Assert.assertTrue("should not exist", !loader2.exists(fqn));
        // calling a get on cache1 should cause the loader to retrieve the node from cache2
        Assert.assertEquals("value", cache1.get(fqn, key));
        // and now loader2 should see it
        Assert.assertTrue("should exist", loader2.exists(fqn));
    }
}
