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

import org.jboss.cache.TreeCache;
import org.jboss.cache.misc.TestingUtil;

/**
 * Teststhe transfer of content under *normal* operation
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class BuddyReplicationContentTest extends BuddyReplicationTestsBase
{
    private String key = "key";
    private String value = "value";

    private void assertNoStaleLocks(TreeCache[] caches)
    {
        for (int i=0; i<caches.length; i++) assertNoStaleLocks(caches[i]);
    }

    private void assertNoStaleLocks(TreeCache cache)
    {
        assertEquals("Number of locks in cache instance " + cache + " should be 0", 0, cache.getNumberOfLocksHeld());
    }

    public void testSimplePut() throws Exception
    {
        TreeCache[] caches = createCaches(3, false);

        try
        {
            String fqn = "/test";
            String backupFqn = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + BuddyManager.getGroupNameFromAddress(caches[0].getLocalAddress()) + fqn;

            assertNoStaleLocks(caches);

            // put something in cache 1
            caches[0].put(fqn, key, value);

            assertNoStaleLocks(caches);


            // this should be in neither of the other caches' "main" trees
            assertEquals(value, caches[0].get(fqn, key));
            assertNull("Should be null", caches[1].get(fqn, key));
            assertNull("Should be null", caches[2].get(fqn, key));

            // check the backup trees

            assertEquals("Buddy should have data in backup tree", value, caches[1].get(backupFqn, key));
            assertNull("Should be null", caches[2].get(backupFqn, key));

            assertNoStaleLocks(caches);
        }
        finally
        {
            cleanup( caches );
        }
    }

    public void testPutAndRemove() throws Exception
    {
        TreeCache[] caches = createCaches(3, false);

        try
        {
            String fqn = "/test";
            String backupFqn = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + BuddyManager.getGroupNameFromAddress(caches[0].getLocalAddress()) + fqn;

            assertNoStaleLocks(caches);

            // put something in cache 1
            caches[0].put(fqn, key, value);

            assertNoStaleLocks(caches);

            // this should be in neither of the other caches' "main" trees
            assertEquals(value, caches[0].get(fqn, key));
            assertNull("Should be null", caches[1].get(fqn, key));
            assertNull("Should be null", caches[2].get(fqn, key));

            // check the backup trees

            assertEquals("Buddy should have data in backup tree", value, caches[1].get(backupFqn, key));
            assertNull("Should be null", caches[2].get(backupFqn, key));

            assertNoStaleLocks(caches);

            // now remove
            caches[0].remove(fqn);
            assertNoStaleLocks(caches);


            assertNull("Should be null", caches[0].get(fqn, key));
            assertNull("Should be null", caches[1].get(fqn, key));
            assertNull("Should be null", caches[2].get(fqn, key));

            // check the backup trees
            assertNull("Should be null", caches[0].get(backupFqn, key));
            assertNull("Should be null", caches[1].get(backupFqn, key));
            assertNull("Should be null", caches[2].get(backupFqn, key));

            assertNoStaleLocks(caches);

        }
        finally
        {
            cleanup( caches );
        }

    }

    public void testPutAndRemove2() throws Exception
    {
        TreeCache[] caches = createCaches(2, 4, false);

        try
        {
            String fqn = "/test";
            String backupFqn = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + BuddyManager.getGroupNameFromAddress(caches[0].getLocalAddress()) + fqn;

            // put something in cache 1
            assertNoStaleLocks(caches);

            caches[0].put(fqn, key, value);

            assertNoStaleLocks(caches);

            // this should be in neither of the other caches' "main" trees
            assertEquals(value, caches[0].get(fqn, key));
            assertNull("Should be null", caches[1].get(fqn, key));
            assertNull("Should be null", caches[2].get(fqn, key));
            assertNull("Should be null", caches[3].get(fqn, key));

            // check the backup trees
            assertEquals("Buddy should have data in backup tree", value, caches[1].get(backupFqn, key));
            assertEquals("Buddy should have data in backup tree", value, caches[2].get(backupFqn, key));
            assertNull("Should be null", caches[3].get(backupFqn, key));

            assertNoStaleLocks(caches);

            // now remove
            caches[0].remove(fqn);
            assertNoStaleLocks(caches);

            assertNull("Should be null", caches[0].get(fqn, key));
            assertNull("Should be null", caches[1].get(fqn, key));
            assertNull("Should be null", caches[2].get(fqn, key));
            assertNull("Should be null", caches[3].get(fqn, key));

            // check the backup trees
            assertNull("Should be null", caches[0].get(backupFqn, key));
            assertNull("Should be null", caches[1].get(backupFqn, key));
            assertNull("Should be null", caches[2].get(backupFqn, key));
            assertNull("Should be null", caches[3].get(backupFqn, key));

            assertNoStaleLocks(caches);
        }
        finally
        {
            cleanup( caches );
        }
    }

    public void testBuddyJoin() throws Exception
    {
        TreeCache[] caches = createCaches(2, false);
        TreeCache cache2 = null;

        try
        {
            String fqn = "/test";
            String backupFqn = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + BuddyManager.getGroupNameFromAddress(caches[1].getLocalAddress()) + fqn;

            assertNoStaleLocks(caches);

            // put something in cache 1
            caches[1].put(fqn, key, value);

            assertNoStaleLocks(caches);

            // this should be in neither of the other caches' "main" trees
            assertEquals(value, caches[1].get(fqn, key));
            assertNull("Should be null", caches[0].get(fqn, key));

            // check the backup trees
            assertEquals("Buddy should have data in backup tree", value, caches[0].get(backupFqn, key));

            assertNoStaleLocks(caches);

            // now add a new cache to the cluster
            cache2 = createCache(1, null);

            // allow this cache a few msecs to join
            TestingUtil.sleepThread(500);

            // now caches[1]'s buddy should be cache2, not cache[0]
            assertIsBuddy(caches[1], cache2, true);
            // this should still be the same
            assertIsBuddy(caches[0], caches[1], true);
            // and cache2's buddy should be cache[0]
            assertIsBuddy(cache2, caches[0], true);

            // so now the backup data we saw on cache[0] should have been removed.
            assertNull("This backup data should have been removed", caches[0].get(backupFqn, key));

            // while cache2 should now posess this backup (due to a state transfer)
            assertEquals("Backup state should have been transferred to this new cache instance", value, cache2.get(backupFqn, key));

            caches[1].remove(fqn);
            assertNoStaleLocks(caches);


            assertNull("Should be null", caches[0].get(fqn, key));
            assertNull("Should be null", caches[1].get(fqn, key));
            assertNull("Should be null", cache2.get(fqn, key));

            // check the backup trees
            assertNull("Should be null", caches[0].get(backupFqn, key));
            assertNull("Should be null", caches[1].get(backupFqn, key));
            assertNull("Should be null", cache2.get(backupFqn, key));

            assertNoStaleLocks(caches);

        }
        finally
        {
            if (cache2 != null) cache2.stopService();
            cleanup( caches );
        }
    }

    public void testCompleteStateSurvival() throws Exception
    {
        TreeCache[] caches = null;

        try
        {
            caches = createCaches(3, false, true);
            caches[0].put("/0", "key", "value");
            caches[1].put("/1", "key", "value");
            caches[2].put("/2", "key", "value");

            TestingUtil.sleepThread(getSleepTimeout());

            caches[2].stopService();

            System.out.println("*** " + caches[0].printLockInfo());
            System.out.println("*** " + caches[1].printLockInfo());
            System.out.println("*** " + caches[2].printLockInfo());


            TestingUtil.sleepThread(getSleepTimeout());

            assertEquals("value", caches[0].get("/2", "key"));

            TestingUtil.sleepThread(getSleepTimeout());

            caches[1].stopService();

            // cache[0] is all thats left!!


            assertEquals("value", caches[0].get("/0", "key"));
            assertEquals("value", caches[1].get("/1", "key"));
            assertEquals("value", caches[2].get("/2", "key"));
        }
        finally
        {
            cleanup(caches);
        }
    }
}
