zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cami...@apache.org
Subject zookeeper git commit: ZOOKEEPER-2719: Pull changes for TTL Nodes into 3.5 branch
Date Fri, 17 Mar 2017 15:04:45 GMT
Repository: zookeeper
Updated Branches:
  refs/heads/branch-3.5 5c214b94e -> abad02e42


ZOOKEEPER-2719: Pull changes for TTL Nodes into 3.5 branch

This PR cherry-picks changes for TTL nodes (ZOOKEEPER-2169) and the CLI for it (ZOOKEEPER-2608) into branch-3.5.

Author: randgalt <jordan@jordanzimmerman.com>

Reviewers: Michael Han <hanm@apache.org>, Camille Fournier <camille@apache.org>

Closes #192 from Randgalt/ZOOKEEPER-2719 and squashes the following commits:

6343001 [randgalt] Added ttl option to CLI create command
39b969b [randgalt] Removed bogus import
64f77a5 [randgalt] cleaned up some doc merge issues
66fdccb [randgalt] This patch takes advantage of 3.5's container support. Most of the work needed to support TTLs is there already. In order not to break on-disk and protocol compatibility the ephemeralOwner is yet-again overloaded to have special meaning. New opcodes and transaction records had to be added in a similar manner to Containers


Project: http://git-wip-us.apache.org/repos/asf/zookeeper/repo
Commit: http://git-wip-us.apache.org/repos/asf/zookeeper/commit/abad02e4
Tree: http://git-wip-us.apache.org/repos/asf/zookeeper/tree/abad02e4
Diff: http://git-wip-us.apache.org/repos/asf/zookeeper/diff/abad02e4

Branch: refs/heads/branch-3.5
Commit: abad02e42867e96dbf68a484d2cf0f37137bd246
Parents: 5c214b9
Author: randgalt <jordan@jordanzimmerman.com>
Authored: Fri Mar 17 11:04:39 2017 -0400
Committer: Camille Fournier <camille@apache.org>
Committed: Fri Mar 17 11:04:39 2017 -0400

----------------------------------------------------------------------
 docs/zookeeperAdmin.html                        |   2 +-
 docs/zookeeperProgrammers.html                  |  22 +-
 .../content/xdocs/zookeeperAdmin.xml            |   2 +-
 .../content/xdocs/zookeeperProgrammers.xml      |  21 +-
 .../main/org/apache/zookeeper/CreateMode.java   |  41 +++-
 .../zookeeper/MultiTransactionRecord.java       |   6 +
 src/java/main/org/apache/zookeeper/Op.java      | 103 ++++++++-
 src/java/main/org/apache/zookeeper/ZooDefs.java |   2 +
 .../main/org/apache/zookeeper/ZooKeeper.java    | 108 +++++++---
 .../org/apache/zookeeper/cli/CreateCommand.java |  37 +++-
 .../zookeeper/server/ContainerManager.java      |  18 ++
 .../org/apache/zookeeper/server/DataNode.java   |   7 +-
 .../org/apache/zookeeper/server/DataTree.java   |  46 +++-
 .../apache/zookeeper/server/EphemeralType.java  |  76 +++++++
 .../zookeeper/server/FinalRequestProcessor.java |   2 +
 .../zookeeper/server/PrepRequestProcessor.java  | 125 +++++++----
 .../org/apache/zookeeper/server/Request.java    |   4 +
 .../apache/zookeeper/server/TraceFormatter.java |   2 +
 .../server/quorum/CommitProcessor.java          |   1 +
 .../server/quorum/FollowerRequestProcessor.java |   1 +
 .../server/quorum/ObserverRequestProcessor.java |   1 +
 .../server/quorum/ReadOnlyRequestProcessor.java |   1 +
 .../zookeeper/server/util/SerializeUtils.java   |   4 +
 .../apache/zookeeper/server/CreateTTLTest.java  | 213 +++++++++++++++++++
 .../zookeeper/server/EphemeralTypeTest.java     |  58 +++++
 src/zookeeper.jute                              |  14 ++
 26 files changed, 808 insertions(+), 109 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/docs/zookeeperAdmin.html
----------------------------------------------------------------------
diff --git a/docs/zookeeperAdmin.html b/docs/zookeeperAdmin.html
index 195c1ca..7a109c9 100644
--- a/docs/zookeeperAdmin.html
+++ b/docs/zookeeperAdmin.html
@@ -1830,7 +1830,7 @@ server.3=zoo3:2888:3888</pre>
 <p>
 <strong>New in 3.5.1:</strong> The
                 time interval in milliseconds for each check of candidate container
-                nodes. Default is "60000".</p>
+                and ttl nodes. Default is "60000".</p>
 </dd>
 
           

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/docs/zookeeperProgrammers.html
----------------------------------------------------------------------
diff --git a/docs/zookeeperProgrammers.html b/docs/zookeeperProgrammers.html
index d66fc99..324ec5d 100644
--- a/docs/zookeeperProgrammers.html
+++ b/docs/zookeeperProgrammers.html
@@ -219,6 +219,9 @@ document.write("Last Published: " + document.lastModified);
 <li>
 <a href="#Container+Nodes">Container Nodes</a>
 </li>
+<li>
+<a href="#TTL+Nodes">TTL Nodes</a>
+</li>
 </ul>
 </li>
 <li>
@@ -573,17 +576,26 @@ document.write("Last Published: " + document.lastModified);
 <a name="Container+Nodes"></a>
 <h4>Container Nodes</h4>
 <p>
-<strong>Added in 3.6.0</strong>
+<strong>Added in 3.5.3</strong>
 </p>
-<p>ZooKeeper has the notion of container nodes. Container nodes are
-          special purpose nodes useful for recipes such as leader, lock, etc.
+<p>ZooKeeper has the notion of container znodes. Container znodes are
+          special purpose znodes useful for recipes such as leader, lock, etc.
           When the last child of a container is deleted, the container becomes
           a candidate to be deleted by the server at some point in the future.</p>
 <p>Given this property, you should be prepared to get
           KeeperException.NoNodeException when creating children inside of
-          container nodes. i.e. when creating child nodes inside of container nodes
+          container znodes. i.e. when creating child znodes inside of container znodes
           always check for KeeperException.NoNodeException and recreate the container
-          node when it occurs.</p>
+          znode when it occurs.</p>
+<a name="TTL+Nodes"></a>
+<h4>TTL Nodes</h4>
+<p>
+<strong>Added in 3.5.3</strong>
+</p>
+<p>When creating PERSISTENT or PERSISTENT_SEQUENTIAL znodes,
+          you can optionally set a TTL in milliseconds for the znode. If the znode
+          is not modified within the TTL and has no children it will become a candidate
+          to be deleted by the server at some point in the future.</p>
 <a name="sc_timeInZk"></a>
 <h3 class="h4">Time in ZooKeeper</h3>
 <p>ZooKeeper tracks time multiple ways:</p>

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
----------------------------------------------------------------------
diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
index 45cd29e..99407a9 100644
--- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
+++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
@@ -1510,7 +1510,7 @@ server.3=zoo3:2888:3888</programlisting>
 
               <para><emphasis role="bold">New in 3.5.1:</emphasis> The
                 time interval in milliseconds for each check of candidate container
-                nodes. Default is "60000".</para>
+                and ttl nodes. Default is "60000".</para>
             </listitem>
           </varlistentry>
 

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
----------------------------------------------------------------------
diff --git a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
index 8710362..12ea60f 100644
--- a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
+++ b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
@@ -247,18 +247,29 @@
       <section>
         <title>Container Nodes</title>
 
-        <para><emphasis role="bold">Added in 3.6.0</emphasis></para>
+        <para><emphasis role="bold">Added in 3.5.3</emphasis></para>
 
-        <para>ZooKeeper has the notion of container nodes. Container nodes are
-          special purpose nodes useful for recipes such as leader, lock, etc.
+        <para>ZooKeeper has the notion of container znodes. Container znodes are
+          special purpose znodes useful for recipes such as leader, lock, etc.
           When the last child of a container is deleted, the container becomes
           a candidate to be deleted by the server at some point in the future.</para>
 
         <para>Given this property, you should be prepared to get
           KeeperException.NoNodeException when creating children inside of
