org.eclipse.gyrex.cloud.tests.internal.zookeeper.preferences.ZooKeeperPreferencesEnsambleTests.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.gyrex.cloud.tests.internal.zookeeper.preferences.ZooKeeperPreferencesEnsambleTests.java

Source

/*******************************************************************************
 * Copyright (c) 2011, 2012 AGETO Service GmbH and others.
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html.
 *
 * Contributors:
 *     Gunnar Wagenknecht - initial API and implementation
 *******************************************************************************/
package org.eclipse.gyrex.cloud.tests.internal.zookeeper.preferences;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.util.UUID;
import java.util.concurrent.TimeoutException;

import org.eclipse.gyrex.cloud.internal.CloudDebug;
import org.eclipse.gyrex.cloud.internal.zk.IZooKeeperLayout;
import org.eclipse.gyrex.cloud.internal.zk.ZooKeeperGate;
import org.eclipse.gyrex.cloud.tests.internal.CloudTestsActivator;
import org.eclipse.gyrex.cloud.tests.internal.zookeeper.BaseEnsambleTest;
import org.eclipse.gyrex.cloud.tests.internal.zookeeper.EnsembleHelper;
import org.eclipse.gyrex.cloud.tests.internal.zookeeper.ZooKeeperEnsambleTestSuite;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.NodeChangeEvent;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.core.runtime.preferences.IPreferencesService;

import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.RandomUtils;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * This is not a real test but must be called from inside
 * {@link ZooKeeperEnsambleTestSuite}
 */
public class ZooKeeperPreferencesEnsambleTests extends BaseEnsambleTest {

    protected static final int TIMEOUT = 10000;

    protected static final String KEY = "key";
    protected static final String DEFAULT_VALUE = "default-value";
    protected static final String VALUE = "value";

    private final String testablePreferenceName = "test";
    private IEclipsePreferences preferencesRoot;

    private TestablePreferencesService s1;
    private TestablePreferences r1;
    private TestablePreferencesService s2;
    private TestablePreferences r2;

    private String createNode() throws BackingStoreException, InterruptedException {
        final NodeChangeRecorder recorder1 = new NodeChangeRecorder();
        r1.addNodeChangeListener(recorder1);
        final NodeChangeRecorder recorder2 = new NodeChangeRecorder();
        r2.addNodeChangeListener(recorder2);

        final String testNodeName = "testNode" + System.currentTimeMillis();
        assertFalse("test node must not exists in tree 1", r1.nodeExists(testNodeName));
        assertFalse("test node must not exists in tree 2", r2.nodeExists(testNodeName));

        recorder1.assertEmpty();
        recorder2.assertEmpty();

        // create node in r1
        final Preferences testNode = r1.node(testNodeName);
        assertTrue("test node must exist in tree 1", r1.nodeExists(testNodeName));
        assertFalse("test node must not exist in tree 2", r2.nodeExists(testNodeName));

        final NodeChangeEvent added = recorder1.pollAdded();
        assertNotNull("no local added event", added);
        assertSame("not the same child node", testNode, added.getChild());
        assertSame("not the same parent node", r1, added.getParent());

        // no event should fire in r2
        recorder2.assertEmpty();

        // flush at root level
        assertSame("parent not the same object", r1, testNode.parent());
        r1.flush();

        // wait for event and verify
        final NodeChangeEvent r2Added = recorder2.pollAdded(TIMEOUT);
        assertNotNull("flushing a node should fire an event in tree 2", r2Added);
        assertSame("not the same node", r2, r2Added.getParent());

        // check that node exists
        assertTrue("test node must exist in tree 1", r1.nodeExists(testNodeName));
        assertTrue("test node must exist in tree 2", r2.nodeExists(testNodeName));

        // compare children (note, we cannot compare real node instances)
        assertEquals("children must be consistent", r1.testableGetChildren().keySet(),
                r2.testableGetChildren().keySet());

        // also compare the children version
        r1.assertChildrenVersionEquals(r2);

        return testNodeName;
    }

