Return-Path: X-Original-To: apmail-curator-dev-archive@minotaur.apache.org Delivered-To: apmail-curator-dev-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 3DFD918E22 for ; Mon, 18 Jan 2016 22:23:40 +0000 (UTC) Received: (qmail 28032 invoked by uid 500); 18 Jan 2016 22:23:40 -0000 Delivered-To: apmail-curator-dev-archive@curator.apache.org Received: (qmail 27976 invoked by uid 500); 18 Jan 2016 22:23:40 -0000 Mailing-List: contact dev-help@curator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@curator.apache.org Delivered-To: mailing list dev@curator.apache.org Received: (qmail 27958 invoked by uid 99); 18 Jan 2016 22:23:40 -0000 Received: from arcas.apache.org (HELO arcas) (140.211.11.28) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 18 Jan 2016 22:23:40 +0000 Received: from arcas.apache.org (localhost [127.0.0.1]) by arcas (Postfix) with ESMTP id EA7ED2C0453 for ; Mon, 18 Jan 2016 22:23:39 +0000 (UTC) Date: Mon, 18 Jan 2016 22:23:39 +0000 (UTC) From: "ASF GitHub Bot (JIRA)" To: dev@curator.apache.org Message-ID: In-Reply-To: References: Subject: [jira] [Commented] (CURATOR-287) PersistentEphemeralNode should be generalized to accept all create modes MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit X-JIRA-FingerPrint: 30527f35849b9dde25b450d4833f0394 [ https://issues.apache.org/jira/browse/CURATOR-287?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15105890#comment-15105890 ] ASF GitHub Bot commented on CURATOR-287: ---------------------------------------- Github user cammckenzie commented on a diff in the pull request: https://github.com/apache/curator/pull/123#discussion_r50050583 --- Diff: 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; + +/** + *

+ * A persistent node is a node that attempts to stay present in + * ZooKeeper, even through connection and session interruptions. + *

+ *

+ * Thanks to bbeck (https://github.com/bbeck) for the initial coding and design + *

+ */ +public class PersistentNode implements Closeable +{ + private final AtomicReference initialCreateLatch = new AtomicReference(new CountDownLatch(1)); + private final Logger log = LoggerFactory.getLogger(getClass()); + private final CuratorFramework client; + private final CreateModable> createMethod; + private final AtomicReference nodePath = new AtomicReference(null); + private final String basePath; + private final CreateMode mode; + private final AtomicReference data = new AtomicReference(); + private final AtomicReference state = new AtomicReference(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()); --- End diff -- This log message refers to ephemeral which is no longer necessarily the case. > PersistentEphemeralNode should be generalized to accept all create modes > ------------------------------------------------------------------------ > > Key: CURATOR-287 > URL: https://issues.apache.org/jira/browse/CURATOR-287 > Project: Apache Curator > Issue Type: New Feature > Components: Recipes > Affects Versions: 3.0.0, 2.9.1 > Reporter: Jordan Zimmerman > Assignee: Jordan Zimmerman > Priority: Minor > > With very little change, PersistentEphemeralNode could work with non-ephemeral nodes. There is a good use case for this: permanent nodes that must always exist with some data. It's actually a pain to do this manually with ZK. -- This message was sent by Atlassian JIRA (v6.3.4#6332)