/* * Copyright 2013 NGDATA nv * * 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.lilyproject.repository.model.impl; import org.apache.commons.io.FileUtils; import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.lilyproject.repository.model.api.RepositoryDefinition; import org.lilyproject.repository.model.api.RepositoryModel; import org.lilyproject.repository.model.api.RepositoryModelEvent; import org.lilyproject.repository.model.api.RepositoryModelEventType; import org.lilyproject.repository.model.api.RepositoryModelListener; import org.lilyproject.util.net.NetUtils; import org.lilyproject.util.zookeeper.ZkUtil; import org.lilyproject.util.zookeeper.ZooKeeperItf; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class RepositoryModelTest { private MiniZooKeeperCluster zkCluster; private File zkDir; private int zkClientPort; private ZooKeeperItf zk; private RepositoryModel repositoryModel; @Before public void setUpBeforeClass() throws Exception { zkDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "lily.repositorymodeltest"); zkClientPort = NetUtils.getFreePort(); zkCluster = new MiniZooKeeperCluster(); zkCluster.setDefaultClientPort(zkClientPort); zkCluster.startup(zkDir); zk = ZkUtil.connect("localhost:" + zkClientPort, 15000); repositoryModel = new RepositoryModelImpl(zk); } @After public void tearDownAfterClass() throws Exception { if (zk != null) { zk.close(); } if (repositoryModel != null) { ((RepositoryModelImpl)repositoryModel).close(); } if (zkCluster != null) { zkCluster.shutdown(); FileUtils.deleteDirectory(zkDir); } } @Test public void testRepositories() throws Exception { TestListener listener = new TestListener(); repositoryModel.registerListener(listener); repositoryModel.create("repo1"); listener.waitForEvents(1); listener.verifyEvents(new RepositoryModelEvent(RepositoryModelEventType.REPOSITORY_ADDED, "repo1")); assertFalse(repositoryModel.repositoryExistsAndActive("repo1")); assertEquals(2, repositoryModel.getRepositories().size()); // 2 because the default repository is also there repositoryModel.updateRepository(new RepositoryDefinition("repo1", RepositoryDefinition.RepositoryLifecycleState.ACTIVE)); listener.waitForEvents(1); listener.verifyEvents(new RepositoryModelEvent(RepositoryModelEventType.REPOSITORY_UPDATED, "repo1")); assertTrue(repositoryModel.repositoryExistsAndActive("repo1")); repositoryModel.delete("repo1"); listener.waitForEvents(1); listener.verifyEvents(new RepositoryModelEvent(RepositoryModelEventType.REPOSITORY_UPDATED, "repo1")); assertEquals(2, repositoryModel.getRepositories().size()); assertEquals(RepositoryDefinition.RepositoryLifecycleState.DELETE_REQUESTED, repositoryModel.getRepository("repo1").getLifecycleState()); repositoryModel.deleteDirect("repo1"); assertEquals(1, repositoryModel.getRepositories().size()); listener.waitForEvents(1); listener.verifyEvents(new RepositoryModelEvent(RepositoryModelEventType.REPOSITORY_REMOVED, "repo1")); repositoryModel.unregisterListener(listener); repositoryModel.create("repo1"); Thread.sleep(20); // this is not very robust, but if the code would be wrong this will fail most of the time listener.waitForEvents(0); } @Test public void testTwoModelInstances() throws Exception { ZooKeeperItf zk1 = ZkUtil.connect("localhost:" + zkClientPort, 15000); ZooKeeperItf zk2 = ZkUtil.connect("localhost:" + zkClientPort, 15000); RepositoryModel repositoryModel1 = new RepositoryModelImpl(zk1); RepositoryModel repositoryModel2 = new RepositoryModelImpl(zk2); TestListener model1Listener = new TestListener(); assertEquals(1, repositoryModel1.getRepositories(model1Listener).size()); TestListener model2Listener = new TestListener(); assertEquals(1, repositoryModel2.getRepositories(model2Listener).size()); repositoryModel1.create("repo1"); repositoryModel1.create("repo2"); repositoryModel2.create("repo3"); repositoryModel2.create("repo4"); model2Listener.waitForEvents(4); model1Listener.waitForEvents(4); assertEquals(5, repositoryModel1.getRepositories().size()); assertEquals(5, repositoryModel2.getRepositories().size()); ((RepositoryModelImpl)repositoryModel1).close(); ((RepositoryModelImpl)repositoryModel2).close(); zk1.close(); zk2.close(); } @Test(expected = Exception.class) public void testDefaultRepositoryCannotBeDeleted() throws Exception { repositoryModel.delete("default"); } @Test(expected = Exception.class) public void testDefaultRepositoryCannotBeDirectDeleted() throws Exception { repositoryModel.deleteDirect("default"); } @Test(expected = Exception.class) public void testDefaultRepositoryCannotBeUpdated() throws Exception { repositoryModel.updateRepository(new RepositoryDefinition("default", RepositoryDefinition.RepositoryLifecycleState.ACTIVE)); } @Test(expected = Exception.class) public void testInvalidRepositoryName() throws Exception { repositoryModel.create("repository#"); } @Test(expected = Exception.class) public void testInvalidRepositoryName2() throws Exception { repositoryModel.create("repository__"); } public static class TestListener implements RepositoryModelListener { private List<RepositoryModelEvent> events = new ArrayList<RepositoryModelEvent>(); @Override public void process(RepositoryModelEvent event) { events.add(event); } public void waitForEvents(int count) throws InterruptedException { long now = System.currentTimeMillis(); synchronized (this) { while (events.size() < count && System.currentTimeMillis() - now < 60000) { Thread.sleep(50); } } } public void verifyEvents(RepositoryModelEvent... expectedEvents) { if (events.size() != expectedEvents.length) { if (events.size() > 0) { System.out.println("The events are:"); for (RepositoryModelEvent event : events) { System.out.println(event.getEventType() + " - " + event.getRepositoryName()); } } else { System.out.println("There are no events."); } assertEquals("Expected number of events", expectedEvents.length, events.size()); } Set<RepositoryModelEvent> expectedEventsSet = new HashSet<RepositoryModelEvent>(Arrays.asList(expectedEvents)); for (RepositoryModelEvent event : expectedEvents) { if (!events.contains(event)) { fail("Expected event not present among events: " + event); } } for (RepositoryModelEvent event : events) { if (!expectedEventsSet.contains(event)) { fail("Got an event which is not among the expected events: " + event); } } events.clear(); } } public static class DummyWatcher implements Watcher { @Override public void process(WatchedEvent watchedEvent) { } } }