org.apache.curator.framework.imps.TestRemoveWatches.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.curator.framework.imps.TestRemoveWatches.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.curator.framework.imps;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.api.CuratorEventType;
import org.apache.curator.framework.api.CuratorListener;
import org.apache.curator.framework.api.CuratorWatcher;
import org.apache.curator.framework.imps.FailedRemoveWatchManager.FailedRemoveWatchDetails;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.retry.RetryOneTime;
import org.apache.curator.test.compatibility.CuratorTestBase;
import org.apache.curator.test.Timing;
import org.apache.curator.test.compatibility.Zk35MethodInterceptor;
import org.apache.curator.utils.CloseableUtils;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.WatcherType;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

@Test(groups = Zk35MethodInterceptor.zk35Group)
public class TestRemoveWatches extends CuratorTestBase {
    private AtomicReference<ConnectionState> registerConnectionStateListener(CuratorFramework client) {
        final AtomicReference<ConnectionState> state = new AtomicReference<ConnectionState>();
        client.getConnectionStateListenable().addListener(new ConnectionStateListener() {

            @Override
            public void stateChanged(CuratorFramework client, ConnectionState newState) {
                state.set(newState);
                synchronized (state) {
                    state.notify();
                }
            }
        });

        return state;
    }

    private boolean blockUntilDesiredConnectionState(AtomicReference<ConnectionState> stateRef, Timing timing,
            final ConnectionState desiredState) {
        if (stateRef.get() == desiredState) {
            return true;
        }

        //noinspection SynchronizationOnLocalVariableOrMethodParameter
        synchronized (stateRef) {
            if (stateRef.get() == desiredState) {
                return true;
            }

            try {
                stateRef.wait(timing.milliseconds());
                return stateRef.get() == desiredState;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
    }

    @Test
    public void testRemoveCuratorDefaultWatcher() throws Exception {
        Timing timing = new Timing();
        CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString())
                .retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            final CountDownLatch removedLatch = new CountDownLatch(1);

            final String path = "/";
            client.getCuratorListenable().addListener(new CuratorListener() {
                @Override
                public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception {
                    if (event.getType() == CuratorEventType.WATCHED
                            && event.getWatchedEvent().getType() == EventType.DataWatchRemoved) {
                        removedLatch.countDown();
                    }
                }
            });

            client.checkExists().watched().forPath(path);

            client.watches().removeAll().forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testRemoveCuratorWatch() throws Exception {
        Timing timing = new Timing();
        CuratorFrameworkImpl client = (CuratorFrameworkImpl) CuratorFrameworkFactory.builder()
                .connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            final CountDownLatch removedLatch = new CountDownLatch(1);

            final String path = "/";
            CuratorWatcher watcher = new CuratorWatcher() {

                @Override
                public void process(WatchedEvent event) throws Exception {
                    if (event.getPath().equals(path) && event.getType() == EventType.DataWatchRemoved) {
                        removedLatch.countDown();
                    }
                }
            };

            client.checkExists().usingWatcher(watcher).forPath(path);

            client.watches().remove(watcher).forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testRemoveWatch() throws Exception {
        Timing timing = new Timing();
        CuratorFrameworkImpl client = (CuratorFrameworkImpl) CuratorFrameworkFactory.builder()
                .connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            final CountDownLatch removedLatch = new CountDownLatch(1);

            final String path = "/";
            Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved);

            client.checkExists().usingWatcher(watcher).forPath(path);

            client.watches().remove(watcher).forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testRemoveWatchInBackgroundWithCallback() throws Exception {
        Timing timing = new Timing();
        CuratorFrameworkImpl client = (CuratorFrameworkImpl) CuratorFrameworkFactory.builder()
                .connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            //Make sure that the event fires on both the watcher and the callback.
            final CountDownLatch removedLatch = new CountDownLatch(2);
            final String path = "/";
            Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved);

            BackgroundCallback callback = new BackgroundCallback() {

                @Override
                public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
                    if (event.getType() == CuratorEventType.REMOVE_WATCHES && event.getPath().equals(path)) {
                        removedLatch.countDown();
                    }
                }
            };

            client.checkExists().usingWatcher(watcher).forPath(path);

            client.watches().remove(watcher).ofType(WatcherType.Any).inBackground(callback).forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");

        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testRemoveWatchInBackgroundWithNoCallback() throws Exception {
        Timing timing = new Timing();
        CuratorFrameworkImpl client = (CuratorFrameworkImpl) CuratorFrameworkFactory.builder()
                .connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            final String path = "/";
            final CountDownLatch removedLatch = new CountDownLatch(1);
            Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved);

            client.checkExists().usingWatcher(watcher).forPath(path);

            client.watches().remove(watcher).inBackground().forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");

        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testRemoveAllWatches() throws Exception {
        Timing timing = new Timing();
        CuratorFrameworkImpl client = (CuratorFrameworkImpl) CuratorFrameworkFactory.builder()
                .connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            final String path = "/";
            final CountDownLatch removedLatch = new CountDownLatch(2);

            Watcher watcher1 = new CountDownWatcher(path, removedLatch, EventType.ChildWatchRemoved);
            Watcher watcher2 = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved);

            client.getChildren().usingWatcher(watcher1).forPath(path);
            client.checkExists().usingWatcher(watcher2).forPath(path);

            client.watches().removeAll().forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testRemoveAllDataWatches() throws Exception {
        Timing timing = new Timing();
        CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString())
                .retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            final String path = "/";
            final AtomicBoolean removedFlag = new AtomicBoolean(false);
            final CountDownLatch removedLatch = new CountDownLatch(1);

            Watcher watcher1 = new BooleanWatcher(path, removedFlag, EventType.ChildWatchRemoved);
            Watcher watcher2 = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved);

            client.getChildren().usingWatcher(watcher1).forPath(path);
            client.checkExists().usingWatcher(watcher2).forPath(path);

            client.watches().removeAll().ofType(WatcherType.Data).forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");
            Assert.assertEquals(removedFlag.get(), false);
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testRemoveAllChildWatches() throws Exception {
        Timing timing = new Timing();
        CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString())
                .retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            final String path = "/";
            final AtomicBoolean removedFlag = new AtomicBoolean(false);
            final CountDownLatch removedLatch = new CountDownLatch(1);

            Watcher watcher1 = new BooleanWatcher(path, removedFlag, EventType.DataWatchRemoved);
            Watcher watcher2 = new CountDownWatcher(path, removedLatch, EventType.ChildWatchRemoved);

            client.checkExists().usingWatcher(watcher1).forPath(path);
            client.getChildren().usingWatcher(watcher2).forPath(path);

            client.watches().removeAll().ofType(WatcherType.Children).forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");
            Assert.assertEquals(removedFlag.get(), false);
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testRemoveLocalWatch() throws Exception {
        Timing timing = new Timing();
        CuratorFrameworkImpl client = (CuratorFrameworkImpl) CuratorFrameworkFactory.builder()
                .connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            AtomicReference<ConnectionState> stateRef = registerConnectionStateListener(client);

            final String path = "/";

            final CountDownLatch removedLatch = new CountDownLatch(1);

            Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved);

            client.checkExists().usingWatcher(watcher).forPath(path);

            //Stop the server so we can check if we can remove watches locally when offline
            server.stop();

            Assert.assertTrue(blockUntilDesiredConnectionState(stateRef, timing, ConnectionState.SUSPENDED));

            client.watches().removeAll().locally().forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testRemoveLocalWatchInBackground() throws Exception {
        Timing timing = new Timing();
        CuratorFrameworkImpl client = (CuratorFrameworkImpl) CuratorFrameworkFactory.builder()
                .connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            AtomicReference<ConnectionState> stateRef = registerConnectionStateListener(client);

            final String path = "/";

            final CountDownLatch removedLatch = new CountDownLatch(1);

            Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved);

            client.checkExists().usingWatcher(watcher).forPath(path);

            //Stop the server so we can check if we can remove watches locally when offline
            server.stop();

            Assert.assertTrue(blockUntilDesiredConnectionState(stateRef, timing, ConnectionState.SUSPENDED));

            client.watches().removeAll().locally().inBackground().forPath(path);

            Assert.assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal");
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    /**
     * Test the case where we try and remove an unregistered watcher. In this case we expect a NoWatcherException to
     * be thrown. 
     * @throws Exception
     */
    @Test(expectedExceptions = KeeperException.NoWatcherException.class)
    public void testRemoveUnregisteredWatcher() throws Exception {
        CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString())
                .retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            final String path = "/";
            Watcher watcher = new Watcher() {
                @Override
                public void process(WatchedEvent event) {
                }
            };

            client.watches().remove(watcher).forPath(path);
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    /**
     * Test the case where we try and remove an unregistered watcher but have the quietly flag set. In this case we expect success. 
     * @throws Exception
     */
    @Test
    public void testRemoveUnregisteredWatcherQuietly() throws Exception {
        Timing timing = new Timing();
        CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString())
                .retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            final AtomicBoolean watcherRemoved = new AtomicBoolean(false);

            final String path = "/";
            Watcher watcher = new BooleanWatcher(path, watcherRemoved, EventType.DataWatchRemoved);

            client.watches().remove(watcher).quietly().forPath(path);

            timing.sleepABit();

            //There should be no watcher removed as none were registered.
            Assert.assertEquals(watcherRemoved.get(), false);
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testGuaranteedRemoveWatch() throws Exception {
        Timing timing = new Timing();
        CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString())
                .retryPolicy(new RetryOneTime(1)).build();
        try {
            client.start();

            AtomicReference<ConnectionState> stateRef = registerConnectionStateListener(client);

            String path = "/";

            CountDownLatch removeLatch = new CountDownLatch(1);

            Watcher watcher = new CountDownWatcher(path, removeLatch, EventType.DataWatchRemoved);
            client.checkExists().usingWatcher(watcher).forPath(path);

            server.stop();

            Assert.assertTrue(blockUntilDesiredConnectionState(stateRef, timing, ConnectionState.SUSPENDED));

            //Remove the watch while we're not connected
            try {
                client.watches().remove(watcher).guaranteed().forPath(path);
                Assert.fail();
            } catch (KeeperException.ConnectionLossException e) {
                //Expected
            }

            server.restart();

            timing.awaitLatch(removeLatch);
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    @Test
    public void testGuaranteedRemoveWatchInBackground() throws Exception {
        Timing timing = new Timing();
        CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(),
                timing.connection(), new ExponentialBackoffRetry(100, 3));
        try {
            client.start();

            AtomicReference<ConnectionState> stateRef = registerConnectionStateListener(client);

            final CountDownLatch guaranteeAddedLatch = new CountDownLatch(1);

            ((CuratorFrameworkImpl) client)
                    .getFailedRemoveWatcherManager().debugListener = new FailedOperationManager.FailedOperationManagerListener<FailedRemoveWatchManager.FailedRemoveWatchDetails>() {

                        @Override
                        public void pathAddedForGuaranteedOperation(FailedRemoveWatchDetails detail) {
                            guaranteeAddedLatch.countDown();
                        }
                    };

            String path = "/";

            CountDownLatch removeLatch = new CountDownLatch(1);

            Watcher watcher = new CountDownWatcher(path, removeLatch, EventType.DataWatchRemoved);
            client.checkExists().usingWatcher(watcher).forPath(path);

            server.stop();
            Assert.assertTrue(blockUntilDesiredConnectionState(stateRef, timing, ConnectionState.SUSPENDED));

            //Remove the watch while we're not connected
            client.watches().remove(watcher).guaranteed().inBackground().forPath(path);

            timing.awaitLatch(guaranteeAddedLatch);

            server.restart();

            timing.awaitLatch(removeLatch);
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    private static class CountDownWatcher implements Watcher {
        private String path;
        private EventType eventType;
        private CountDownLatch removeLatch;

        public CountDownWatcher(String path, CountDownLatch removeLatch, EventType eventType) {
            this.path = path;
            this.eventType = eventType;
            this.removeLatch = removeLatch;
        }

        @Override
        public void process(WatchedEvent event) {
            if (event.getPath() == null || event.getType() == null) {
                return;
            }

            if (event.getPath().equals(path) && event.getType() == eventType) {
                removeLatch.countDown();
            }
        }
    }

    private static class BooleanWatcher implements Watcher {
        private String path;
        private EventType eventType;
        private AtomicBoolean removedFlag;

        public BooleanWatcher(String path, AtomicBoolean removedFlag, EventType eventType) {
            this.path = path;
            this.eventType = eventType;
            this.removedFlag = removedFlag;
        }

        @Override
        public void process(WatchedEvent event) {
            if (event.getPath() == null || event.getType() == null) {
                return;
            }

            if (event.getPath().equals(path) && event.getType() == eventType) {
                removedFlag.set(true);
            }
        }
    }
}