    private String randomPath() {
        IPath path = Path.EMPTY;
        final int segments = RandomUtils.nextInt(16);
        for (int i = 0; i < segments; i++) {
            path = path.append(StringUtils.substring(DigestUtils.shaHex(UUID.randomUUID().toString()), 0, 6));
        }
        return path.toString();
    }

    @Override
    @Before
    public void setUp() throws Exception {
        super.setUp();

        EnsembleHelper.assertRunningAndConnected();

        // enable debugging
        CloudDebug.debug = true;
        CloudDebug.zooKeeperPreferences = true;
        CloudDebug.zooKeeperPreferencesSync = true;

        // remove any existing data in ZooKeeper
        ZooKeeperGate.get().deletePath(IZooKeeperLayout.PATH_PREFERENCES_ROOT.append(testablePreferenceName));

        preferencesRoot = CloudTestsActivator.getInstance().getService(IPreferencesService.class).getRootNode();

        s1 = new TestablePreferencesService("s1");
        assertTrue("must be connected", s1.isConnected());
        r1 = new TestablePreferences(preferencesRoot, testablePreferenceName, s1);
        assertTrue("must be active", s1.isActive(r1));

        s2 = new TestablePreferencesService("s2");
        assertTrue("must be connected", s2.isConnected());
        r2 = new TestablePreferences(preferencesRoot, testablePreferenceName, s2);
        assertTrue("must be active", s2.isActive(r2));
    }

    private void setValue(final TestablePreferences local, final TestablePreferences remote)
            throws InterruptedException, TimeoutException, BackingStoreException {
        final PreferenceChangeRecorder recorderLocal = new PreferenceChangeRecorder();
        local.addPreferenceChangeListener(recorderLocal);
        final PreferenceChangeRecorder recorderRemote = new PreferenceChangeRecorder();
        remote.addPreferenceChangeListener(recorderRemote);

        // read old value
        final String oldLocal = local.get(KEY, null);
        final String oldRemote = remote.get(KEY, null);
        assertEquals("current values don't match; inconsistent state", oldLocal, oldRemote);

        // set a value in source
        final String value = VALUE + System.currentTimeMillis();
        local.put(KEY, value);

        // no event should fire in target
        assertNotNull("setting a value in local should fire an event in local", recorderLocal.peek());
        assertNull("setting a value in local should not fire an event in remote", recorderRemote.peek());

        // flush
        local.flush();

        // wait for event and verify
        final PreferenceChangeEvent r2Event = recorderRemote.poll(TIMEOUT);
        assertNotNull("flushing a local node should fire an event in remote after setting a value", r2Event);
        assertSame("not the same node", remote, r2Event.getNode());
        assertEquals("unexpected key", KEY, r2Event.getKey());
        assertEquals("unexpected new value", value, r2Event.getNewValue());
        assertEquals("unexpected old value", oldRemote, r2Event.getOldValue());

        // compare properties
        assertEquals("properties version must be consistent", local.testableGetPropertiesVersion(),
                remote.testableGetPropertiesVersion());
        assertEquals("properties must be consistent", local.testableGetProperties(),
                remote.testableGetProperties());
    }

    @Override
    @After
    public void tearDown() throws Exception {
        if (null != s1) {
            s1.shutdown();
        }
        if (null != s2) {
            s2.shutdown();
        }

        super.tearDown();
    }

    @Test
    public void test01SetValueRandomPath() throws Exception {
        for (int i = 0; i < 20; i++) {
            // vary source and target
            TestablePreferences source, target;
            if ((i % 2) == 0) {
                source = r1;
                target = r2;
            } else {
                source = r2;
                target = r1;
            }

            // randomize path (but we must use the same for source and target)
            final String path = randomPath();
            source = (TestablePreferences) source.node(path);
            target = (TestablePreferences) target.node(path);

            setValue(source, target);
        }
    }

    @Test
    public void test01SetValueSamePath() throws Exception {
        // get path
        final String path = randomPath();

        // spin loop
        for (int i = 0; i < 30; i++) {
            // vary source and target
            TestablePreferences source, target;
            if ((i % 2) == 0) {
                source = r1;
                target = r2;
            } else {
                source = r2;
                target = r1;
            }

            source = (TestablePreferences) source.node(path);
            target = (TestablePreferences) target.node(path);

            setValue(source, target);
        }
    }

