curator-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From randg...@apache.org
Subject [1/5] curator git commit: Deprecated PersistentEphemeralNode in favor of PersistentNode which is the same code but now accepts any createmode
Date Tue, 19 Jan 2016 02:05:51 GMT
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 <randgalt@apache.org>
Authored: Tue Jan 12 11:45:38 2016 -0500
Committer: randgalt <randgalt@apache.org>
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);
+        }
+    }
+}


Mime
View raw message