Repository: curator Updated Branches: refs/heads/CURATOR-3.0 d26c38dba -> c6a22ba50
Deprecated PersistentEphemeralNode in favor of PersistentNode which is the same code but now accepts any createmode Project: http://git-wip-us.apache.org/repos/asf/curator/repo Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/fefbba1c Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/fefbba1c Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/fefbba1c Branch: refs/heads/CURATOR-3.0 Commit: fefbba1cc3bd5641983657440b40e25425165a6a Parents: 45332f3 Author: randgalt <[email protected]> Authored: Tue Jan 12 11:45:38 2016 -0500 Committer: randgalt <[email protected]> Committed: Tue Jan 12 11:45:38 2016 -0500 ---------------------------------------------------------------------- .../recipes/nodes/PersistentEphemeralNode.java | 310 +-------------- .../framework/recipes/nodes/PersistentNode.java | 382 +++++++++++++++++++ .../src/site/confluence/group-member.confluence | 2 +- .../persistent-ephemeral-node.confluence | 20 +- .../nodes/TestPersistentEphemeralNode.java | 6 +- .../TestPersistentEphemeralNodeListener.java | 1 + .../recipes/nodes/TestPersistentNode.java | 62 +++ 7 files changed, 468 insertions(+), 315 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/curator/blob/fefbba1c/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentEphemeralNode.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentEphemeralNode.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentEphemeralNode.java index 684e0d9..5576dc2 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentEphemeralNode.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentEphemeralNode.java @@ -19,30 +19,8 @@ package org.apache.curator.framework.recipes.nodes; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable; -import org.apache.curator.framework.api.BackgroundCallback; -import org.apache.curator.framework.api.CreateModable; -import org.apache.curator.framework.api.CuratorEvent; -import org.apache.curator.framework.api.CuratorWatcher; -import org.apache.curator.framework.state.ConnectionState; -import org.apache.curator.framework.state.ConnectionStateListener; -import org.apache.curator.utils.PathUtils; import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher.Event.EventType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.io.Closeable; -import java.io.IOException; -import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; /** * <p> @@ -52,85 +30,18 @@ import java.util.concurrent.atomic.AtomicReference; * <p> * Thanks to bbeck (https://github.com/bbeck) for the initial coding and design * </p> + * + * @deprecated This has been replaced with the more general {@link PersistentNode} */ -public class PersistentEphemeralNode implements Closeable +@Deprecated +public class PersistentEphemeralNode extends PersistentNode { - private final AtomicReference<CountDownLatch> initialCreateLatch = new AtomicReference<CountDownLatch>(new CountDownLatch(1)); - private final Logger log = LoggerFactory.getLogger(getClass()); - private final CuratorFramework client; - private final CreateModable<ACLBackgroundPathAndBytesable<String>> createMethod; - private final AtomicReference<String> nodePath = new AtomicReference<String>(null); - private final String basePath; - private final Mode mode; - private final AtomicReference<byte[]> data = new AtomicReference<byte[]>(); - private final AtomicReference<State> state = new AtomicReference<State>(State.LATENT); - private final AtomicBoolean authFailure = new AtomicBoolean(false); - private final BackgroundCallback backgroundCallback; - private final CuratorWatcher watcher = new CuratorWatcher() - { - @Override - public void process(WatchedEvent event) throws Exception - { - if ( event.getType() == EventType.NodeDeleted ) - { - createNode(); - } - else if ( event.getType() == EventType.NodeDataChanged ) - { - watchNode(); - } - } - }; - private final BackgroundCallback checkExistsCallback = new BackgroundCallback() - { - @Override - public void processResult(CuratorFramework client, CuratorEvent event) throws Exception - { - if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() ) - { - createNode(); - } - } - }; - private final BackgroundCallback setDataCallback = new BackgroundCallback() - { - - @Override - public void processResult(CuratorFramework client, CuratorEvent event) - throws Exception - { - //If the result is ok then initialisation is complete (if we're still initialising) - //Don't retry on other errors as the only recoverable cases will be connection loss - //and the node not existing, both of which are already handled by other watches. - if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) - { - //Update is ok, mark initialisation as complete if required. - initialisationComplete(); - } - } - }; - private final ConnectionStateListener connectionStateListener = new ConnectionStateListener() - { - @Override - public void stateChanged(CuratorFramework client, ConnectionState newState) - { - if ( newState == ConnectionState.RECONNECTED ) - { - createNode(); - } - } - }; - - private enum State - { - LATENT, - STARTED, - CLOSED - } - /** * The mode for node creation + * + * @deprecated This has been replaced with the more general {@link PersistentNode} */ + @Deprecated public enum Mode { /** @@ -216,212 +127,9 @@ public class PersistentEphemeralNode implements Closeable * @param basePath the base path for the node * @param initData data for the node */ + @SuppressWarnings("deprecation") public PersistentEphemeralNode(CuratorFramework client, Mode mode, String basePath, byte[] initData) { - this.client = Preconditions.checkNotNull(client, "client cannot be null"); - this.basePath = PathUtils.validatePath(basePath); - this.mode = Preconditions.checkNotNull(mode, "mode cannot be null"); - final byte[] data = Preconditions.checkNotNull(initData, "data cannot be null"); - - backgroundCallback = new BackgroundCallback() - { - @Override - public void processResult(CuratorFramework client, CuratorEvent event) throws Exception - { - String path = null; - boolean nodeExists = false; - if ( event.getResultCode() == KeeperException.Code.NODEEXISTS.intValue() ) - { - path = event.getPath(); - nodeExists = true; - } - else if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) - { - path = event.getName(); - } - else if ( event.getResultCode() == KeeperException.Code.NOAUTH.intValue() ) - { - log.warn("Client does not have authorisation to write ephemeral node at path {}", event.getPath()); - authFailure.set(true); - return; - } - if ( path != null ) - { - authFailure.set(false); - nodePath.set(path); - watchNode(); - - if ( nodeExists ) - { - client.setData().inBackground(setDataCallback).forPath(getActualPath(), getData()); - } - else - { - initialisationComplete(); - } - } - else - { - createNode(); - } - } - }; - - createMethod = mode.isProtected() ? client.create().creatingParentContainersIfNeeded().withProtection() : client.create().creatingParentContainersIfNeeded(); - this.data.set(Arrays.copyOf(data, data.length)); - } - - private void initialisationComplete() - { - CountDownLatch localLatch = initialCreateLatch.getAndSet(null); - if ( localLatch != null ) - { - localLatch.countDown(); - } - } - - /** - * You must call start() to initiate the persistent ephemeral node. An attempt to create the node - * in the background will be started - */ - public void start() - { - Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Already started"); - - client.getConnectionStateListenable().addListener(connectionStateListener); - createNode(); - } - - /** - * Block until the either initial node creation initiated by {@link #start()} succeeds or - * the timeout elapses. - * - * @param timeout the maximum time to wait - * @param unit time unit - * @return if the node was created before timeout - * @throws InterruptedException if the thread is interrupted - */ - public boolean waitForInitialCreate(long timeout, TimeUnit unit) throws InterruptedException - { - Preconditions.checkState(state.get() == State.STARTED, "Not started"); - - CountDownLatch localLatch = initialCreateLatch.get(); - return (localLatch == null) || localLatch.await(timeout, unit); - } - - @Override - public void close() throws IOException - { - if ( !state.compareAndSet(State.STARTED, State.CLOSED) ) - { - return; - } - - client.getConnectionStateListenable().removeListener(connectionStateListener); - - try - { - deleteNode(); - } - catch ( Exception e ) - { - throw new IOException(e); - } - } - - /** - * Returns the currently set path or null if the node does not exist - * - * @return node path or null - */ - public String getActualPath() - { - return nodePath.get(); - } - - /** - * Set data that ephemeral node should set in ZK also writes the data to the node - * - * @param data new data value - * @throws Exception errors - */ - public void setData(byte[] data) throws Exception - { - data = Preconditions.checkNotNull(data, "data cannot be null"); - this.data.set(Arrays.copyOf(data, data.length)); - if ( isActive() ) - { - client.setData().inBackground().forPath(getActualPath(), getData()); - } - } - - /** - * Return the current value of our data - * - * @return our data - */ - public byte[] getData() - { - return this.data.get(); - } - - private void deleteNode() throws Exception - { - String localNodePath = nodePath.getAndSet(null); - if ( localNodePath != null ) - { - try - { - client.delete().guaranteed().forPath(localNodePath); - } - catch ( KeeperException.NoNodeException ignore ) - { - // ignore - } - } - } - - private void createNode() - { - if ( !isActive() ) - { - return; - } - - try - { - String existingPath = nodePath.get(); - String createPath = (existingPath != null && !mode.isProtected()) ? existingPath : basePath; - createMethod.withMode(mode.getCreateMode(existingPath != null)).inBackground(backgroundCallback).forPath(createPath, data.get()); - } - catch ( Exception e ) - { - throw new RuntimeException("Creating node. BasePath: " + basePath, e); // should never happen unless there's a programming error - so throw RuntimeException - } - } - - private void watchNode() throws Exception - { - if ( !isActive() ) - { - return; - } - - String localNodePath = nodePath.get(); - if ( localNodePath != null ) - { - client.checkExists().usingWatcher(watcher).inBackground(checkExistsCallback).forPath(localNodePath); - } - } - - private boolean isActive() - { - return (state.get() == State.STARTED); - } - - @VisibleForTesting - boolean isAuthFailure() - { - return authFailure.get(); + super(client, mode.getCreateMode(false), mode.isProtected(), basePath, initData); } } http://git-wip-us.apache.org/repos/asf/curator/blob/fefbba1c/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentNode.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentNode.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentNode.java new file mode 100644 index 0000000..c66eb30 --- /dev/null +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentNode.java @@ -0,0 +1,382 @@ +/** + * 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.recipes.nodes; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable; +import org.apache.curator.framework.api.BackgroundCallback; +import org.apache.curator.framework.api.CreateBuilder; +import org.apache.curator.framework.api.CreateModable; +import org.apache.curator.framework.api.CuratorEvent; +import org.apache.curator.framework.api.CuratorWatcher; +import org.apache.curator.framework.state.ConnectionState; +import org.apache.curator.framework.state.ConnectionStateListener; +import org.apache.curator.utils.PathUtils; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher.Event.EventType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.Closeable; +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/** + * <p> + * A persistent ephemeral node is an ephemeral node that attempts to stay present in + * ZooKeeper, even through connection and session interruptions. + * </p> + * <p> + * Thanks to bbeck (https://github.com/bbeck) for the initial coding and design + * </p> + */ +public class PersistentNode implements Closeable +{ + private final AtomicReference<CountDownLatch> initialCreateLatch = new AtomicReference<CountDownLatch>(new CountDownLatch(1)); + private final Logger log = LoggerFactory.getLogger(getClass()); + private final CuratorFramework client; + private final CreateModable<ACLBackgroundPathAndBytesable<String>> createMethod; + private final AtomicReference<String> nodePath = new AtomicReference<String>(null); + private final String basePath; + private final CreateMode mode; + private final AtomicReference<byte[]> data = new AtomicReference<byte[]>(); + private final AtomicReference<State> state = new AtomicReference<State>(State.LATENT); + private final AtomicBoolean authFailure = new AtomicBoolean(false); + private final BackgroundCallback backgroundCallback; + private final boolean useProtection; + private final CuratorWatcher watcher = new CuratorWatcher() + { + @Override + public void process(WatchedEvent event) throws Exception + { + if ( event.getType() == EventType.NodeDeleted ) + { + createNode(); + } + else if ( event.getType() == EventType.NodeDataChanged ) + { + watchNode(); + } + } + }; + private final BackgroundCallback checkExistsCallback = new BackgroundCallback() + { + @Override + public void processResult(CuratorFramework client, CuratorEvent event) throws Exception + { + if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() ) + { + createNode(); + } + else + { + boolean isEphemeral = event.getStat().getEphemeralOwner() != 0; + if ( isEphemeral != mode.isEphemeral() ) + { + log.warn("Existing node ephemeral state doesn't match requested state. Maybe the node was created outside of PersistentNode? " + basePath); + } + } + } + }; + private final BackgroundCallback setDataCallback = new BackgroundCallback() + { + + @Override + public void processResult(CuratorFramework client, CuratorEvent event) + throws Exception + { + //If the result is ok then initialisation is complete (if we're still initialising) + //Don't retry on other errors as the only recoverable cases will be connection loss + //and the node not existing, both of which are already handled by other watches. + if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) + { + //Update is ok, mark initialisation as complete if required. + initialisationComplete(); + } + } + }; + private final ConnectionStateListener connectionStateListener = new ConnectionStateListener() + { + @Override + public void stateChanged(CuratorFramework client, ConnectionState newState) + { + if ( newState == ConnectionState.RECONNECTED ) + { + createNode(); + } + } + }; + + private enum State + { + LATENT, + STARTED, + CLOSED + } + + /** + * @param client client instance + * @param mode creation mode + * @param useProtection if true, call {@link CreateBuilder#withProtection()} + * @param basePath the base path for the node + * @param initData data for the node + */ + public PersistentNode(CuratorFramework client, final CreateMode mode, boolean useProtection, final String basePath, byte[] initData) + { + this.useProtection = useProtection; + this.client = Preconditions.checkNotNull(client, "client cannot be null"); + this.basePath = PathUtils.validatePath(basePath); + this.mode = Preconditions.checkNotNull(mode, "mode cannot be null"); + final byte[] data = Preconditions.checkNotNull(initData, "data cannot be null"); + + backgroundCallback = new BackgroundCallback() + { + @Override + public void processResult(CuratorFramework client, CuratorEvent event) throws Exception + { + String path = null; + boolean nodeExists = false; + if ( event.getResultCode() == KeeperException.Code.NODEEXISTS.intValue() ) + { + path = event.getPath(); + nodeExists = true; + } + else if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) + { + path = event.getName(); + } + else if ( event.getResultCode() == KeeperException.Code.NOAUTH.intValue() ) + { + log.warn("Client does not have authorisation to write ephemeral node at path {}", event.getPath()); + authFailure.set(true); + return; + } + if ( path != null ) + { + authFailure.set(false); + nodePath.set(path); + watchNode(); + + if ( nodeExists ) + { + client.setData().inBackground(setDataCallback).forPath(getActualPath(), getData()); + } + else + { + initialisationComplete(); + } + } + else + { + createNode(); + } + } + }; + + createMethod = useProtection ? client.create().creatingParentContainersIfNeeded().withProtection() : client.create().creatingParentContainersIfNeeded(); + this.data.set(Arrays.copyOf(data, data.length)); + } + + private void initialisationComplete() + { + CountDownLatch localLatch = initialCreateLatch.getAndSet(null); + if ( localLatch != null ) + { + localLatch.countDown(); + } + } + + /** + * You must call start() to initiate the persistent ephemeral node. An attempt to create the node + * in the background will be started + */ + public void start() + { + Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Already started"); + + client.getConnectionStateListenable().addListener(connectionStateListener); + createNode(); + } + + /** + * Block until the either initial node creation initiated by {@link #start()} succeeds or + * the timeout elapses. + * + * @param timeout the maximum time to wait + * @param unit time unit + * @return if the node was created before timeout + * @throws InterruptedException if the thread is interrupted + */ + public boolean waitForInitialCreate(long timeout, TimeUnit unit) throws InterruptedException + { + Preconditions.checkState(state.get() == State.STARTED, "Not started"); + + CountDownLatch localLatch = initialCreateLatch.get(); + return (localLatch == null) || localLatch.await(timeout, unit); + } + + @Override + public void close() throws IOException + { + if ( !state.compareAndSet(State.STARTED, State.CLOSED) ) + { + return; + } + + client.getConnectionStateListenable().removeListener(connectionStateListener); + + try + { + deleteNode(); + } + catch ( Exception e ) + { + throw new IOException(e); + } + } + + /** + * Returns the currently set path or null if the node does not exist + * + * @return node path or null + */ + public String getActualPath() + { + return nodePath.get(); + } + + /** + * Set data that ephemeral node should set in ZK also writes the data to the node + * + * @param data new data value + * @throws Exception errors + */ + public void setData(byte[] data) throws Exception + { + data = Preconditions.checkNotNull(data, "data cannot be null"); + this.data.set(Arrays.copyOf(data, data.length)); + if ( isActive() ) + { + client.setData().inBackground().forPath(getActualPath(), getData()); + } + } + + /** + * Return the current value of our data + * + * @return our data + */ + public byte[] getData() + { + return this.data.get(); + } + + private void deleteNode() throws Exception + { + String localNodePath = nodePath.getAndSet(null); + if ( localNodePath != null ) + { + try + { + client.delete().guaranteed().forPath(localNodePath); + } + catch ( KeeperException.NoNodeException ignore ) + { + // ignore + } + } + } + + private void createNode() + { + if ( !isActive() ) + { + return; + } + + try + { + String existingPath = nodePath.get(); + String createPath = (existingPath != null && !useProtection) ? existingPath : basePath; + createMethod.withMode(getCreateMode(existingPath != null)).inBackground(backgroundCallback).forPath(createPath, data.get()); + } + catch ( Exception e ) + { + throw new RuntimeException("Creating node. BasePath: " + basePath, e); // should never happen unless there's a programming error - so throw RuntimeException + } + } + + private CreateMode getCreateMode(boolean pathIsSet) + { + if ( pathIsSet ) + { + switch ( mode ) + { + default: + { + break; + } + + case EPHEMERAL_SEQUENTIAL: + { + return CreateMode.EPHEMERAL; // protection case - node already set + } + + case PERSISTENT_SEQUENTIAL: + { + return CreateMode.PERSISTENT; // protection case - node already set + } + } + } + return mode; + } + + private void watchNode() throws Exception + { + if ( !isActive() ) + { + return; + } + + String localNodePath = nodePath.get(); + if ( localNodePath != null ) + { + client.checkExists().usingWatcher(watcher).inBackground(checkExistsCallback).forPath(localNodePath); + } + } + + private boolean isActive() + { + return (state.get() == State.STARTED); + } + + @VisibleForTesting + boolean isAuthFailure() + { + return authFailure.get(); + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/fefbba1c/curator-recipes/src/site/confluence/group-member.confluence ---------------------------------------------------------------------- diff --git a/curator-recipes/src/site/confluence/group-member.confluence b/curator-recipes/src/site/confluence/group-member.confluence index a370675..5c24c75 100644 --- a/curator-recipes/src/site/confluence/group-member.confluence +++ b/curator-recipes/src/site/confluence/group-member.confluence @@ -5,7 +5,7 @@ Group membership management. Adds this instance into a group and keeps a cache o h2. Participating Classes * GroupMember -* PersistentEphemeralNode +* PersistentNode * PathChildrenCache h2. Usage http://git-wip-us.apache.org/repos/asf/curator/blob/fefbba1c/curator-recipes/src/site/confluence/persistent-ephemeral-node.confluence ---------------------------------------------------------------------- diff --git a/curator-recipes/src/site/confluence/persistent-ephemeral-node.confluence b/curator-recipes/src/site/confluence/persistent-ephemeral-node.confluence index 0023f57..aeb9e10 100644 --- a/curator-recipes/src/site/confluence/persistent-ephemeral-node.confluence +++ b/curator-recipes/src/site/confluence/persistent-ephemeral-node.confluence @@ -1,32 +1,34 @@ h1. Persistent Ephemeral Node h2. Description -A persistent ephemeral node is an ephemeral node that attempts to stay present in ZooKeeper, even through connection and session interruptions. +A persistent node is a node that attempts to stay present in ZooKeeper, even through connection and session interruptions. h2. Participating Classes -* PersistentEphemeralNode +* PersistentNode h2. Usage -h3. Creating a PersistentEphemeralNode +h3. Creating a PersistentNode {code} -public PersistentEphemeralNode(CuratorFramework client, - PersistentEphemeralNode.Mode mode, +public PersistentNode(CuratorFramework client, + CreateMode mode, + boolean useProtection, String basePath, byte[] data) Parameters: client - client instance -mode - creation/protection mode +mode - creation mode +useProtection - if true, call CreateBuilder.withProtection() basePath - the base path for the node data - data for the node {code} h3. General Usage -PersistentEphemeralNodes must be started: +PersistentNodes must be started: {code} node.start(); {code} -When you are through with the PersistentEphemeralNode instance, you should call close: +When you are through with the PersistentNode instance, you should call close: {code} node.close(); {code} @@ -34,4 +36,4 @@ node.close(); NOTE: this will delete the node h2. Error Handling -PersistentEphemeralNode instances internally handle all error states recreating the node as necessary. +PersistentNode instances internally handle all error states recreating the node as necessary. http://git-wip-us.apache.org/repos/asf/curator/blob/fefbba1c/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNode.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNode.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNode.java index 84eaa52..f451feb 100644 --- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNode.java +++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNode.java @@ -20,7 +20,6 @@ package org.apache.curator.framework.recipes.nodes; import com.google.common.base.Throwables; import com.google.common.collect.Lists; - import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; @@ -30,21 +29,19 @@ import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.KillSession; -import org.apache.curator.test.TestingServer; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.Watcher.Event.EventType; +import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; - import java.io.IOException; import java.util.Arrays; import java.util.Collection; @@ -55,6 +52,7 @@ import java.util.concurrent.TimeUnit; import static org.testng.Assert.*; +@SuppressWarnings("deprecation") public class TestPersistentEphemeralNode extends BaseClassForTests { private static final String DIR = "/test"; http://git-wip-us.apache.org/repos/asf/curator/blob/fefbba1c/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNodeListener.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNodeListener.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNodeListener.java index ceff4c5..6771eec 100644 --- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNodeListener.java +++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentEphemeralNodeListener.java @@ -34,6 +34,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +@SuppressWarnings("deprecation") public class TestPersistentEphemeralNodeListener extends BaseClassForTests { @Test http://git-wip-us.apache.org/repos/asf/curator/blob/fefbba1c/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentNode.java ---------------------------------------------------------------------- diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentNode.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentNode.java new file mode 100644 index 0000000..c006dd7 --- /dev/null +++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentNode.java @@ -0,0 +1,62 @@ +/** + * 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.recipes.nodes; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.RetryOneTime; +import org.apache.curator.test.BaseClassForTests; +import org.apache.curator.test.Timing; +import org.apache.curator.utils.CloseableUtils; +import org.apache.zookeeper.CreateMode; +import org.testng.Assert; +import org.testng.annotations.Test; +import java.util.concurrent.TimeUnit; + +public class TestPersistentNode extends BaseClassForTests +{ + @Test + public void testBasic() throws Exception + { + final byte[] TEST_DATA = "hey".getBytes(); + + Timing timing = new Timing(); + PersistentNode pen = null; + CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); + try + { + client.start(); + pen = new PersistentNode(client, CreateMode.PERSISTENT, false, "/test", TEST_DATA); + pen.start(); + Assert.assertTrue(pen.waitForInitialCreate(timing.milliseconds(), TimeUnit.MILLISECONDS)); + client.close(); // cause session to end - force checks that node is persistent + + client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); + client.start(); + + byte[] bytes = client.getData().forPath("/test"); + Assert.assertEquals(bytes, TEST_DATA); + } + finally + { + CloseableUtils.closeQuietly(pen); + CloseableUtils.closeQuietly(client); + } + } +}
