/** * Copyright 2008 the original author or authors. * * 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 net.sf.katta.zk; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import net.sf.katta.AbstractKattaTest; import net.sf.katta.index.IndexMetaData; import net.sf.katta.util.KattaException; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.io.Writable; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.jmock.Expectations; import org.jmock.Mockery; public class ZKClientTest extends AbstractKattaTest { public void testStart() throws Exception { stopZkServer(); final ZKClient client = new ZKClient(_conf); try { client.start(500); fail("this should fail, since no zk server is yet started."); } catch (final Exception e) { // expected } startZkServer(); client.start(30000);// now should work client.close(); } public void testShowStructure() throws KattaException { final ZKClient client = new ZKClient(_conf); client.start(10000); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); client.showFolders(true, outputStream); String output = new String(outputStream.toByteArray()); assertTrue(output.contains("+katta")); assertTrue(output.contains("'--node-to-shard")); } public void testCreateFolder() throws KattaException { final ZKClient client = new ZKClient(_conf); final String path = "/katta"; client.start(10000); if (client.exists(path)) { assertTrue(client.deleteRecursive(path)); } assertFalse(client.exists(path)); client.create(path); assertTrue(client.exists(path)); assertTrue(client.delete(path)); final String value = "some value"; client.create(path, new Text(value)); final Text text = new Text(); client.readData(path, text); assertEquals(value, text.toString()); assertTrue(client.exists(path)); assertTrue(client.delete(path)); client.create(path); List<String> children = client.getChildren(path); assertEquals(0, children.size()); client.create("/katta/child1"); client.create("/katta/child2"); children = client.getChildren(path); assertEquals(2, children.size()); client.deleteRecursive(path); client.close(); } public void testChildNotifications() throws Exception { final ZKClient client = new ZKClient(_conf); client.start(10000); final MyListener listener = new MyListener(); final String file = "/childFile"; if (client.exists(file)) { client.deleteRecursive(file); } client.create(file); client.subscribeChildChanges(file, listener); for (int i = 0; i < 10; i++) { client.getEventLock().lock(); try { client.create(file + "/" + i); client.getEventLock().getDataChangedCondition().await(); } finally { client.getEventLock().unlock(); } } assertEquals(10, listener._counter); client.close(); } public void testDataNotifications() throws Exception { final ZKClient client = new ZKClient(_conf); client.start(10000); final MyListener listener = new MyListener(); final String katta = "/dataFile"; if (client.exists(katta)) { client.deleteRecursive(katta); } client.create(katta, new IndexMetaData("path", 3, IndexMetaData.IndexState.ANNOUNCED)); client.subscribeDataChanges(katta, listener); for (int i = 0; i < 10; i++) { client.getEventLock().lock(); try{ final IndexMetaData indexMetaData = new IndexMetaData("path", 3, IndexMetaData.IndexState.ANNOUNCED); client.writeData(katta, indexMetaData); client.getEventLock().getDataChangedCondition().await(); } finally { client.getEventLock().unlock(); } } assertEquals(10, listener._counter); client.close(); } public void testWatchProcessThreadSafeness() throws Exception { final ZKClient zkClient = new ZKClient(_conf); zkClient.start(5000); final String path = "/"; final int listenerCount = 20; final int threadCount = 10; final int watchEventsPerThread = 100; final Mockery mockery = new Mockery(); final List<IZkChildListener> childListeners = new ArrayList<IZkChildListener>(); for (int i = 0; i < listenerCount; i++) { childListeners.add(mockery.mock(IZkChildListener.class, "cl" + i)); } mockery.checking(new Expectations() { { for (final IZkChildListener childListener : childListeners) { atLeast(1).of(childListener).handleChildChange(with(equal(path)), with(aNonNull(List.class))); } } }); final AtomicInteger concurrentModificationExceptions = new AtomicInteger(); final AtomicInteger unknownExceptions = new AtomicInteger(); final Runnable fireWatchEventsRunnable = new Runnable() { public void run() { try { for (int i = 0; i < watchEventsPerThread; i++) { zkClient.process(new WatchedEvent(EventType.NodeChildrenChanged ,KeeperState.SyncConnected, path)); for (final IZkChildListener childListener : childListeners) { zkClient.unsubscribeChildChanges(path, childListener); zkClient.subscribeChildChanges(path, childListener); } } } catch (final ConcurrentModificationException e) { concurrentModificationExceptions.incrementAndGet(); e.printStackTrace(); } catch (final Exception e) { unknownExceptions.incrementAndGet(); e.printStackTrace(); } } }; final Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread(fireWatchEventsRunnable); threads[i].start(); } for (final Thread thread : threads) { thread.join(); } if (concurrentModificationExceptions.get() > 0) { fail(concurrentModificationExceptions.get() + " ConcurrentModificationException exceptions was thrown"); } if (unknownExceptions.get() > 0) { fail(unknownExceptions.get() + " unknown exceptions was thrown"); } zkClient.close(); mockery.assertIsSatisfied(); } protected class MyListener implements IZkChildListener, IZkDataListener { public int _counter = 0; public void handleChildChange(final String parentPath, final List<String> currentChilds) throws KattaException { handleEvent(); } public void handleDataAdded(final String dataPath, final Writable data) throws KattaException { handleEvent(); } public void handleDataChange(final String dataPath, final Writable data) throws KattaException { handleEvent(); } public void handleDataDeleted(final String dataPath) throws KattaException { handleEvent(); } public Writable createWritable() { return new IntWritable(); } private void handleEvent() { _counter++; } } }