org.midonet.cluster.EntityMonitorTest.java Source code

Java tutorial

Introduction

Here is the source code for org.midonet.cluster.EntityMonitorTest.java

Source

/*
 * Copyright 2014 Midokura SARL
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.midonet.cluster;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.Singleton;

import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Op;
import org.junit.Before;
import org.junit.Test;

import rx.Observable;
import rx.functions.Action0;
import rx.functions.Action1;

import org.midonet.cluster.data.Entity;
import org.midonet.midolman.config.MidolmanConfig;
import org.midonet.midolman.guice.config.ConfigProviderModule;
import org.midonet.midolman.guice.config.TypedConfigModule;
import org.midonet.midolman.guice.serialization.SerializationModule;
import org.midonet.midolman.guice.zookeeper.MockZookeeperConnectionModule;
import org.midonet.midolman.serialization.SerializationException;
import org.midonet.midolman.serialization.Serializer;
import org.midonet.midolman.state.AbstractZkManager;
import org.midonet.midolman.state.Directory;
import org.midonet.midolman.state.PathBuilder;
import org.midonet.midolman.state.StateAccessException;
import org.midonet.midolman.state.ZkManager;
import org.midonet.midolman.state.ZookeeperConnectionWatcher;
import org.midonet.midolman.state.zkManagers.BaseConfig;
import org.midonet.midolman.version.DataWriteVersion;
import org.midonet.midolman.version.guice.VersionModule;
import org.midonet.util.eventloop.MockReactor;
import org.midonet.util.eventloop.Reactor;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

public class EntityMonitorTest {

    public static class TestModule extends AbstractModule {

        String basePath = "/test";

        @Override
        public void configure() {
            bind(PathBuilder.class).toInstance(new PathBuilder(basePath));
            bind(Reactor.class).toInstance(new MockReactor());
            bind(TestEntityZkManager.class).asEagerSingleton();
        }

        @Provides
        @Singleton
        public ZkManager provideZkManager(Directory directory) {
            return new ZkManager(directory, basePath);
        }

    }

    public static class TestEntity extends Entity.Base<UUID, TestEntity.Data, TestEntity> {
        public static class Data {
            public String name;

            @Override
            public int hashCode() {
                return Objects.hash(name);
            }

            public boolean equals(Object o) {
                if (this == o)
                    return true;
                if (o == null || getClass() != o.getClass())
                    return false;
                Data that = (Data) o;
                return that.name.equals(name);
            }
        }

        public TestEntity(UUID id) {
            this(id, new Data());
        }

        public TestEntity(UUID id, Data data) {
            super(id, data);
        }

        @Override
        protected TestEntity self() {
            return this;
        }
    }

    public static class TestEntityConfig extends BaseConfig {
        private String name;

        public TestEntityConfig() {
            super();
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            TestEntityConfig that = (TestEntityConfig) o;
            return Objects.equals(this.name, that.name);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name);
        }
    }

    public static class TestEntityZkManager extends AbstractZkManager<UUID, TestEntityConfig>
            implements WatchableZkManager<UUID, TestEntityConfig> {

        public final String entitiesPath;

        @Inject
        public TestEntityZkManager(ZkManager zk, PathBuilder paths, Serializer serializer) {
            super(zk, paths, serializer);
            this.entitiesPath = paths.getBasePath() + "/testentity";
        }

        @Override
        protected String getConfigPath(UUID key) {
            return entitiesPath + "/" + key;
        }

        @Override
        protected Class<TestEntityConfig> getConfigClass() {
            return TestEntityConfig.class;
        }

        @Override
        public List<UUID> getAndWatchIdList(Runnable watcher) throws StateAccessException {
            return getUuidList(entitiesPath, watcher);
        }

        public void create(UUID id, TestEntityConfig cfg) throws SerializationException, StateAccessException {
            List<Op> ops = new ArrayList<>();
            ops.add(simpleCreateOp(id, cfg));
            zk.multi(ops);
        }

        public void update(UUID id, TestEntityConfig cfg) throws SerializationException, StateAccessException {
            List<Op> ops = new ArrayList<>();
            ops.add(simpleUpdateOp(id, cfg));
            zk.multi(ops);
        }

        public void delete(UUID id) throws SerializationException, StateAccessException {
            zk.delete(getConfigPath(id));
        }
    }

    public static final EntityMonitor.Transformer<UUID, TestEntityConfig, TestEntity> transformer = new EntityMonitor.Transformer<UUID, TestEntityConfig, TestEntity>() {
        @Override
        public TestEntity transform(UUID id, TestEntityConfig data) {
            TestEntity e = new TestEntity(id);
            e.getData().name = data.name;
            return e;
        }
    };

    EntityMonitor<UUID, TestEntityConfig, TestEntity> em = null;
    Injector injector = null;
    TestEntityZkManager teZkMgr = null;

    @Before
    public void setup() throws Exception {
        injector = Guice.createInjector(new VersionModule(), new SerializationModule(),
                new ConfigProviderModule(new HierarchicalConfiguration()), new MockZookeeperConnectionModule(),
                new TypedConfigModule<>(MidolmanConfig.class), new TestModule());

        PathBuilder paths = injector.getInstance(PathBuilder.class);
        Directory directory = injector.getInstance(Directory.class);
        teZkMgr = injector.getInstance(TestEntityZkManager.class);

        directory.add(paths.getBasePath(), null, CreateMode.PERSISTENT);
        directory.add(paths.getWriteVersionPath(), DataWriteVersion.CURRENT.getBytes(), CreateMode.PERSISTENT);
        directory.add(teZkMgr.entitiesPath, DataWriteVersion.CURRENT.getBytes(), CreateMode.PERSISTENT);

        ZookeeperConnectionWatcher zkConnWatcher = new ZookeeperConnectionWatcher();
        em = new EntityMonitor<>(teZkMgr, zkConnWatcher, transformer);
    }

    public static class Actions {
        public static <T> Action1<T> failAny(Class<T> k) {
            return new Action1<T>() {
                @Override
                public void call(T t) {
                    fail("Unexpected: " + t);
                }
            };
        }

        public static Action0 failAny() {
            return new Action0() {
                @Override
                public void call() {
                    fail("Unexpected");
                }
            };
        }
    }

    public static class Accumulator<T> implements Action1<T> {
        private final List<T> expected;
        private final List<T> found = new ArrayList<>();

        @SafeVarargs
        public static <T> Accumulator<T> exactly(T... items) {
            return new Accumulator<>(Arrays.asList(items));
        }

        public static <T> Accumulator<T> exactly(List<T> items) {
            return new Accumulator<>(items);
        }

        private Accumulator(List<T> items) {
            expected = new ArrayList<>(items);
        }

        @Override
        public void call(T t) {
            found.add(t);
        }

        public void verify() {
            assertEquals(expected, found);
        }

        public void verifyIgnoreOrder() {
            assertTrue(expected.containsAll(found) && expected.size() == found.size());
        }
    }

    /*
     * Adds two elements and watches them, then modifies one. We expect to
     * receive the appropriate notifications.
     */
    @Test
    public void test() throws Exception {

        // Ids
        UUID id1 = UUID.randomUUID();
        UUID id2 = UUID.randomUUID();

        // Element 1
        TestEntityConfig cfg1 = new TestEntityConfig();
        cfg1.setName("name_" + id1);

        // Element 1, version 2
        TestEntityConfig cfg1_2 = new TestEntityConfig();
        cfg1_2.setName("new_name_" + id1);

        // Element 2
        TestEntityConfig cfg2 = new TestEntityConfig();
        cfg2.setName("name_" + id2);

        TestEntity e1 = transformer.transform(id1, cfg1);
        TestEntity e1_2 = transformer.transform(id1, cfg1_2);
        TestEntity e2 = transformer.transform(id2, cfg2);

        teZkMgr.create(id1, cfg1);
        teZkMgr.create(id2, cfg2);

        Observable<TestEntity> obs = em.updated();
        assertNotNull(obs);

        Accumulator<TestEntity> acc = Accumulator.exactly(e1, e2, e1_2);
        obs.subscribe(acc, Actions.failAny(Throwable.class), Actions.failAny());

        em.watch(id1);
        em.watch(id2);

        teZkMgr.update(id1, cfg1_2);

        acc.verify();
    }

}