    @Test
    public void test02RemoveValue() throws Exception {
        // set value first
        setValue(r1, r2);
        assertNotNull("must be executed after setValue", r2.get(KEY, null));

        final PreferenceChangeRecorder recorder1 = new PreferenceChangeRecorder();
        r1.addPreferenceChangeListener(recorder1);
        final PreferenceChangeRecorder recorder2 = new PreferenceChangeRecorder();
        r2.addPreferenceChangeListener(recorder2);

        // remove in r2
        r2.remove(KEY);

        // no event should fire in r1
        assertNotNull("removing a value in tree 2 should fire an event in tree 2", recorder2.peek());
        assertNull("removing a value in tree 2 should not fire an event in tree 1", recorder1.poll(TIMEOUT));

        // flush
        r2.flush();

        // wait for event and verify
        final PreferenceChangeEvent r1Event = recorder1.poll(TIMEOUT);
        assertNotNull("flushing a node should fire an event in tree 1 after removing a value in tree 2", r1Event);
        assertSame("not the same node", r1, r1Event.getNode());
        assertEquals("unexpected key", KEY, r1Event.getKey());
        assertNull("unexpected new value", r1Event.getNewValue());

        // compare properties
        assertEquals("properties version must be consistent", r1.testableGetPropertiesVersion(),
                r2.testableGetPropertiesVersion());
        assertEquals("properties must be consistent", r1.testableGetProperties(), r2.testableGetProperties());
    }

    @Test
    public void test03CreateNode() throws Exception {
        // we execute this test multiple times in order to detect any timing issues
        for (int i = 0; i < 10; i++) {
            createNode();
        }
    }

    @Test
    public void test04RemoveNode() throws Exception {
        // create node first
        final String testNodeName = createNode();

        assertTrue("test node must exist in tree 1", r1.nodeExists(testNodeName));
        assertTrue("test node must exist in tree 2", r2.nodeExists(testNodeName));
        final Preferences r1Child = r1.node(testNodeName);
        final Preferences r2Child = r2.node(testNodeName);

        final NodeChangeRecorder recorder1 = new NodeChangeRecorder();
        r1.addNodeChangeListener(recorder1);
        final NodeChangeRecorder recorder2 = new NodeChangeRecorder();
        r2.addNodeChangeListener(recorder2);

        // remove node in r2
        r2Child.removeNode();

        // check exists
        assertFalse("test node must not exist", r2Child.nodeExists(""));
        assertFalse("test node must not exist in tree 2", r2.nodeExists(testNodeName));
        assertTrue("test node must exist in tree 1", r1.nodeExists(testNodeName));

        final NodeChangeEvent removed = recorder2.pollRemoved();
        assertNotNull("no local added event", removed);
        assertSame("not the same child node", r2Child, removed.getChild());
        assertSame("not the same parent node", r2, removed.getParent());

        // no event should fire in r1
        recorder1.assertEmpty();

        // flush at root level
        r2.flush();

        // wait for event and verify
        final NodeChangeEvent r1Remove = recorder1.pollRemoved(TIMEOUT);
        assertNotNull("flushing a node should fire an event in tree 1", r1Remove);
        assertSame("not the same node", r1, r1Remove.getParent());

        // check that node exists
        assertFalse("test node not must exist in tree 1", r1.nodeExists(testNodeName));
        assertFalse("test node not must exist in tree 2", r2.nodeExists(testNodeName));
        assertFalse("r1Child must not exist", r1Child.nodeExists(""));
        assertFalse("r2Child must not exist", r2Child.nodeExists(""));

        // compare children (note, we cannot compare real node instances)
        assertEquals("children must be consistent", r1.testableGetChildren().keySet(),
                r2.testableGetChildren().keySet());

        // compare children versions
        r1.assertChildrenVersionEquals(r2);
    }
}