-          container nodes. i.e. when creating child nodes inside of container nodes
+          container znodes. i.e. when creating child znodes inside of container znodes
           always check for KeeperException.NoNodeException and recreate the container
-          node when it occurs.</para>
+          znode when it occurs.</para>
+      </section>
+
+      <section>
+        <title>TTL Nodes</title>
+
+        <para><emphasis role="bold">Added in 3.5.3</emphasis></para>
+
+        <para>When creating PERSISTENT or PERSISTENT_SEQUENTIAL znodes,
+          you can optionally set a TTL in milliseconds for the znode. If the znode
+          is not modified within the TTL and has no children it will become a candidate
+          to be deleted by the server at some point in the future.</para>
       </section>
     </section>
 

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/CreateMode.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/CreateMode.java b/src/java/main/org/apache/zookeeper/CreateMode.java
index a5c1bb1..35a5730 100644
--- a/src/java/main/org/apache/zookeeper/CreateMode.java
+++ b/src/java/main/org/apache/zookeeper/CreateMode.java
@@ -28,21 +28,21 @@ public enum CreateMode {
     /**
      * The znode will not be automatically deleted upon client's disconnect.
      */
-    PERSISTENT (0, false, false, false),
+    PERSISTENT (0, false, false, false, false),
     /**
     * The znode will not be automatically deleted upon client's disconnect,
     * and its name will be appended with a monotonically increasing number.
     */
-    PERSISTENT_SEQUENTIAL (2, false, true, false),
+    PERSISTENT_SEQUENTIAL (2, false, true, false, false),
     /**
      * The znode will be deleted upon the client's disconnect.
      */
-    EPHEMERAL (1, true, false, false),
+    EPHEMERAL (1, true, false, false, false),
     /**
      * The znode will be deleted upon the client's disconnect, and its name
      * will be appended with a monotonically increasing number.
      */
-    EPHEMERAL_SEQUENTIAL (3, true, true, false),
+    EPHEMERAL_SEQUENTIAL (3, true, true, false, false),
     /**
      * The znode will be a container node. Container
      * nodes are special purpose nodes useful for recipes such as leader, lock,
@@ -52,7 +52,20 @@ public enum CreateMode {
      * {@link org.apache.zookeeper.KeeperException.NoNodeException}
      * when creating children inside of this container node.
      */
-    CONTAINER (4, false, false, true);
+    CONTAINER (4, false, false, true, false),
+    /**
+     * The znode will not be automatically deleted upon client's disconnect.
+     * However if the znode has not been modified within the given TTL, it
+     * will be deleted once it has no children.
+     */
+    PERSISTENT_WITH_TTL(5, false, false, false, true),
+    /**
+     * The znode will not be automatically deleted upon client's disconnect,
+     * and its name will be appended with a monotonically increasing number.
+     * However if the znode has not been modified within the given TTL, it
+     * will be deleted once it has no children.
+     */
+    PERSISTENT_SEQUENTIAL_WITH_TTL(6, false, true, false, true);
 
     private static final Logger LOG = LoggerFactory.getLogger(CreateMode.class);
 
@@ -60,13 +73,15 @@ public enum CreateMode {
     private boolean sequential;
     private final boolean isContainer;
     private int flag;
+    private boolean isTTL;
 
     CreateMode(int flag, boolean ephemeral, boolean sequential,
-               boolean isContainer) {
+               boolean isContainer, boolean isTTL) {
         this.flag = flag;
         this.ephemeral = ephemeral;
         this.sequential = sequential;
         this.isContainer = isContainer;
+        this.isTTL = isTTL;
     }
 
     public boolean isEphemeral() { 
@@ -81,6 +96,10 @@ public enum CreateMode {
         return isContainer;
     }
 
+    public boolean isTTL() {
+        return isTTL;
+    }
+
     public int toFlag() {
         return flag;
     }
@@ -100,6 +119,10 @@ public enum CreateMode {
 
         case 4: return CreateMode.CONTAINER;
 
+        case 5: return CreateMode.PERSISTENT_WITH_TTL;
+
+        case 6: return CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL;
+
         default:
             String errMsg = "Received an invalid flag value: " + flag
                     + " to convert to a CreateMode";
@@ -128,6 +151,12 @@ public enum CreateMode {
             case 4:
                 return CreateMode.CONTAINER;
 
+            case 5:
+                return CreateMode.PERSISTENT_WITH_TTL;
+
+            case 6:
+                return CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL;
+
             default:
                 return defaultMode;
         }

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/MultiTransactionRecord.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/MultiTransactionRecord.java b/src/java/main/org/apache/zookeeper/MultiTransactionRecord.java
index fa46b48..336a677 100644
--- a/src/java/main/org/apache/zookeeper/MultiTransactionRecord.java
+++ b/src/java/main/org/apache/zookeeper/MultiTransactionRecord.java
@@ -68,6 +68,7 @@ public class MultiTransactionRecord implements Record, Iterable<Op> {
             switch (op.getType()) {
                 case ZooDefs.OpCode.create:
                 case ZooDefs.OpCode.create2:
+                case ZooDefs.OpCode.createTTL:
                 case ZooDefs.OpCode.createContainer:
                 case ZooDefs.OpCode.delete:
                 case ZooDefs.OpCode.setData:
@@ -97,6 +98,11 @@ public class MultiTransactionRecord implements Record, Iterable<Op> {
                     cr.deserialize(archive, tag);
                     add(Op.create(cr.getPath(), cr.getData(), cr.getAcl(), cr.getFlags()));
                     break;
+                case ZooDefs.OpCode.createTTL:
+                    CreateTTLRequest crTtl = new CreateTTLRequest();
+                    crTtl.deserialize(archive, tag);
+                    add(Op.create(crTtl.getPath(), crTtl.getData(), crTtl.getAcl(), crTtl.getFlags(), crTtl.getTtl()));
+                    break;
                 case ZooDefs.OpCode.delete:
                     DeleteRequest dr = new DeleteRequest();
                     dr.deserialize(archive, tag);

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/Op.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/Op.java b/src/java/main/org/apache/zookeeper/Op.java
index b6c3a95..c73cc79 100644
--- a/src/java/main/org/apache/zookeeper/Op.java
+++ b/src/java/main/org/apache/zookeeper/Op.java
@@ -22,8 +22,10 @@ import org.apache.zookeeper.common.PathUtils;
 import org.apache.zookeeper.data.ACL;
 import org.apache.zookeeper.proto.CheckVersionRequest;
 import org.apache.zookeeper.proto.CreateRequest;
+import org.apache.zookeeper.proto.CreateTTLRequest;
 import org.apache.zookeeper.proto.DeleteRequest;
 import org.apache.zookeeper.proto.SetDataRequest;
+import org.apache.zookeeper.server.EphemeralType;
 
 import java.util.Arrays;
 import java.util.Iterator;
@@ -71,6 +73,32 @@ public abstract class Op {
     }
 
     /**
+     * Constructs a create operation.  Arguments are as for the ZooKeeper method of the same name
+     * but adding an optional ttl
+     * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode)
+     * @see CreateMode#fromFlag(int)
+     *
+     * @param path
+     *                the path for the node
+     * @param data
+     *                the initial data for the node
+     * @param acl
+     *                the acl for the node
+     * @param flags
+     *                specifying whether the node to be created is ephemeral
+     *                and/or sequential but using the integer encoding.
+     * @param ttl
+     *                optional ttl or 0 (flags must imply a TTL creation mode)
+     */
+    public static Op create(String path, byte[] data, List<ACL> acl, int flags, long ttl) {
+        CreateMode createMode = CreateMode.fromFlag(flags, CreateMode.PERSISTENT);
+        if (createMode.isTTL()) {
+            return new CreateTTL(path, data, acl, createMode, ttl);
+        }
+        return new Create(path, data, acl, flags);
+    }
+
+    /**
      * Constructs a create operation.  Arguments are as for the ZooKeeper method of the same name.
      * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode)
      *
@@ -89,6 +117,30 @@ public abstract class Op {
     }
 
     /**
+     * Constructs a create operation.  Arguments are as for the ZooKeeper method of the same name
+     * but adding an optional ttl
+     * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode)
+     *
+     * @param path
+     *                the path for the node
+     * @param data
+     *                the initial data for the node
+     * @param acl
+     *                the acl for the node
+     * @param createMode
+     *                specifying whether the node to be created is ephemeral
+     *                and/or sequential
+     * @param ttl
+     *                optional ttl or 0 (createMode must imply a TTL)
+     */
+    public static Op create(String path, byte[] data, List<ACL> acl, CreateMode createMode, long ttl) {
+        if (createMode.isTTL()) {
+            return new CreateTTL(path, data, acl, createMode, ttl);
+        }
+        return new Create(path, data, acl, createMode);
+    }
+
+    /**
      * Constructs a delete operation.  Arguments are as for the ZooKeeper method of the same name.
      * @see ZooKeeper#delete(String, int)
      *
@@ -178,9 +230,9 @@ public abstract class Op {
     // these internal classes are public, but should not generally be referenced.
     //
     public static class Create extends Op {
-        private byte[] data;
-        private List<ACL> acl;
-        private int flags;
+        protected byte[] data;
+        protected List<ACL> acl;
+        protected int flags;
 
         private Create(String path, byte[] data, List<ACL> acl, int flags) {
             super(getOpcode(CreateMode.fromFlag(flags, CreateMode.PERSISTENT)), path);
@@ -190,6 +242,9 @@ public abstract class Op {
         }
 
         private static int getOpcode(CreateMode createMode) {
+            if (createMode.isTTL()) {
+                return ZooDefs.OpCode.createTTL;
+            }
             return createMode.isContainer() ? ZooDefs.OpCode.createContainer : ZooDefs.OpCode.create;
         }
 
@@ -243,6 +298,48 @@ public abstract class Op {
         void validate() throws KeeperException {
             CreateMode createMode = CreateMode.fromFlag(flags);
             PathUtils.validatePath(getPath(), createMode.isSequential());
+            EphemeralType.validateTTL(createMode, -1);
+        }
+    }
+
+    public static class CreateTTL extends Create {
+        private final long ttl;
+
+        private CreateTTL(String path, byte[] data, List<ACL> acl, int flags, long ttl) {
+            super(path, data, acl, flags);
+            this.ttl = ttl;
+        }
+
+        private CreateTTL(String path, byte[] data, List<ACL> acl, CreateMode createMode, long ttl) {
+            super(path, data, acl, createMode);
+            this.ttl = ttl;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return super.equals(o) && (o instanceof CreateTTL) && (ttl == ((CreateTTL)o).ttl);
+        }
+
+        @Override
+        public int hashCode() {
+            return super.hashCode() + (int)(ttl ^ (ttl >>> 32));
+        }
+
+        @Override
+        public Record toRequestRecord() {
+            return new CreateTTLRequest(getPath(), data, acl, flags, ttl);
+        }
+
+        @Override
+        Op withChroot(String path) {
+            return new CreateTTL(path, data, acl, flags, ttl);
+        }
+
+        @Override
+        void validate() throws KeeperException {
+            CreateMode createMode = CreateMode.fromFlag(flags);
+            PathUtils.validatePath(getPath(), createMode.isSequential());
+            EphemeralType.validateTTL(createMode, ttl);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/ZooDefs.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/ZooDefs.java b/src/java/main/org/apache/zookeeper/ZooDefs.java
index 021d421..cd26c73 100644
--- a/src/java/main/org/apache/zookeeper/ZooDefs.java
+++ b/src/java/main/org/apache/zookeeper/ZooDefs.java
@@ -69,6 +69,8 @@ public class ZooDefs {
 
         public final int deleteContainer = 20;
 
+        public final int createTTL = 21;
+
         public final int auth = 100;
 
         public final int setWatches = 101;

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/ZooKeeper.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java
index b8c8008..fd3323d 100644
--- a/src/java/main/org/apache/zookeeper/ZooKeeper.java
+++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java
@@ -18,18 +18,6 @@
 
 package org.apache.zookeeper;
 
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 import org.apache.jute.Record;
 import org.apache.zookeeper.AsyncCallback.ACLCallback;
 import org.apache.zookeeper.AsyncCallback.Children2Callback;
@@ -45,10 +33,10 @@ import org.apache.zookeeper.KeeperException.NoWatcherException;
 import org.apache.zookeeper.OpResult.ErrorResult;
 import org.apache.zookeeper.Watcher.Event.EventType;
 import org.apache.zookeeper.Watcher.WatcherType;
-import org.apache.zookeeper.client.ZKClientConfig;
 import org.apache.zookeeper.client.ConnectStringParser;
 import org.apache.zookeeper.client.HostProvider;
 import org.apache.zookeeper.client.StaticHostProvider;
+import org.apache.zookeeper.client.ZKClientConfig;
 import org.apache.zookeeper.client.ZooKeeperSaslClient;
 import org.apache.zookeeper.common.PathUtils;
 import org.apache.zookeeper.common.StringUtils;
@@ -58,6 +46,7 @@ import org.apache.zookeeper.proto.CheckWatchesRequest;
 import org.apache.zookeeper.proto.Create2Response;
 import org.apache.zookeeper.proto.CreateRequest;
 import org.apache.zookeeper.proto.CreateResponse;
+import org.apache.zookeeper.proto.CreateTTLRequest;
 import org.apache.zookeeper.proto.DeleteRequest;
 import org.apache.zookeeper.proto.ExistsRequest;
 import org.apache.zookeeper.proto.GetACLRequest;
@@ -79,9 +68,22 @@ import org.apache.zookeeper.proto.SetDataResponse;
 import org.apache.zookeeper.proto.SyncRequest;
 import org.apache.zookeeper.proto.SyncResponse;
 import org.apache.zookeeper.server.DataTree;
+import org.apache.zookeeper.server.EphemeralType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * This is the main class of ZooKeeper client library. To use a ZooKeeper
  * service, an application must first instantiate an object of ZooKeeper class.
@@ -1404,6 +1406,7 @@ public class ZooKeeper implements AutoCloseable {
     {
         final String clientPath = path;
         PathUtils.validatePath(clientPath, createMode.isSequential());
+        EphemeralType.validateTTL(createMode, -1);
 
         final String serverPath = prependChroot(clientPath);
 
@@ -1490,23 +1493,34 @@ public class ZooKeeper implements AutoCloseable {
     public String create(final String path, byte data[], List<ACL> acl,
             CreateMode createMode, Stat stat)
             throws KeeperException, InterruptedException {
+        return create(path, data, acl, createMode, stat, -1);
+    }
+
+    /**
+     * same as {@link #create(String, byte[], List, CreateMode, Stat)} but
+     * allows for specifying a TTL when mode is {@link CreateMode#PERSISTENT_WITH_TTL}
+     * or {@link CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If the znode has not been modified
+     * within the given TTL, it will be deleted once it has no children. The TTL unit is
+     * milliseconds and must be greater than 0 and less than or equal to
+     * {@link EphemeralType#MAX_TTL}.
+     */
+    public String create(final String path, byte data[], List<ACL> acl,
+            CreateMode createMode, Stat stat, long ttl)
+            throws KeeperException, InterruptedException {
         final String clientPath = path;
         PathUtils.validatePath(clientPath, createMode.isSequential());
+        EphemeralType.validateTTL(createMode, ttl);
 
         final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
-        h.setType(createMode.isContainer() ? ZooDefs.OpCode.createContainer : ZooDefs.OpCode.create2);
-        CreateRequest request = new CreateRequest();
+        setCreateHeader(createMode, h);
         Create2Response response = new Create2Response();
-        request.setData(data);
-        request.setFlags(createMode.toFlag());
-        request.setPath(serverPath);
         if (acl != null && acl.size() == 0) {
             throw new KeeperException.InvalidACLException();
         }
-        request.setAcl(acl);
-        ReplyHeader r = cnxn.submitRequest(h, request, response, null);
+        Record record = makeCreateRecord(createMode, serverPath, data, acl, ttl);
+        ReplyHeader r = cnxn.submitRequest(h, record, response, null);
         if (r.getErr() != 0) {
             throw KeeperException.create(KeeperException.Code.get(r.getErr()),
                     clientPath);
@@ -1521,6 +1535,35 @@ public class ZooKeeper implements AutoCloseable {
         }
     }
 
+    private void setCreateHeader(CreateMode createMode, RequestHeader h) {
+        if (createMode.isTTL()) {
+            h.setType(ZooDefs.OpCode.createTTL);
+        } else {
+            h.setType(createMode.isContainer() ? ZooDefs.OpCode.createContainer : ZooDefs.OpCode.create2);
+        }
+    }
+
+    private Record makeCreateRecord(CreateMode createMode, String serverPath, byte[] data, List<ACL> acl, long ttl) {
+        Record record;
+        if (createMode.isTTL()) {
+            CreateTTLRequest request = new CreateTTLRequest();
+            request.setData(data);
+            request.setFlags(createMode.toFlag());
+            request.setPath(serverPath);
+            request.setAcl(acl);
+            request.setTtl(ttl);
+            record = request;
+        } else {
+            CreateRequest request = new CreateRequest();
+            request.setData(data);
+            request.setFlags(createMode.toFlag());
+            request.setPath(serverPath);
+            request.setAcl(acl);
+            record = request;
+        }
+        return record;
+    }
+
     /**
      * The asynchronous version of create.
      *
@@ -1531,6 +1574,7 @@ public class ZooKeeper implements AutoCloseable {
     {
         final String clientPath = path;
         PathUtils.validatePath(clientPath, createMode.isSequential());
+        EphemeralType.validateTTL(createMode, -1);
 
         final String serverPath = prependChroot(clientPath);
 
@@ -1555,21 +1599,29 @@ public class ZooKeeper implements AutoCloseable {
     public void create(final String path, byte data[], List<ACL> acl,
             CreateMode createMode, Create2Callback cb, Object ctx)
     {
+        create(path, data, acl, createMode, cb, ctx, -1);
+    }
+
+    /**
+     * The asynchronous version of create with ttl.
+     *
+     * @see #create(String, byte[], List, CreateMode, Stat, long)
+     */
+    public void create(final String path, byte data[], List<ACL> acl,
+            CreateMode createMode, Create2Callback cb, Object ctx, long ttl)
+    {
         final String clientPath = path;
         PathUtils.validatePath(clientPath, createMode.isSequential());
+        EphemeralType.validateTTL(createMode, ttl);
 
         final String serverPath = prependChroot(clientPath);
 
         RequestHeader h = new RequestHeader();
-        h.setType(createMode.isContainer() ? ZooDefs.OpCode.createContainer : ZooDefs.OpCode.create2);
-        CreateRequest request = new CreateRequest();
-        Create2Response response = new Create2Response();
+        setCreateHeader(createMode, h);
         ReplyHeader r = new ReplyHeader();
-        request.setData(data);
-        request.setFlags(createMode.toFlag());
-        request.setPath(serverPath);
-        request.setAcl(acl);
-        cnxn.queuePacket(h, r, request, response, cb, clientPath,
+        Create2Response response = new Create2Response();
+        Record record = makeCreateRecord(createMode, serverPath, data, acl, ttl);
+        cnxn.queuePacket(h, r, record, response, cb, clientPath,
                 serverPath, ctx, null);
     }
 

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/cli/CreateCommand.java b/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
index 2c37784..3868161 100644
--- a/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
@@ -23,6 +23,8 @@ import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.ZooDefs;
 import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.server.EphemeralType;
 
 /**
  * create command for cli
@@ -33,14 +35,15 @@ public class CreateCommand extends CliCommand {
     private String[] args;
     private CommandLine cl;
 
-    {
+    static {
         options.addOption(new Option("e", false, "ephemeral"));
         options.addOption(new Option("s", false, "sequential"));
         options.addOption(new Option("c", false, "container"));
+        options.addOption(new Option("t", true, "ttl"));
     }
 
     public CreateCommand() {
-        super("create", "[-s] [-e] [-c] path [data] [acl]");
+        super("create", "[-s] [-e] [-c] [-t ttl] path [data] [acl]");
     }
 
 
@@ -62,23 +65,47 @@ public class CreateCommand extends CliCommand {
 
     @Override
     public boolean exec() throws CliException {
-        CreateMode flags = CreateMode.PERSISTENT;
         boolean hasE = cl.hasOption("e");
         boolean hasS = cl.hasOption("s");
         boolean hasC = cl.hasOption("c");
+        boolean hasT = cl.hasOption("t");
         if (hasC && (hasE || hasS)) {
             throw new MalformedCommandException("-c cannot be combined with -s or -e. Containers cannot be ephemeral or sequential.");
         }
+        long ttl;
+        try {
+            ttl = hasT ? Long.parseLong(cl.getOptionValue("t")) : 0;
+        } catch (NumberFormatException e) {
+            throw new MalformedCommandException("-t argument must be a long value");
+        }
 
+        if ( hasT && hasE ) {
+            throw new MalformedCommandException("TTLs cannot be used with Ephemeral znodes");
+        }
+        if ( hasT && hasC ) {
+            throw new MalformedCommandException("TTLs cannot be used with Container znodes");
+        }
+
+        CreateMode flags;
         if(hasE && hasS) {
             flags = CreateMode.EPHEMERAL_SEQUENTIAL;
         } else if (hasE) {
             flags = CreateMode.EPHEMERAL;
         } else if (hasS) {
-            flags = CreateMode.PERSISTENT_SEQUENTIAL;
+            flags = hasT ? CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL : CreateMode.PERSISTENT_SEQUENTIAL;
         } else if (hasC) {
             flags = CreateMode.CONTAINER;
+        } else {
+            flags = hasT ? CreateMode.PERSISTENT_WITH_TTL : CreateMode.PERSISTENT;
+        }
+        if ( hasT ) {
+            try {
+                EphemeralType.ttlToEphemeralOwner(ttl);
+            } catch (IllegalArgumentException e) {
+                throw new MalformedCommandException(e.getMessage());
+            }
         }
+
         String path = args[1];
         byte[] data = null;
         if (args.length > 2) {
@@ -89,7 +116,7 @@ public class CreateCommand extends CliCommand {
             acl = AclParser.parse(args[3]);
         }
         try {
-            String newPath = zk.create(path, data, acl, flags);
+            String newPath = hasT ? zk.create(path, data, acl, flags, new Stat(), ttl) : zk.create(path, data, acl, flags);
             err.println("Created " + newPath);
         } catch(KeeperException.EphemeralOnLocalSessionException e) {
             err.println("Unable to create ephemeral node on a local session");

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/ContainerManager.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/ContainerManager.java b/src/java/main/org/apache/zookeeper/server/ContainerManager.java
index 7eca968..8834f29 100644
--- a/src/java/main/org/apache/zookeeper/server/ContainerManager.java
+++ b/src/java/main/org/apache/zookeeper/server/ContainerManager.java
@@ -153,6 +153,24 @@ public class ContainerManager {
                 candidates.add(containerPath);
             }
         }
+        for (String ttlPath : zkDb.getDataTree().getTtls()) {
+            DataNode node = zkDb.getDataTree().getNode(ttlPath);
+            if (node != null) {
+                Set<String> children = node.getChildren();
+                if ((children == null) || (children.size() == 0)) {
+                    long elapsed = getElapsed(node);
+                    long ttl = EphemeralType.getTTL(node.stat.getEphemeralOwner());
+                    if ((ttl != 0) && (elapsed > ttl)) {
+                        candidates.add(ttlPath);
+                    }
+                }
+            }
+        }
         return candidates;
     }
+
+    // VisibleForTesting
+    protected long getElapsed(DataNode node) {
+        return Time.currentWallTime() - node.stat.getMtime();
+    }
 }

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/DataNode.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/DataNode.java b/src/java/main/org/apache/zookeeper/server/DataNode.java
index 1bad238..0859aab 100644
--- a/src/java/main/org/apache/zookeeper/server/DataNode.java
+++ b/src/java/main/org/apache/zookeeper/server/DataNode.java
@@ -162,8 +162,11 @@ public class DataNode implements Record {
     }
 
     private static long getClientEphemeralOwner(StatPersisted stat) {
-        return (stat.getEphemeralOwner() == DataTree.CONTAINER_EPHEMERAL_OWNER)
-                ? 0 : stat.getEphemeralOwner();
+        EphemeralType ephemeralType = EphemeralType.get(stat.getEphemeralOwner());
+        if (ephemeralType != EphemeralType.NORMAL) {
+            return 0;
+        }
+        return stat.getEphemeralOwner();
     }
 
     synchronized public void deserialize(InputArchive archive, String tag)

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/DataTree.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java
index 7bd3380..f0ab2b3 100644
--- a/src/java/main/org/apache/zookeeper/server/DataTree.java
+++ b/src/java/main/org/apache/zookeeper/server/DataTree.java
@@ -41,6 +41,7 @@ import org.apache.zookeeper.data.Stat;
 import org.apache.zookeeper.data.StatPersisted;
 import org.apache.zookeeper.txn.CheckVersionTxn;
 import org.apache.zookeeper.txn.CreateContainerTxn;
+import org.apache.zookeeper.txn.CreateTTLTxn;
 import org.apache.zookeeper.txn.CreateTxn;
 import org.apache.zookeeper.txn.DeleteTxn;
 import org.apache.zookeeper.txn.ErrorTxn;
@@ -78,8 +79,6 @@ import java.util.concurrent.ConcurrentHashMap;
 public class DataTree {
     private static final Logger LOG = LoggerFactory.getLogger(DataTree.class);
 
-    public static final long CONTAINER_EPHEMERAL_OWNER = Long.MIN_VALUE;
-
     /**
      * This hashtable provides a fast lookup to the datanodes. The tree is the
      * source of truth and is where all the locking occurs
@@ -137,6 +136,12 @@ public class DataTree {
     private final Set<String> containers =
             Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
 
+    /**
+     * This set contains the paths of all ttl nodes
+     */
+    private final Set<String> ttls =
+            Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+
     private final ReferenceCountedACLCache aclCache = new ReferenceCountedACLCache();
 
     @SuppressWarnings("unchecked")
@@ -156,6 +161,10 @@ public class DataTree {
         return new HashSet<String>(containers);
     }
 
+    public Set<String> getTtls() {
+        return new HashSet<String>(ttls);
+    }
+
     public Collection<Long> getSessions() {
         return ephemerals.keySet();
     }
@@ -463,8 +472,11 @@ public class DataTree {
             DataNode child = new DataNode(data, longval, stat);
             parent.addChild(childName);
             nodes.put(path, child);
-            if (ephemeralOwner == CONTAINER_EPHEMERAL_OWNER) {
+            EphemeralType ephemeralType = EphemeralType.get(ephemeralOwner);
+            if (ephemeralType == EphemeralType.CONTAINER) {
                 containers.add(path);
+            } else if (ephemeralType == EphemeralType.TTL) {
+                ttls.add(path);
             } else if (ephemeralOwner != 0) {
                 HashSet<String> list = ephemerals.get(ephemeralOwner);
                 if (list == null) {
@@ -534,8 +546,11 @@ public class DataTree {
             parent.removeChild(childName);
             parent.stat.setPzxid(zxid);
             long eowner = node.stat.getEphemeralOwner();
-            if (eowner == CONTAINER_EPHEMERAL_OWNER) {
+            EphemeralType ephemeralType = EphemeralType.get(eowner);
+            if (ephemeralType == EphemeralType.CONTAINER) {
                 containers.remove(path);
+            } else if (ephemeralType == EphemeralType.TTL) {
+                ttls.remove(path);
             } else if (eowner != 0) {
                 HashSet<String> nodes = ephemerals.get(eowner);
                 if (nodes != null) {
@@ -792,6 +807,19 @@ public class DataTree {
                             header.getZxid(), header.getTime(), stat);
                     rc.stat = stat;
                     break;
+                case OpCode.createTTL:
+                    CreateTTLTxn createTtlTxn = (CreateTTLTxn) txn;
+                    rc.path = createTtlTxn.getPath();
+                    stat = new Stat();
+                    createNode(
+                            createTtlTxn.getPath(),
+                            createTtlTxn.getData(),
+                            createTtlTxn.getAcl(),
+                            EphemeralType.ttlToEphemeralOwner(createTtlTxn.getTtl()),
+                            createTtlTxn.getParentCVersion(),
+                            header.getZxid(), header.getTime(), stat);
+                    rc.stat = stat;
+                    break;
                 case OpCode.createContainer:
                     CreateContainerTxn createContainerTxn = (CreateContainerTxn) txn;
                     rc.path = createContainerTxn.getPath();
@@ -800,7 +828,7 @@ public class DataTree {
                             createContainerTxn.getPath(),
                             createContainerTxn.getData(),
                             createContainerTxn.getAcl(),
-                            CONTAINER_EPHEMERAL_OWNER,
+                            EphemeralType.CONTAINER_EPHEMERAL_OWNER,
                             createContainerTxn.getParentCVersion(),
                             header.getZxid(), header.getTime(), stat);
                     rc.stat = stat;
@@ -856,6 +884,9 @@ public class DataTree {
                             case OpCode.create:
                                 record = new CreateTxn();
                                 break;
+                            case OpCode.createTTL:
+                                record = new CreateTTLTxn();
+                                break;
                             case OpCode.createContainer:
                                 record = new CreateContainerTxn();
                                 break;
@@ -1175,8 +1206,11 @@ public class DataTree {
                 }
                 parent.addChild(path.substring(lastSlash + 1));
                 long eowner = node.stat.getEphemeralOwner();
-                if (eowner == CONTAINER_EPHEMERAL_OWNER) {
+                EphemeralType ephemeralType = EphemeralType.get(eowner);
+                if (ephemeralType == EphemeralType.CONTAINER) {
                     containers.add(path);
+                } else if (ephemeralType == EphemeralType.TTL) {
+                    ttls.add(path);
                 } else if (eowner != 0) {
                     HashSet<String> list = ephemerals.get(eowner);
                     if (list == null) {

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/EphemeralType.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/EphemeralType.java b/src/java/main/org/apache/zookeeper/server/EphemeralType.java
new file mode 100644
index 0000000..cb4de9d
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/server/EphemeralType.java
@@ -0,0 +1,76 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.zookeeper.server;
+
+import org.apache.zookeeper.CreateMode;
+
+public enum EphemeralType {
+    /**
+     * Not ephemeral
+     */
+    VOID,
+    /**
+     * Standard, pre-3.5.x EPHEMERAL
+     */
+    NORMAL,
+    /**
+     * Container node
+     */
+    CONTAINER,
+    /**
+     * TTL node
+     */
+    TTL;
+
+    public static final long CONTAINER_EPHEMERAL_OWNER = Long.MIN_VALUE;
+    public static final long MAX_TTL = 0x0fffffffffffffffL;
+    public static final long TTL_MASK = 0x8000000000000000L;
+
+    public static EphemeralType get(long ephemeralOwner) {
+        if (ephemeralOwner == CONTAINER_EPHEMERAL_OWNER) {
+            return CONTAINER;
+        }
+        if (ephemeralOwner < 0) {
+            return TTL;
+        }
+        return (ephemeralOwner == 0) ? VOID : NORMAL;
+    }
+
+    public static void validateTTL(CreateMode mode, long ttl) {
+        if (mode.isTTL()) {
+            ttlToEphemeralOwner(ttl);
+        } else if (ttl >= 0) {
+            throw new IllegalArgumentException("ttl not valid for mode: " + mode);
+        }
+    }
+
+    public static long getTTL(long ephemeralOwner) {
+        if ((ephemeralOwner < 0) && (ephemeralOwner != CONTAINER_EPHEMERAL_OWNER)) {
+            return (ephemeralOwner & MAX_TTL);
+        }
+        return 0;
+    }
+
+    public static long ttlToEphemeralOwner(long ttl) {
+        if ((ttl > MAX_TTL) || (ttl <= 0)) {
+            throw new IllegalArgumentException("ttl must be positive and cannot be larger than: " + MAX_TTL);
+        }
+        return TTL_MASK | ttl;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java
index b41e03e..75a859f 100644
--- a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java
+++ b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java
@@ -215,6 +215,7 @@ public class FinalRequestProcessor implements RequestProcessor {
                             subResult = new CreateResult(subTxnResult.path);
                             break;
                         case OpCode.create2:
+                        case OpCode.createTTL:
                         case OpCode.createContainer:
                             subResult = new CreateResult(subTxnResult.path, subTxnResult.stat);
                             break;
@@ -244,6 +245,7 @@ public class FinalRequestProcessor implements RequestProcessor {
                 break;
             }
             case OpCode.create2:
+            case OpCode.createTTL:
             case OpCode.createContainer: {
                 lastOp = "CREA";
                 rsp = new Create2Response(rc.path, rc.stat);

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
index 4899c81..c28a471 100644
--- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
+++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
@@ -36,6 +36,7 @@ import org.apache.zookeeper.data.Id;
 import org.apache.zookeeper.data.StatPersisted;
 import org.apache.zookeeper.proto.CheckVersionRequest;
 import org.apache.zookeeper.proto.CreateRequest;
+import org.apache.zookeeper.proto.CreateTTLRequest;
 import org.apache.zookeeper.proto.DeleteRequest;
 import org.apache.zookeeper.proto.ReconfigRequest;
 import org.apache.zookeeper.proto.SetACLRequest;
@@ -53,6 +54,7 @@ import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
 import org.apache.zookeeper.txn.CheckVersionTxn;
 import org.apache.zookeeper.txn.CreateContainerTxn;
 import org.apache.zookeeper.txn.CreateSessionTxn;
+import org.apache.zookeeper.txn.CreateTTLTxn;
 import org.apache.zookeeper.txn.CreateTxn;
 import org.apache.zookeeper.txn.DeleteTxn;
 import org.apache.zookeeper.txn.ErrorTxn;
@@ -363,53 +365,9 @@ public class PrepRequestProcessor extends ZooKeeperCriticalThread implements
         switch (type) {
             case OpCode.create:
             case OpCode.create2:
+            case OpCode.createTTL:
             case OpCode.createContainer: {
-                CreateRequest createRequest = (CreateRequest)record;
-                if (deserialize) {
-                    ByteBufferInputStream.byteBuffer2Record(request.request, createRequest);
-                }
-                CreateMode createMode = CreateMode.fromFlag(createRequest.getFlags());
-                validateCreateRequest(createMode, request);
-                String path = createRequest.getPath();
-                String parentPath = validatePathForCreate(path, request.sessionId);
-
-                List<ACL> listACL = fixupACL(path, request.authInfo, createRequest.getAcl());
-                ChangeRecord parentRecord = getRecordForPath(parentPath);
-
-                checkACL(zks, parentRecord.acl, ZooDefs.Perms.CREATE, request.authInfo);
-                int parentCVersion = parentRecord.stat.getCversion();
-                if (createMode.isSequential()) {
-                    path = path + String.format(Locale.ENGLISH, "%010d", parentCVersion);
-                }
-                validatePath(path, request.sessionId);
-                try {
-                    if (getRecordForPath(path) != null) {
-                        throw new KeeperException.NodeExistsException(path);
-                    }
-                } catch (KeeperException.NoNodeException e) {
-                    // ignore this one
-                }
-                boolean ephemeralParent = (parentRecord.stat.getEphemeralOwner() != 0) &&
-                        (parentRecord.stat.getEphemeralOwner() != DataTree.CONTAINER_EPHEMERAL_OWNER);
-                if (ephemeralParent) {
-                    throw new KeeperException.NoChildrenForEphemeralsException(path);
-                }
-                int newCversion = parentRecord.stat.getCversion()+1;
-                if (type == OpCode.createContainer) {
-                    request.setTxn(new CreateContainerTxn(path, createRequest.getData(), listACL, newCversion));
-                } else {
-                    request.setTxn(new CreateTxn(path, createRequest.getData(), listACL, createMode.isEphemeral(),
-                            newCversion));
-                }
-                StatPersisted s = new StatPersisted();
-                if (createMode.isEphemeral()) {
-                    s.setEphemeralOwner(request.sessionId);
-                }
-                parentRecord = parentRecord.duplicate(request.getHdr().getZxid());
-                parentRecord.childCount++;
-                parentRecord.stat.setCversion(newCversion);
-                addChangeRecord(parentRecord);
-                addChangeRecord(new ChangeRecord(request.getHdr().getZxid(), path, s, 0, listACL));
+                pRequest2TxnCreate(type, request, record, deserialize);
                 break;
             }
             case OpCode.deleteContainer: {
@@ -420,7 +378,7 @@ public class PrepRequestProcessor extends ZooKeeperCriticalThread implements
                 if (nodeRecord.childCount > 0) {
                     throw new KeeperException.NotEmptyException(path);
                 }
-                if (nodeRecord.stat.getEphemeralOwner() != DataTree.CONTAINER_EPHEMERAL_OWNER) {
+                if (EphemeralType.get(nodeRecord.stat.getEphemeralOwner()) == EphemeralType.NORMAL) {
                     throw new KeeperException.BadVersionException(path);
                 }
                 request.setTxn(new DeleteTxn(path));
@@ -673,6 +631,75 @@ public class PrepRequestProcessor extends ZooKeeperCriticalThread implements
         }
     }
 
+    private void pRequest2TxnCreate(int type, Request request, Record record, boolean deserialize) throws IOException, KeeperException {
+        if (deserialize) {
+            ByteBufferInputStream.byteBuffer2Record(request.request, record);
+        }
+
+        int flags;
+        String path;
+        List<ACL> acl;
+        byte[] data;
+        long ttl;
+        if (type == OpCode.createTTL) {
+            CreateTTLRequest createTtlRequest = (CreateTTLRequest)record;
+            flags = createTtlRequest.getFlags();
+            path = createTtlRequest.getPath();
+            acl = createTtlRequest.getAcl();
+            data = createTtlRequest.getData();
+            ttl = createTtlRequest.getTtl();
+        } else {
+            CreateRequest createRequest = (CreateRequest)record;
+            flags = createRequest.getFlags();
+            path = createRequest.getPath();
+            acl = createRequest.getAcl();
+            data = createRequest.getData();
+            ttl = 0;
+        }
+        CreateMode createMode = CreateMode.fromFlag(flags);
+        validateCreateRequest(createMode, request);
+        String parentPath = validatePathForCreate(path, request.sessionId);
+
+        List<ACL> listACL = fixupACL(path, request.authInfo, acl);
+        ChangeRecord parentRecord = getRecordForPath(parentPath);
+
+        checkACL(zks, parentRecord.acl, ZooDefs.Perms.CREATE, request.authInfo);
+        int parentCVersion = parentRecord.stat.getCversion();
+        if (createMode.isSequential()) {
+            path = path + String.format(Locale.ENGLISH, "%010d", parentCVersion);
+        }
+        validatePath(path, request.sessionId);
+        try {
+            if (getRecordForPath(path) != null) {
+                throw new KeeperException.NodeExistsException(path);
+            }
+        } catch (KeeperException.NoNodeException e) {
+            // ignore this one
+        }
+        boolean ephemeralParent = EphemeralType.get(parentRecord.stat.getEphemeralOwner()) == EphemeralType.NORMAL;
+        if (ephemeralParent) {
+            throw new KeeperException.NoChildrenForEphemeralsException(path);
+        }
+        int newCversion = parentRecord.stat.getCversion()+1;
+        if (type == OpCode.createContainer) {
+            request.setTxn(new CreateContainerTxn(path, data, listACL, newCversion));
+        } else if (type == OpCode.createTTL) {
+            request.setTxn(new CreateTTLTxn(path, data, listACL, newCversion, ttl));
+        } else {
+            request.setTxn(new CreateTxn(path, data, listACL, createMode.isEphemeral(),
+                    newCversion));
+        }
+        StatPersisted s = new StatPersisted();
+        if (createMode.isEphemeral()) {
+            s.setEphemeralOwner(request.sessionId);
+        }
+        parentRecord = parentRecord.duplicate(request.getHdr().getZxid());
+        parentRecord.childCount++;
+        parentRecord.stat.setCversion(newCversion);
+        addChangeRecord(parentRecord);
+        addChangeRecord(new ChangeRecord(request.getHdr().getZxid(), path, s, 0, listACL));
+    }
+
     private void validatePath(String path, long sessionId) throws BadArgumentsException {
         try {
             PathUtils.validatePath(path);
@@ -721,6 +748,10 @@ public class PrepRequestProcessor extends ZooKeeperCriticalThread implements
                 CreateRequest create2Request = new CreateRequest();
                 pRequest2Txn(request.type, zks.getNextZxid(), request, create2Request, true);
                 break;
+            case OpCode.createTTL:
+                CreateTTLRequest createTtlRequest = new CreateTTLRequest();
+                pRequest2Txn(request.type, zks.getNextZxid(), request, createTtlRequest, true);
+                break;
             case OpCode.deleteContainer:
             case OpCode.delete:
                 DeleteRequest deleteRequest = new DeleteRequest();

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/Request.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/Request.java b/src/java/main/org/apache/zookeeper/server/Request.java
index d34efe6..ede9280 100644
--- a/src/java/main/org/apache/zookeeper/server/Request.java
+++ b/src/java/main/org/apache/zookeeper/server/Request.java
@@ -137,6 +137,7 @@ public class Request {
         case OpCode.closeSession:
         case OpCode.create:
         case OpCode.create2:
+        case OpCode.createTTL:
         case OpCode.createContainer:
         case OpCode.createSession:
         case OpCode.delete:
@@ -171,6 +172,7 @@ public class Request {
             return false;
         case OpCode.create:
         case OpCode.create2:
+        case OpCode.createTTL:
         case OpCode.createContainer:
         case OpCode.error:
         case OpCode.delete:
@@ -197,6 +199,8 @@ public class Request {
             return "create";
         case OpCode.create2:
             return "create2";
+        case OpCode.createTTL:
+            return "createTtl";
         case OpCode.createContainer:
             return "createContainer";
         case OpCode.setWatches:

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/TraceFormatter.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/TraceFormatter.java b/src/java/main/org/apache/zookeeper/server/TraceFormatter.java
index 955a724..63a3edd 100644
--- a/src/java/main/org/apache/zookeeper/server/TraceFormatter.java
+++ b/src/java/main/org/apache/zookeeper/server/TraceFormatter.java
@@ -37,6 +37,8 @@ public class TraceFormatter {
             return "create";
         case OpCode.create2:
             return "create2";
+        case OpCode.createTTL:
+            return "createTtl";
         case OpCode.createContainer:
             return "createContainer";
         case OpCode.delete:

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java
index 7713707..d6caa15 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java
@@ -133,6 +133,7 @@ public class CommitProcessor extends ZooKeeperCriticalThread implements
         switch (request.type) {
             case OpCode.create:
             case OpCode.create2:
+            case OpCode.createTTL:
             case OpCode.createContainer:
             case OpCode.delete:
             case OpCode.deleteContainer:

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java
index 832e4fa..c623eba 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java
@@ -84,6 +84,7 @@ public class FollowerRequestProcessor extends ZooKeeperCriticalThread implements
                     break;
                 case OpCode.create:
                 case OpCode.create2:
+                case OpCode.createTTL:
                 case OpCode.createContainer:
                 case OpCode.delete:
                 case OpCode.deleteContainer:

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java
index f5604a5..85a5212 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java
@@ -93,6 +93,7 @@ public class ObserverRequestProcessor extends ZooKeeperCriticalThread implements
                     break;
                 case OpCode.create:
                 case OpCode.create2:
+                case OpCode.createTTL:
                 case OpCode.createContainer:
                 case OpCode.delete:
                 case OpCode.deleteContainer:

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java
index 4a36f11..c62eb42 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java
@@ -82,6 +82,7 @@ public class ReadOnlyRequestProcessor extends ZooKeeperCriticalThread implements
                 case OpCode.sync:
                 case OpCode.create:
                 case OpCode.create2:
+                case OpCode.createTTL:
                 case OpCode.createContainer:
                 case OpCode.delete:
                 case OpCode.deleteContainer:

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/main/org/apache/zookeeper/server/util/SerializeUtils.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/util/SerializeUtils.java b/src/java/main/org/apache/zookeeper/server/util/SerializeUtils.java
index bfb6f1a..b7936f1 100644
--- a/src/java/main/org/apache/zookeeper/server/util/SerializeUtils.java
+++ b/src/java/main/org/apache/zookeeper/server/util/SerializeUtils.java
@@ -27,6 +27,7 @@ import org.apache.zookeeper.server.DataTree;
 import org.apache.zookeeper.server.ZooTrace;
 import org.apache.zookeeper.txn.CreateContainerTxn;
 import org.apache.zookeeper.txn.CreateSessionTxn;
+import org.apache.zookeeper.txn.CreateTTLTxn;
 import org.apache.zookeeper.txn.CreateTxn;
 import org.apache.zookeeper.txn.CreateTxnV0;
 import org.apache.zookeeper.txn.DeleteTxn;
@@ -68,6 +69,9 @@ public class SerializeUtils {
         case OpCode.create2:
             txn = new CreateTxn();
             break;
+        case OpCode.createTTL:
+            txn = new CreateTTLTxn();
+            break;
         case OpCode.createContainer:
             txn = new CreateContainerTxn();
             break;

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/test/org/apache/zookeeper/server/CreateTTLTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/CreateTTLTest.java b/src/java/test/org/apache/zookeeper/server/CreateTTLTest.java
new file mode 100644
index 0000000..66d17eb
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/CreateTTLTest.java
@@ -0,0 +1,213 @@
+/**
+ * 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.zookeeper.server;
+
+import org.apache.zookeeper.AsyncCallback;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Op;
+import org.apache.zookeeper.OpResult;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.test.ClientBase;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class CreateTTLTest extends ClientBase {
+    private ZooKeeper zk;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        zk = createClient();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        zk.close();
+    }
+
+    @Test
+    public void testCreate()
+            throws IOException, KeeperException, InterruptedException {
+        Stat stat = new Stat();
+        zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, stat, 100);
+        Assert.assertEquals(0, stat.getEphemeralOwner());
+
+        final AtomicLong fakeElapsed = new AtomicLong(0);
+        ContainerManager containerManager = newContainerManager(fakeElapsed);
+        containerManager.checkContainers();
+        Assert.assertNotNull("Ttl node should not have been deleted yet", zk.exists("/foo", false));
+
+        fakeElapsed.set(1000);
+        containerManager.checkContainers();
+        Assert.assertNull("Ttl node should have been deleted", zk.exists("/foo", false));
+    }
+
+    @Test
+    public void testCreateSequential()
+            throws IOException, KeeperException, InterruptedException {
+        Stat stat = new Stat();
+        String path = zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, stat, 100);
+        Assert.assertEquals(0, stat.getEphemeralOwner());
+
+        final AtomicLong fakeElapsed = new AtomicLong(0);
+        ContainerManager containerManager = newContainerManager(fakeElapsed);
+        containerManager.checkContainers();
+        Assert.assertNotNull("Ttl node should not have been deleted yet", zk.exists(path, false));
+
+        fakeElapsed.set(1000);
+        containerManager.checkContainers();
+        Assert.assertNull("Ttl node should have been deleted", zk.exists(path, false));
+    }
+
+    @Test
+    public void testCreateAsync()
+            throws IOException, KeeperException, InterruptedException {
+        AsyncCallback.Create2Callback callback = new AsyncCallback.Create2Callback() {
+            @Override
+            public void processResult(int rc, String path, Object ctx, String name, Stat stat) {
+                // NOP
+            }
+        };
+        zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, callback, null, 100);
+
+        final AtomicLong fakeElapsed = new AtomicLong(0);
+        ContainerManager containerManager = newContainerManager(fakeElapsed);
+        containerManager.checkContainers();
+        Assert.assertNotNull("Ttl node should not have been deleted yet", zk.exists("/foo", false));
+
+        fakeElapsed.set(1000);
+        containerManager.checkContainers();
+        Assert.assertNull("Ttl node should have been deleted", zk.exists("/foo", false));
+    }
+
+    @Test
+    public void testModifying()
+            throws IOException, KeeperException, InterruptedException {
+        Stat stat = new Stat();
+        zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, stat, 100);
+        Assert.assertEquals(0, stat.getEphemeralOwner());
+
+        final AtomicLong fakeElapsed = new AtomicLong(0);
+        ContainerManager containerManager = newContainerManager(fakeElapsed);
+        containerManager.checkContainers();
+        Assert.assertNotNull("Ttl node should not have been deleted yet", zk.exists("/foo", false));
+
+        for ( int i = 0; i < 10; ++i ) {
+            fakeElapsed.set(50);
+            zk.setData("/foo", new byte[i + 1], -1);
+            containerManager.checkContainers();
+            Assert.assertNotNull("Ttl node should not have been deleted yet", zk.exists("/foo", false));
+        }
+
+        fakeElapsed.set(200);
+        containerManager.checkContainers();
+        Assert.assertNull("Ttl node should have been deleted", zk.exists("/foo", false));
+    }
+
+    @Test
+    public void testMulti()
+            throws IOException, KeeperException, InterruptedException {
+        Op createTtl = Op.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, 100);
+        Op createTtlSequential = Op.create("/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, 200);
+        Op createNonTtl = Op.create("/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        List<OpResult> results = zk.multi(Arrays.asList(createTtl, createTtlSequential, createNonTtl));
+        String sequentialPath = ((OpResult.CreateResult)results.get(1)).getPath();
+
+        final AtomicLong fakeElapsed = new AtomicLong(0);
+        ContainerManager containerManager = newContainerManager(fakeElapsed);
+        containerManager.checkContainers();
+        Assert.assertNotNull("node should not have been deleted yet", zk.exists("/a", false));
+        Assert.assertNotNull("node should not have been deleted yet", zk.exists(sequentialPath, false));
+        Assert.assertNotNull("node should never be deleted", zk.exists("/c", false));
+
+        fakeElapsed.set(110);
+        containerManager.checkContainers();
+        Assert.assertNull("node should have been deleted", zk.exists("/a", false));
+        Assert.assertNotNull("node should not have been deleted yet", zk.exists(sequentialPath, false));
+        Assert.assertNotNull("node should never be deleted", zk.exists("/c", false));
+
+        fakeElapsed.set(210);
+        containerManager.checkContainers();
+        Assert.assertNull("node should have been deleted", zk.exists("/a", false));
+        Assert.assertNull("node should have been deleted", zk.exists(sequentialPath, false));
+        Assert.assertNotNull("node should never be deleted", zk.exists("/c", false));
+    }
+
+    @Test
+    public void testBadUsage() throws KeeperException, InterruptedException {
+        for ( CreateMode createMode : CreateMode.values() ) {
+            try {
+                zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode, new Stat(), createMode.isTTL() ? 0 : 100);
+                Assert.fail("should have thrown IllegalArgumentException");
+            } catch (IllegalArgumentException dummy) {
+                // correct
+            }
+        }
+
+        for ( CreateMode createMode : CreateMode.values() ) {
+            AsyncCallback.Create2Callback callback = new AsyncCallback.Create2Callback() {
+                @Override
+                public void processResult(int rc, String path, Object ctx, String name, Stat stat) {
+                    // NOP
+                }
+            };
+            try {
+                zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode, callback, null, createMode.isTTL() ? 0 : 100);
+                Assert.fail("should have thrown IllegalArgumentException");
+            } catch (IllegalArgumentException dummy) {
+                // correct
+            }
+        }
+
+        try {
+            Op op = Op.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, 0);
+            zk.multi(Collections.singleton(op));
+            Assert.fail("should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException dummy) {
+            // correct
+        }
+        try {
+            Op op = Op.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, 0);
+            zk.multi(Collections.singleton(op));
+            Assert.fail("should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException dummy) {
+            // correct
+        }
+    }
+
+    private ContainerManager newContainerManager(final AtomicLong fakeElapsed) {
+        return new ContainerManager(serverFactory.getZooKeeperServer()
+                .getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100) {
+            @Override
+            protected long getElapsed(DataNode node) {
+                return fakeElapsed.get();
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/java/test/org/apache/zookeeper/server/EphemeralTypeTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/EphemeralTypeTest.java b/src/java/test/org/apache/zookeeper/server/EphemeralTypeTest.java
new file mode 100644
index 0000000..2e6b3c7
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/EphemeralTypeTest.java
@@ -0,0 +1,58 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.zookeeper.server;
+
+import org.apache.zookeeper.CreateMode;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class EphemeralTypeTest {
+    @Test
+    public void testTtls() {
+        long ttls[] = {100, 1, EphemeralType.MAX_TTL};
+        for (long ttl : ttls) {
+            long ephemeralOwner = EphemeralType.ttlToEphemeralOwner(ttl);
+            Assert.assertEquals(EphemeralType.TTL, EphemeralType.get(ephemeralOwner));
+            Assert.assertEquals(ttl, EphemeralType.getTTL(ephemeralOwner));
+        }
+
+        EphemeralType.validateTTL(CreateMode.PERSISTENT_WITH_TTL, 100);
+        EphemeralType.validateTTL(CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, 100);
+
+        try {
+            EphemeralType.validateTTL(CreateMode.EPHEMERAL, 100);
+            Assert.fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException dummy) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testContainerValue() {
+        Assert.assertEquals(Long.MIN_VALUE, EphemeralType.CONTAINER_EPHEMERAL_OWNER);
+        Assert.assertEquals(EphemeralType.CONTAINER, EphemeralType.get(EphemeralType.CONTAINER_EPHEMERAL_OWNER));
+    }
+
+    @Test
+    public void testNonSpecial() {
+        Assert.assertEquals(EphemeralType.VOID, EphemeralType.get(0));
+        Assert.assertEquals(EphemeralType.NORMAL, EphemeralType.get(1));
+        Assert.assertEquals(EphemeralType.NORMAL, EphemeralType.get(Long.MAX_VALUE));
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/abad02e4/src/zookeeper.jute
----------------------------------------------------------------------
diff --git a/src/zookeeper.jute b/src/zookeeper.jute
index 3858081..a404e78 100644
--- a/src/zookeeper.jute
+++ b/src/zookeeper.jute
@@ -127,6 +127,13 @@ module org.apache.zookeeper.proto {
         vector<org.apache.zookeeper.data.ACL> acl;
         int flags;
     }
+    class CreateTTLRequest {
+        ustring path;
+        buffer data;
+        vector<org.apache.zookeeper.data.ACL> acl;
+        int flags;
+        long ttl;
+    }
     class DeleteRequest {
         ustring path;
         int version;
@@ -260,6 +267,13 @@ module org.apache.zookeeper.txn {
         boolean ephemeral;
         int parentCVersion;
     }
+    class CreateTTLTxn {
+        ustring path;
+        buffer data;
+        vector<org.apache.zookeeper.data.ACL> acl;
+        int parentCVersion;
+        long ttl;
+    }
     class CreateContainerTxn {
         ustring path;
         buffer data;


Mime
View raw message