zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From an...@apache.org
Subject [05/51] [partial] zookeeper git commit: ZOOKEEPER-3032: MAVEN MIGRATION - branch 3.4 move java server, client
Date Wed, 10 Oct 2018 10:30:04 GMT
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c0aa3b3f/zookeeper-common/src/main/java/org/apache/zookeeper/CreateMode.java
----------------------------------------------------------------------
diff --git a/zookeeper-common/src/main/java/org/apache/zookeeper/CreateMode.java b/zookeeper-common/src/main/java/org/apache/zookeeper/CreateMode.java
new file mode 100644
index 0000000..84f5be0
--- /dev/null
+++ b/zookeeper-common/src/main/java/org/apache/zookeeper/CreateMode.java
@@ -0,0 +1,94 @@
+/**
+ * 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;
+
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.zookeeper.KeeperException;
+
+/***
+ *  CreateMode value determines how the znode is created on ZooKeeper.
+ */
+@InterfaceAudience.Public
+public enum CreateMode {
+    
+    /**
+     * The znode will not be automatically deleted upon client's disconnect.
+     */
+    PERSISTENT (0, 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),
+    /**
+     * The znode will be deleted upon the client's disconnect.
+     */
+    EPHEMERAL (1, true, 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);
+
+    private static final Logger LOG = LoggerFactory.getLogger(CreateMode.class);
+
+    private boolean ephemeral;
+    private boolean sequential;
+    private int flag;
+
+    CreateMode(int flag, boolean ephemeral, boolean sequential) {
+        this.flag = flag;
+        this.ephemeral = ephemeral;
+        this.sequential = sequential;
+    }
+
+    public boolean isEphemeral() { 
+        return ephemeral;
+    }
+
+    public boolean isSequential() { 
+        return sequential;
+    }
+
+    public int toFlag() {
+        return flag;
+    }
+
+    /**
+     * Map an integer value to a CreateMode value
+     */
+    static public CreateMode fromFlag(int flag) throws KeeperException {
+        switch(flag) {
+        case 0: return CreateMode.PERSISTENT;
+
+        case 1: return CreateMode.EPHEMERAL;
+
+        case 2: return CreateMode.PERSISTENT_SEQUENTIAL;
+
+        case 3: return CreateMode.EPHEMERAL_SEQUENTIAL ;
+
+        default:
+            String errMsg = "Received an invalid flag value: " + flag
+                    + " to convert to a CreateMode";
+            LOG.error(errMsg);
+            throw new KeeperException.BadArgumentsException(errMsg);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c0aa3b3f/zookeeper-common/src/main/java/org/apache/zookeeper/Environment.java
----------------------------------------------------------------------
diff --git a/zookeeper-common/src/main/java/org/apache/zookeeper/Environment.java b/zookeeper-common/src/main/java/org/apache/zookeeper/Environment.java
new file mode 100644
index 0000000..51797a1
--- /dev/null
+++ b/zookeeper-common/src/main/java/org/apache/zookeeper/Environment.java
@@ -0,0 +1,103 @@
+/**
+ * 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;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provide insight into the runtime environment.
+ *
+ */
+public class Environment {
+    public static final String JAAS_CONF_KEY = "java.security.auth.login.config";
+
+    public static class Entry {
+        private String k;
+        private String v;
+        public Entry(String k, String v) {
+            this.k = k;
+            this.v = v;
+        }
+        public String getKey() { return k; }
+        public String getValue() { return v; }
+        
+        @Override
+        public String toString() {
+            return k + "=" + v;
+        }
+    }
+
+    private static void put(ArrayList<Entry> l, String k, String v) {
+        l.add(new Entry(k,v));
+    }
+
+    public static List<Entry> list() {
+        ArrayList<Entry> l = new ArrayList<Entry>();
+        put(l, "zookeeper.version", Version.getFullVersion());
+
+        try {
+            put(l, "host.name",
+                InetAddress.getLocalHost().getCanonicalHostName());
+        } catch (UnknownHostException e) {
+            put(l, "host.name", "<NA>");
+        }
+
+        put(l, "java.version",
+                System.getProperty("java.version", "<NA>"));
+        put(l, "java.vendor",
+                System.getProperty("java.vendor", "<NA>"));
+        put(l, "java.home",
+                System.getProperty("java.home", "<NA>"));
+        put(l, "java.class.path",
+                System.getProperty("java.class.path", "<NA>"));
+        put(l, "java.library.path",
+                System.getProperty("java.library.path", "<NA>"));
+        put(l, "java.io.tmpdir",
+                System.getProperty("java.io.tmpdir", "<NA>"));
+        put(l, "java.compiler",
+                System.getProperty("java.compiler", "<NA>"));
+        put(l, "os.name",
+                System.getProperty("os.name", "<NA>"));
+        put(l, "os.arch",
+                System.getProperty("os.arch", "<NA>"));
+        put(l, "os.version",
+                System.getProperty("os.version", "<NA>"));
+        put(l, "user.name",
+                System.getProperty("user.name", "<NA>"));
+        put(l, "user.home",
+                System.getProperty("user.home", "<NA>"));
+        put(l, "user.dir",
+                System.getProperty("user.dir", "<NA>"));
+        
+        return l;
+    }
+    
+    public static void logEnv(String msg, Logger log) {
+        List<Entry> env = Environment.list();
+        for (Entry e : env) {
+            log.info(msg + e.toString());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c0aa3b3f/zookeeper-common/src/main/java/org/apache/zookeeper/KeeperException.java
----------------------------------------------------------------------
diff --git a/zookeeper-common/src/main/java/org/apache/zookeeper/KeeperException.java b/zookeeper-common/src/main/java/org/apache/zookeeper/KeeperException.java
new file mode 100644
index 0000000..bdf4203
--- /dev/null
+++ b/zookeeper-common/src/main/java/org/apache/zookeeper/KeeperException.java
@@ -0,0 +1,728 @@
+/**
+ * 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;
+
+import org.apache.yetus.audience.InterfaceAudience;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SuppressWarnings("serial")
+@InterfaceAudience.Public
+public abstract class KeeperException extends Exception {
+    /**
+     * All multi-requests that result in an exception retain the results
+     * here so that it is possible to examine the problems in the catch
+     * scope.  Non-multi requests will get a null if they try to access
+     * these results.
+     */
+    private List<OpResult> results;
+
+    /**
+     * All non-specific keeper exceptions should be constructed via
+     * this factory method in order to guarantee consistency in error
+     * codes and such.  If you know the error code, then you should
+     * construct the special purpose exception directly.  That will
+     * allow you to have the most specific possible declarations of
+     * what exceptions might actually be thrown.
+     *
+     * @param code The error code.
+     * @param path The ZooKeeper path being operated on.
+     * @return The specialized exception, presumably to be thrown by
+     *  the caller.
+     */
+    public static KeeperException create(Code code, String path) {
+        KeeperException r = create(code);
+        r.path = path;
+        return r;
+    }
+
+    /**
+     * @deprecated deprecated in 3.1.0, use {@link #create(Code, String)}
+     * instead
+     */
+    @Deprecated
+    public static KeeperException create(int code, String path) {
+        KeeperException r = create(Code.get(code));
+        r.path = path;
+        return r;
+    }
+
+    /**
+     * @deprecated deprecated in 3.1.0, use {@link #create(Code)}
+     * instead
+     */
+    @Deprecated
+    public static KeeperException create(int code) {
+        return create(Code.get(code));
+    }
+
+    /**
+     * All non-specific keeper exceptions should be constructed via
+     * this factory method in order to guarantee consistency in error
+     * codes and such.  If you know the error code, then you should
+     * construct the special purpose exception directly.  That will
+     * allow you to have the most specific possible declarations of
+     * what exceptions might actually be thrown.
+     *
+     * @param code The error code of your new exception.  This will
+     * also determine the specific type of the exception that is
+     * returned.
+     * @return The specialized exception, presumably to be thrown by
+     * the caller.
+     */
+    public static KeeperException create(Code code) {
+        switch (code) {
+            case SYSTEMERROR:
+                return new SystemErrorException();
+            case RUNTIMEINCONSISTENCY:
+                return new RuntimeInconsistencyException();
+            case DATAINCONSISTENCY:
+                return new DataInconsistencyException();
+            case CONNECTIONLOSS:
+                return new ConnectionLossException();
+            case MARSHALLINGERROR:
+                return new MarshallingErrorException();
+            case UNIMPLEMENTED:
+                return new UnimplementedException();
+            case OPERATIONTIMEOUT:
+                return new OperationTimeoutException();
+            case BADARGUMENTS:
+                return new BadArgumentsException();
+            case APIERROR:
+                return new APIErrorException();
+            case NONODE:
+                return new NoNodeException();
+            case NOAUTH:
+                return new NoAuthException();
+            case BADVERSION:
+                return new BadVersionException();
+            case NOCHILDRENFOREPHEMERALS:
+                return new NoChildrenForEphemeralsException();
+            case NODEEXISTS:
+                return new NodeExistsException();
+            case INVALIDACL:
+                return new InvalidACLException();
+            case AUTHFAILED:
+                return new AuthFailedException();
+            case NOTEMPTY:
+                return new NotEmptyException();
+            case SESSIONEXPIRED:
+                return new SessionExpiredException();
+            case INVALIDCALLBACK:
+                return new InvalidCallbackException();
+            case SESSIONMOVED:
+                return new SessionMovedException();
+            case NOTREADONLY:
+                return new NotReadOnlyException();
+
+            case OK:
+            default:
+                throw new IllegalArgumentException("Invalid exception code");
+        }
+    }
+
+    /**
+     * Set the code for this exception
+     * @param code error code
+     * @deprecated deprecated in 3.1.0, exceptions should be immutable, this
+     * method should not be used
+     */
+    @Deprecated
+    public void setCode(int code) {
+        this.code = Code.get(code);
+    }
+
+    /** This interface contains the original static final int constants
+     * which have now been replaced with an enumeration in Code. Do not
+     * reference this class directly, if necessary (legacy code) continue
+     * to access the constants through Code.
+     * Note: an interface is used here due to the fact that enums cannot
+     * reference constants defined within the same enum as said constants
+     * are considered initialized _after_ the enum itself. By using an
+     * interface as a super type this allows the deprecated constants to
+     * be initialized first and referenced when constructing the enums. I
+     * didn't want to have constants declared twice. This
+     * interface should be private, but it's declared public to enable
+     * javadoc to include in the user API spec.
+     */
+    @Deprecated
+    @InterfaceAudience.Public
+    public interface CodeDeprecated {
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#OK} instead
+         */
+        @Deprecated
+        public static final int Ok = 0;
+
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#SYSTEMERROR} instead
+         */
+        @Deprecated
+        public static final int SystemError = -1;
+        /**
+         * @deprecated deprecated in 3.1.0, use
+         * {@link Code#RUNTIMEINCONSISTENCY} instead
+         */
+        @Deprecated
+        public static final int RuntimeInconsistency = -2;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#DATAINCONSISTENCY}
+         * instead
+         */
+        @Deprecated
+        public static final int DataInconsistency = -3;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#CONNECTIONLOSS}
+         * instead
+         */
+        @Deprecated
+        public static final int ConnectionLoss = -4;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#MARSHALLINGERROR}
+         * instead
+         */
+        @Deprecated
+        public static final int MarshallingError = -5;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#UNIMPLEMENTED}
+         * instead
+         */
+        @Deprecated
+        public static final int Unimplemented = -6;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#OPERATIONTIMEOUT}
+         * instead
+         */
+        @Deprecated
+        public static final int OperationTimeout = -7;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#BADARGUMENTS}
+         * instead
+         */
+        @Deprecated
+        public static final int BadArguments = -8;
+
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#APIERROR} instead
+         */
+        @Deprecated
+        public static final int APIError = -100;
+
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#NONODE} instead
+         */
+        @Deprecated
+        public static final int NoNode = -101;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#NOAUTH} instead
+         */
+        @Deprecated
+        public static final int NoAuth = -102;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#BADVERSION} instead
+         */
+        @Deprecated
+        public static final int BadVersion = -103;
+        /**
+         * @deprecated deprecated in 3.1.0, use
+         * {@link Code#NOCHILDRENFOREPHEMERALS}
+         * instead
+         */
+        @Deprecated
+        public static final int NoChildrenForEphemerals = -108;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#NODEEXISTS} instead
+         */
+        @Deprecated
+        public static final int NodeExists = -110;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#NOTEMPTY} instead
+         */
+        @Deprecated
+        public static final int NotEmpty = -111;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#SESSIONEXPIRED} instead
+         */
+        @Deprecated
+        public static final int SessionExpired = -112;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#INVALIDCALLBACK}
+         * instead
+         */
+        @Deprecated
+        public static final int InvalidCallback = -113;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#INVALIDACL} instead
+         */
+        @Deprecated
+        public static final int InvalidACL = -114;
+        /**
+         * @deprecated deprecated in 3.1.0, use {@link Code#AUTHFAILED} instead
+         */
+        @Deprecated
+        public static final int AuthFailed = -115;
+        /**
+         * This value will be used directly in {@link CODE#SESSIONMOVED}
+         */
+        // public static final int SessionMoved = -118;
+    }
+
+    /** Codes which represent the various KeeperException
+     * types. This enum replaces the deprecated earlier static final int
+     * constants. The old, deprecated, values are in "camel case" while the new
+     * enum values are in all CAPS.
+     */
+    @InterfaceAudience.Public
+    public static enum Code implements CodeDeprecated {
+        /** Everything is OK */
+        OK (Ok),
+
+        /** System and server-side errors.
+         * This is never thrown by the server, it shouldn't be used other than
+         * to indicate a range. Specifically error codes greater than this
+         * value, but lesser than {@link #APIERROR}, are system errors.
+         */
+        SYSTEMERROR (SystemError),
+
+        /** A runtime inconsistency was found */
+        RUNTIMEINCONSISTENCY (RuntimeInconsistency),
+        /** A data inconsistency was found */
+        DATAINCONSISTENCY (DataInconsistency),
+        /** Connection to the server has been lost */
+        CONNECTIONLOSS (ConnectionLoss),
+        /** Error while marshalling or unmarshalling data */
+        MARSHALLINGERROR (MarshallingError),
+        /** Operation is unimplemented */
+        UNIMPLEMENTED (Unimplemented),
+        /** Operation timeout */
+        OPERATIONTIMEOUT (OperationTimeout),
+        /** Invalid arguments */
+        BADARGUMENTS (BadArguments),
+        
+        /** API errors.
+         * This is never thrown by the server, it shouldn't be used other than
+         * to indicate a range. Specifically error codes greater than this
+         * value are API errors (while values less than this indicate a
+         * {@link #SYSTEMERROR}).
+         */
+        APIERROR (APIError),
+
+        /** Node does not exist */
+        NONODE (NoNode),
+        /** Not authenticated */
+        NOAUTH (NoAuth),
+        /** Version conflict */
+        BADVERSION (BadVersion),
+        /** Ephemeral nodes may not have children */
+        NOCHILDRENFOREPHEMERALS (NoChildrenForEphemerals),
+        /** The node already exists */
+        NODEEXISTS (NodeExists),
+        /** The node has children */
+        NOTEMPTY (NotEmpty),
+        /** The session has been expired by the server */
+        SESSIONEXPIRED (SessionExpired),
+        /** Invalid callback specified */
+        INVALIDCALLBACK (InvalidCallback),
+        /** Invalid ACL specified */
+        INVALIDACL (InvalidACL),
+        /** Client authentication failed */
+        AUTHFAILED (AuthFailed),
+        /** Session moved to another server, so operation is ignored */
+        SESSIONMOVED (-118),
+        /** State-changing request is passed to read-only server */
+        NOTREADONLY (-119);
+
+        private static final Map<Integer,Code> lookup
+            = new HashMap<Integer,Code>();
+
+        static {
+            for(Code c : EnumSet.allOf(Code.class))
+                lookup.put(c.code, c);
+        }
+
+        private final int code;
+        Code(int code) {
+            this.code = code;
+        }
+
+        /**
+         * Get the int value for a particular Code.
+         * @return error code as integer
+         */
+        public int intValue() { return code; }
+
+        /**
+         * Get the Code value for a particular integer error code
+         * @param code int error code
+         * @return Code value corresponding to specified int code, or null
+         */
+        public static Code get(int code) {
+            return lookup.get(code);
+        }
+    }
+
+    static String getCodeMessage(Code code) {
+        switch (code) {
+            case OK:
+                return "ok";
+            case SYSTEMERROR:
+                return "SystemError";
+            case RUNTIMEINCONSISTENCY:
+                return "RuntimeInconsistency";
+            case DATAINCONSISTENCY:
+                return "DataInconsistency";
+            case CONNECTIONLOSS:
+                return "ConnectionLoss";
+            case MARSHALLINGERROR:
+                return "MarshallingError";
+            case UNIMPLEMENTED:
+                return "Unimplemented";
+            case OPERATIONTIMEOUT:
+                return "OperationTimeout";
+            case BADARGUMENTS:
+                return "BadArguments";
+            case APIERROR:
+                return "APIError";
+            case NONODE:
+                return "NoNode";
+            case NOAUTH:
+                return "NoAuth";
+            case BADVERSION:
+                return "BadVersion";
+            case NOCHILDRENFOREPHEMERALS:
+                return "NoChildrenForEphemerals";
+            case NODEEXISTS:
+                return "NodeExists";
+            case INVALIDACL:
+                return "InvalidACL";
+            case AUTHFAILED:
+                return "AuthFailed";
+            case NOTEMPTY:
+                return "Directory not empty";
+            case SESSIONEXPIRED:
+                return "Session expired";
+            case INVALIDCALLBACK:
+                return "Invalid callback";
+            case SESSIONMOVED:
+                return "Session moved";
+            case NOTREADONLY:
+                return "Not a read-only call";
+            default:
+                return "Unknown error " + code;
+        }
+    }
+
+    private Code code;
+
+    private String path;
+
+    public KeeperException(Code code) {
+        this.code = code;
+    }
+
+    KeeperException(Code code, String path) {
+        this.code = code;
+        this.path = path;
+    }
+
+    /**
+     * Read the error code for this exception
+     * @return the error code for this exception
+     * @deprecated deprecated in 3.1.0, use {@link #code()} instead
+     */
+    @Deprecated
+    public int getCode() {
+        return code.code;
+    }
+
+    /**
+     * Read the error Code for this exception
+     * @return the error Code for this exception
+     */
+    public Code code() {
+        return code;
+    }
+
+    /**
+     * Read the path for this exception
+     * @return the path associated with this error, null if none
+     */
+    public String getPath() {
+        return path;
+    }
+
+    @Override
+    public String getMessage() {
+        if (path == null) {
+            return "KeeperErrorCode = " + getCodeMessage(code);
+        }
+        return "KeeperErrorCode = " + getCodeMessage(code) + " for " + path;
+    }
+
+    void setMultiResults(List<OpResult> results) {
+        this.results = results;
+    }
+
+    /**
+     * If this exception was thrown by a multi-request then the (partial) results
+     * and error codes can be retrieved using this getter.
+     * @return A copy of the list of results from the operations in the multi-request.
+     *
+     * @since 3.4.0
+     *
+     */
+    public List<OpResult> getResults() {
+        return results != null ? new ArrayList<OpResult>(results) : null;
+    }
+
+    /**
+     *  @see Code#APIERROR
+     */
+    @InterfaceAudience.Public
+    public static class APIErrorException extends KeeperException {
+        public APIErrorException() {
+            super(Code.APIERROR);
+        }
+    }
+
+    /**
+     *  @see Code#AUTHFAILED
+     */
+    @InterfaceAudience.Public
+    public static class AuthFailedException extends KeeperException {
+        public AuthFailedException() {
+            super(Code.AUTHFAILED);
+        }
+    }
+
+    /**
+     *  @see Code#BADARGUMENTS
+     */
+    @InterfaceAudience.Public
+    public static class BadArgumentsException extends KeeperException {
+        public BadArgumentsException() {
+            super(Code.BADARGUMENTS);
+        }
+        public BadArgumentsException(String path) {
+            super(Code.BADARGUMENTS, path);
+        }
+    }
+
+    /**
+     * @see Code#BADVERSION
+     */
+    @InterfaceAudience.Public
+    public static class BadVersionException extends KeeperException {
+        public BadVersionException() {
+            super(Code.BADVERSION);
+        }
+        public BadVersionException(String path) {
+            super(Code.BADVERSION, path);
+        }
+    }
+
+    /**
+     * @see Code#CONNECTIONLOSS
+     */
+    @InterfaceAudience.Public
+    public static class ConnectionLossException extends KeeperException {
+        public ConnectionLossException() {
+            super(Code.CONNECTIONLOSS);
+        }
+    }
+
+    /**
+     * @see Code#DATAINCONSISTENCY
+     */
+    @InterfaceAudience.Public
+    public static class DataInconsistencyException extends KeeperException {
+        public DataInconsistencyException() {
+            super(Code.DATAINCONSISTENCY);
+        }
+    }
+
+    /**
+     * @see Code#INVALIDACL
+     */
+    @InterfaceAudience.Public
+    public static class InvalidACLException extends KeeperException {
+        public InvalidACLException() {
+            super(Code.INVALIDACL);
+        }
+        public InvalidACLException(String path) {
+            super(Code.INVALIDACL, path);
+        }
+    }
+
+    /**
+     * @see Code#INVALIDCALLBACK
+     */
+    @InterfaceAudience.Public
+    public static class InvalidCallbackException extends KeeperException {
+        public InvalidCallbackException() {
+            super(Code.INVALIDCALLBACK);
+        }
+    }
+
+    /**
+     * @see Code#MARSHALLINGERROR
+     */
+    @InterfaceAudience.Public
+    public static class MarshallingErrorException extends KeeperException {
+        public MarshallingErrorException() {
+            super(Code.MARSHALLINGERROR);
+        }
+    }
+
+    /**
+     * @see Code#NOAUTH
+     */
+    @InterfaceAudience.Public
+    public static class NoAuthException extends KeeperException {
+        public NoAuthException() {
+            super(Code.NOAUTH);
+        }
+    }
+
+    /**
+     * @see Code#NOCHILDRENFOREPHEMERALS
+     */
+    @InterfaceAudience.Public
+    public static class NoChildrenForEphemeralsException extends KeeperException {
+        public NoChildrenForEphemeralsException() {
+            super(Code.NOCHILDRENFOREPHEMERALS);
+        }
+        public NoChildrenForEphemeralsException(String path) {
+            super(Code.NOCHILDRENFOREPHEMERALS, path);
+        }
+    }
+
+    /**
+     * @see Code#NODEEXISTS
+     */
+    @InterfaceAudience.Public
+    public static class NodeExistsException extends KeeperException {
+        public NodeExistsException() {
+            super(Code.NODEEXISTS);
+        }
+        public NodeExistsException(String path) {
+            super(Code.NODEEXISTS, path);
+        }
+    }
+
+    /**
+     * @see Code#NONODE
+     */
+    @InterfaceAudience.Public
+    public static class NoNodeException extends KeeperException {
+        public NoNodeException() {
+            super(Code.NONODE);
+        }
+        public NoNodeException(String path) {
+            super(Code.NONODE, path);
+        }
+    }
+
+    /**
+     * @see Code#NOTEMPTY
+     */
+    @InterfaceAudience.Public
+    public static class NotEmptyException extends KeeperException {
+        public NotEmptyException() {
+            super(Code.NOTEMPTY);
+        }
+        public NotEmptyException(String path) {
+            super(Code.NOTEMPTY, path);
+        }
+    }
+
+    /**
+     * @see Code#OPERATIONTIMEOUT
+     */
+    @InterfaceAudience.Public
+    public static class OperationTimeoutException extends KeeperException {
+        public OperationTimeoutException() {
+            super(Code.OPERATIONTIMEOUT);
+        }
+    }
+
+    /**
+     * @see Code#RUNTIMEINCONSISTENCY
+     */
+    @InterfaceAudience.Public
+    public static class RuntimeInconsistencyException extends KeeperException {
+        public RuntimeInconsistencyException() {
+            super(Code.RUNTIMEINCONSISTENCY);
+        }
+    }
+
+    /**
+     * @see Code#SESSIONEXPIRED
+     */
+    @InterfaceAudience.Public
+    public static class SessionExpiredException extends KeeperException {
+        public SessionExpiredException() {
+            super(Code.SESSIONEXPIRED);
+        }
+    }
+
+    /**
+     * @see Code#SESSIONMOVED
+     */
+    @InterfaceAudience.Public
+    public static class SessionMovedException extends KeeperException {
+        public SessionMovedException() {
+            super(Code.SESSIONMOVED);
+        }
+    }
+
+    /**
+     * @see Code#NOTREADONLY
+     */
+    @InterfaceAudience.Public
+    public static class NotReadOnlyException extends KeeperException {
+        public NotReadOnlyException() {
+            super(Code.NOTREADONLY);
+        }
+    }
+
+    /**
+     * @see Code#SYSTEMERROR
+     */
+    @InterfaceAudience.Public
+    public static class SystemErrorException extends KeeperException {
+        public SystemErrorException() {
+            super(Code.SYSTEMERROR);
+        }
+    }
+
+    /**
+     * @see Code#UNIMPLEMENTED
+     */
+    @InterfaceAudience.Public
+    public static class UnimplementedException extends KeeperException {
+        public UnimplementedException() {
+            super(Code.UNIMPLEMENTED);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c0aa3b3f/zookeeper-common/src/main/java/org/apache/zookeeper/Login.java
----------------------------------------------------------------------
diff --git a/zookeeper-common/src/main/java/org/apache/zookeeper/Login.java b/zookeeper-common/src/main/java/org/apache/zookeeper/Login.java
new file mode 100644
index 0000000..c4975be
--- /dev/null
+++ b/zookeeper-common/src/main/java/org/apache/zookeeper/Login.java
@@ -0,0 +1,407 @@
+/**
+ * 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;
+
+/** 
+ * This class is responsible for refreshing Kerberos credentials for
+ * logins for both Zookeeper client and server.
+ * See ZooKeeperSaslServer for server-side usage.
+ * See ZooKeeperSaslClient for client-side usage.
+ */
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.zookeeper.client.ZooKeeperSaslClient;
+import org.apache.zookeeper.common.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.Subject;
+import java.util.Date;
+import java.util.Random;
+import java.util.Set;
+
+public class Login {
+    private static final Logger LOG = LoggerFactory.getLogger(Login.class);
+    public CallbackHandler callbackHandler;
+
+    // LoginThread will sleep until 80% of time from last refresh to
+    // ticket's expiry has been reached, at which time it will wake
+    // and try to renew the ticket.
+    private static final float TICKET_RENEW_WINDOW = 0.80f;
+
+    /**
+     * Percentage of random jitter added to the renewal time
+     */
+    private static final float TICKET_RENEW_JITTER = 0.05f;
+
+    // Regardless of TICKET_RENEW_WINDOW setting above and the ticket expiry time,
+    // thread will not sleep between refresh attempts any less than 1 minute (60*1000 milliseconds = 1 minute).
+    // Change the '1' to e.g. 5, to change this to 5 minutes.
+    private static final long MIN_TIME_BEFORE_RELOGIN = 1 * 60 * 1000L;
+
+    private Subject subject = null;
+    private Thread t = null;
+    private boolean isKrbTicket = false;
+    private boolean isUsingTicketCache = false;
+    private boolean isUsingKeytab = false;
+
+    /** Random number generator */
+    private static Random rng = new Random();
+
+    private LoginContext login = null;
+    private String loginContextName = null;
+    private String keytabFile = null;
+    private String principal = null;
+
+    // Initialize 'lastLogin' to do a login at first time
+    private long lastLogin = Time.currentElapsedTime() - MIN_TIME_BEFORE_RELOGIN;
+
+    /**
+     * LoginThread constructor. The constructor starts the thread used
+     * to periodically re-login to the Kerberos Ticket Granting Server.
+     * @param loginContextName
+     *               name of section in JAAS file that will be use to login.
+     *               Passed as first param to javax.security.auth.login.LoginContext().
+     *
+     * @param callbackHandler
+     *               Passed as second param to javax.security.auth.login.LoginContext().
+     * @throws javax.security.auth.login.LoginException
+     *               Thrown if authentication fails.
+     */
+    public Login(final String loginContextName, CallbackHandler callbackHandler)
+            throws LoginException {
+        this.callbackHandler = callbackHandler;
+        login = login(loginContextName);
+        this.loginContextName = loginContextName;
+        subject = login.getSubject();
+        isKrbTicket = !subject.getPrivateCredentials(KerberosTicket.class).isEmpty();
+        AppConfigurationEntry entries[] = Configuration.getConfiguration().getAppConfigurationEntry(loginContextName);
+        for (AppConfigurationEntry entry: entries) {
+            // there will only be a single entry, so this for() loop will only be iterated through once.
+            if (entry.getOptions().get("useTicketCache") != null) {
+                String val = (String)entry.getOptions().get("useTicketCache");
+                if (val.equals("true")) {
+                    isUsingTicketCache = true;
+                }
+            }
+            if (entry.getOptions().get("keyTab") != null) {
+                keytabFile = (String)entry.getOptions().get("keyTab");
+                isUsingKeytab = true;
+            }
+            if (entry.getOptions().get("principal") != null) {
+                principal = (String)entry.getOptions().get("principal");
+            }
+            break;
+        }
+
+        if (!isKrbTicket) {
+            // if no TGT, do not bother with ticket management.
+            return;
+        }
+
+        // Refresh the Ticket Granting Ticket (TGT) periodically. How often to refresh is determined by the
+        // TGT's existing expiry date and the configured MIN_TIME_BEFORE_RELOGIN. For testing and development,
+        // you can decrease the interval of expiration of tickets (for example, to 3 minutes) by running :
+        //  "modprinc -maxlife 3mins <principal>" in kadmin.
+        t = new Thread(new Runnable() {
+            public void run() {
+                LOG.info("TGT refresh thread started.");
+                while (true) {  // renewal thread's main loop. if it exits from here, thread will exit.
+                    KerberosTicket tgt = getTGT();
+                    long now = Time.currentWallTime();
+                    long nextRefresh;
+                    Date nextRefreshDate;
+                    if (tgt == null) {
+                        nextRefresh = now + MIN_TIME_BEFORE_RELOGIN;
+                        nextRefreshDate = new Date(nextRefresh);
+                        LOG.warn("No TGT found: will try again at " + nextRefreshDate);
+                    } else {
+                        nextRefresh = getRefreshTime(tgt);
+                        long expiry = tgt.getEndTime().getTime();
+                        Date expiryDate = new Date(expiry);
+                        if ((isUsingTicketCache) && (tgt.getEndTime().equals(tgt.getRenewTill()))) {
+                            LOG.error("The TGT cannot be renewed beyond the next expiry date: " + expiryDate + "." +
+                                    "This process will not be able to authenticate new SASL connections after that " +
+                                    "time (for example, it will not be authenticate a new connection with a Zookeeper " +
+                                    "Quorum member).  Ask your system administrator to either increase the " +
+                                    "'renew until' time by doing : 'modprinc -maxrenewlife " + principal + "' within " +
+                                    "kadmin, or instead, to generate a keytab for " + principal + ". Because the TGT's " +
+                                    "expiry cannot be further extended by refreshing, exiting refresh thread now.");
+                            return;
+                        }
+                        // determine how long to sleep from looking at ticket's expiry.
+                        // We should not allow the ticket to expire, but we should take into consideration
+                        // MIN_TIME_BEFORE_RELOGIN. Will not sleep less than MIN_TIME_BEFORE_RELOGIN, unless doing so
+                        // would cause ticket expiration.
+                        if ((nextRefresh > expiry) ||
+                                ((now + MIN_TIME_BEFORE_RELOGIN) > expiry)) {
+                            // expiry is before next scheduled refresh).
+                            nextRefresh = now;
+                        } else {
+                            if (nextRefresh < (now + MIN_TIME_BEFORE_RELOGIN)) {
+                                // next scheduled refresh is sooner than (now + MIN_TIME_BEFORE_LOGIN).
+                                Date until = new Date(nextRefresh);
+                                Date newuntil = new Date(now + MIN_TIME_BEFORE_RELOGIN);
+                                LOG.warn("TGT refresh thread time adjusted from : " + until + " to : " + newuntil + " since "
+                                        + "the former is sooner than the minimum refresh interval ("
+                                        + MIN_TIME_BEFORE_RELOGIN / 1000 + " seconds) from now.");
+                            }
+                            nextRefresh = Math.max(nextRefresh, now + MIN_TIME_BEFORE_RELOGIN);
+                        }
+                        nextRefreshDate = new Date(nextRefresh);
+                        if (nextRefresh > expiry) {
+                            LOG.error("next refresh: " + nextRefreshDate + " is later than expiry " + expiryDate
+                                    + ". This may indicate a clock skew problem. Check that this host and the KDC's "
+                                    + "hosts' clocks are in sync. Exiting refresh thread.");
+                            return;
+                        }
+                    }
+                    if (now == nextRefresh) {
+                        LOG.info("refreshing now because expiry is before next scheduled refresh time.");
+                    } else if (now < nextRefresh) {
+                        Date until = new Date(nextRefresh);
+                        LOG.info("TGT refresh sleeping until: " + until.toString());
+                        try {
+                            Thread.sleep(nextRefresh - now);
+                        } catch (InterruptedException ie) {
+                            LOG.warn("TGT renewal thread has been interrupted and will exit.");
+                            break;
+                        }
+                    }
+                    else {
+                        LOG.error("nextRefresh:" + nextRefreshDate + " is in the past: exiting refresh thread. Check"
+                                + " clock sync between this host and KDC - (KDC's clock is likely ahead of this host)."
+                                + " Manual intervention will be required for this client to successfully authenticate."
+                                + " Exiting refresh thread.");
+                        break;
+                    }
+                    if (isUsingTicketCache) {
+                        String cmd = "/usr/bin/kinit";
+                        if (System.getProperty("zookeeper.kinit") != null) {
+                            cmd = System.getProperty("zookeeper.kinit");
+                        }
+                        String kinitArgs = "-R";
+                        int retry = 1;
+                        while (retry >= 0) {
+                            try {
+                                LOG.debug("running ticket cache refresh command: " + cmd + " " + kinitArgs);
+                                Shell.execCommand(cmd, kinitArgs);
+                                break;
+                            } catch (Exception e) {
+                                if (retry > 0) {
+                                    --retry;
+                                    // sleep for 10 seconds
+                                    try {
+                                        Thread.sleep(10 * 1000);
+                                    } catch (InterruptedException ie) {
+                                        LOG.error("Interrupted while renewing TGT, exiting Login thread");
+                                        return;
+                                    }
+                                } else {
+                                    LOG.warn("Could not renew TGT due to problem running shell command: '" + cmd
+                                            + " " + kinitArgs + "'" + "; exception was:" + e + ". Exiting refresh thread.",e);
+                                    return;
+                                }
+                            }
+                        }
+                    }
+                    try {
+                        int retry = 1;
+                        while (retry >= 0) {
+                            try {
+                                reLogin();
+                                break;
+                            } catch (LoginException le) {
+                                if (retry > 0) {
+                                    --retry;
+                                    // sleep for 10 seconds.
+                                    try {
+                                        Thread.sleep(10 * 1000);
+                                    } catch (InterruptedException e) {
+                                        LOG.error("Interrupted during login retry after LoginException:", le);
+                                        throw le;
+                                    }
+                                } else {
+                                    LOG.error("Could not refresh TGT for principal: " + principal + ".", le);
+                                }
+                            }
+                        }
+                    } catch (LoginException le) {
+                        LOG.error("Failed to refresh TGT: refresh thread exiting now.",le);
+                        break;
+                    }
+                }
+            }
+        });
+        t.setDaemon(true);
+    }
+
+    public void startThreadIfNeeded() {
+        // thread object 't' will be null if a refresh thread is not needed.
+        if (t != null) {
+            t.start();
+        }
+    }
+
+    public void shutdown() {
+        if ((t != null) && (t.isAlive())) {
+            t.interrupt();
+            try {
+                t.join();
+            } catch (InterruptedException e) {
+                LOG.warn("error while waiting for Login thread to shutdown: " + e);
+            }
+        }
+    }
+
+    public Subject getSubject() {
+        return subject;
+    }
+
+    public String getLoginContextName() {
+        return loginContextName;
+    }
+
+    private synchronized LoginContext login(final String loginContextName) throws LoginException {
+        if (loginContextName == null) {
+            throw new LoginException("loginContext name (JAAS file section header) was null. " +
+                    "Please check your java.security.login.auth.config (=" +
+                    System.getProperty("java.security.login.auth.config") +
+                    ") and your " + ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY + "(=" + 
+                    System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + ")");
+        }
+        LoginContext loginContext = new LoginContext(loginContextName,callbackHandler);
+        loginContext.login();
+        LOG.info("{} successfully logged in.", loginContextName);
+        return loginContext;
+    }
+
+    // c.f. org.apache.hadoop.security.UserGroupInformation.
+    private long getRefreshTime(KerberosTicket tgt) {
+        long start = tgt.getStartTime().getTime();
+        long expires = tgt.getEndTime().getTime();
+        LOG.info("TGT valid starting at:        " + tgt.getStartTime().toString());
+        LOG.info("TGT expires:                  " + tgt.getEndTime().toString());
+        long proposedRefresh = start + (long) ((expires - start) *
+                (TICKET_RENEW_WINDOW + (TICKET_RENEW_JITTER * rng.nextDouble())));
+        if (proposedRefresh > expires) {
+            // proposedRefresh is too far in the future: it's after ticket expires: simply return now.
+            return Time.currentWallTime();
+        }
+        else {
+            return proposedRefresh;
+        }
+    }
+
+    private synchronized KerberosTicket getTGT() {
+        Set<KerberosTicket> tickets = subject.getPrivateCredentials(KerberosTicket.class);
+        for(KerberosTicket ticket: tickets) {
+            KerberosPrincipal server = ticket.getServer();
+            if (server.getName().equals("krbtgt/" + server.getRealm() + "@" + server.getRealm())) {
+                LOG.debug("Client principal is \"" + ticket.getClient().getName() + "\".");
+                LOG.debug("Server principal is \"" + ticket.getServer().getName() + "\".");
+                return ticket;
+            }
+        }
+        return null;
+    }
+
+    private boolean hasSufficientTimeElapsed() {
+        long now = Time.currentElapsedTime();
+        if (now - getLastLogin() < MIN_TIME_BEFORE_RELOGIN ) {
+            LOG.warn("Not attempting to re-login since the last re-login was " +
+                    "attempted less than " + (MIN_TIME_BEFORE_RELOGIN/1000) + " seconds"+
+                    " before.");
+            return false;
+        }
+        // register most recent relogin attempt
+        setLastLogin(now);
+        return true;
+    }
+
+    /**
+     * Returns login object
+     * @return login
+     */
+    private LoginContext getLogin() {
+        return login;
+    }
+
+    /**
+     * Set the login object
+     * @param login
+     */
+    private void setLogin(LoginContext login) {
+        this.login = login;
+    }
+
+    /**
+     * Set the last login time.
+     * @param time the number of milliseconds since the beginning of time
+     */
+    private void setLastLogin(long time) {
+        lastLogin = time;
+    }
+
+    /**
+     * Get the time of the last login.
+     * @return the number of milliseconds since the beginning of time.
+     */
+    private long getLastLogin() {
+        return lastLogin;
+    }
+
+    /**
+     * Re-login a principal. This method assumes that {@link #login(String)} has happened already.
+     * @throws javax.security.auth.login.LoginException on a failure
+     */
+    // c.f. HADOOP-6559
+    private synchronized void reLogin()
+            throws LoginException {
+        if (!isKrbTicket) {
+            return;
+        }
+        LoginContext login = getLogin();
+        if (login  == null) {
+            throw new LoginException("login must be done first");
+        }
+        if (!hasSufficientTimeElapsed()) {
+            return;
+        }
+        LOG.info("Initiating logout for " + principal);
+        synchronized (Login.class) {
+            //clear up the kerberos state. But the tokens are not cleared! As per
+            //the Java kerberos login module code, only the kerberos credentials
+            //are cleared
+            login.logout();
+            //login and also update the subject field of this instance to
+            //have the new credentials (pass it to the LoginContext constructor)
+            login = new LoginContext(loginContextName, getSubject());
+            LOG.info("Initiating re-login for " + principal);
+            login.login();
+            setLogin(login);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c0aa3b3f/zookeeper-common/src/main/java/org/apache/zookeeper/MultiResponse.java
----------------------------------------------------------------------
diff --git a/zookeeper-common/src/main/java/org/apache/zookeeper/MultiResponse.java b/zookeeper-common/src/main/java/org/apache/zookeeper/MultiResponse.java
new file mode 100644
index 0000000..70f7623
--- /dev/null
+++ b/zookeeper-common/src/main/java/org/apache/zookeeper/MultiResponse.java
@@ -0,0 +1,166 @@
+/*
+ * 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;
+
+import org.apache.jute.InputArchive;
+import org.apache.jute.OutputArchive;
+import org.apache.jute.Record;
+import org.apache.zookeeper.proto.CreateResponse;
+import org.apache.zookeeper.proto.MultiHeader;
+import org.apache.zookeeper.proto.SetDataResponse;
+import org.apache.zookeeper.proto.ErrorResponse;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Handles the response from a multi request.  Such a response consists of
+ * a sequence of responses each prefixed by a MultiResponse that indicates
+ * the type of the response.  The end of the list is indicated by a MultiHeader
+ * with a negative type.  Each individual response is in the same format as
+ * with the corresponding operation in the original request list.
+ */
+public class MultiResponse implements Record, Iterable<OpResult> {
+    private List<OpResult> results = new ArrayList<OpResult>();
+
+    public void add(OpResult x) {
+        results.add(x);
+    }
+
+    @Override
+    public Iterator<OpResult> iterator() {
+        return results.iterator();
+    }
+
+    public int size() {
+        return results.size();
+    }
+
+    @Override
+    public void serialize(OutputArchive archive, String tag) throws IOException {
+        archive.startRecord(this, tag);
+
+        int index = 0;
+        for (OpResult result : results) {
+            int err = result.getType() == ZooDefs.OpCode.error ? ((OpResult.ErrorResult)result).getErr() : 0;
+
+            new MultiHeader(result.getType(), false, err).serialize(archive, tag);
+
+            switch (result.getType()) {
+                case ZooDefs.OpCode.create:
+                    new CreateResponse(((OpResult.CreateResult) result).getPath()).serialize(archive, tag);
+                    break;
+                case ZooDefs.OpCode.delete:
+                case ZooDefs.OpCode.check:
+                    break;
+                case ZooDefs.OpCode.setData:
+                    new SetDataResponse(((OpResult.SetDataResult) result).getStat()).serialize(archive, tag);
+                    break;
+                case ZooDefs.OpCode.error:
+                    new ErrorResponse(((OpResult.ErrorResult) result).getErr()).serialize(archive, tag);
+                    break;
+                default:
+                    throw new IOException("Invalid type " + result.getType() + " in MultiResponse");
+            }
+        }
+        new MultiHeader(-1, true, -1).serialize(archive, tag);
+        archive.endRecord(this, tag);
+    }
+
+    @Override
+    public void deserialize(InputArchive archive, String tag) throws IOException {
+        results = new ArrayList<OpResult>();
+
+        archive.startRecord(tag);
+        MultiHeader h = new MultiHeader();
+        h.deserialize(archive, tag);
+        while (!h.getDone()) {
+            switch (h.getType()) {
+                case ZooDefs.OpCode.create:
+                    CreateResponse cr = new CreateResponse();
+                    cr.deserialize(archive, tag);
+                    results.add(new OpResult.CreateResult(cr.getPath()));
+                    break;
+
+                case ZooDefs.OpCode.delete:
+                    results.add(new OpResult.DeleteResult());
+                    break;
+
+                case ZooDefs.OpCode.setData:
+                    SetDataResponse sdr = new SetDataResponse();
+                    sdr.deserialize(archive, tag);
+                    results.add(new OpResult.SetDataResult(sdr.getStat()));
+                    break;
+
+                case ZooDefs.OpCode.check:
+                    results.add(new OpResult.CheckResult());
+                    break;
+
+                case ZooDefs.OpCode.error:
+                    //FIXME: need way to more cleanly serialize/deserialize exceptions
+                    ErrorResponse er = new ErrorResponse();
+                    er.deserialize(archive, tag);
+                    results.add(new OpResult.ErrorResult(er.getErr()));
+                    break;
+
+                default:
+                    throw new IOException("Invalid type " + h.getType() + " in MultiResponse");
+            }
+            h.deserialize(archive, tag);
+        }
+        archive.endRecord(tag);
+    }
+
+    public List<OpResult> getResultList() {
+        return results;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MultiResponse)) return false;
+
+        MultiResponse other = (MultiResponse) o;
+
+        if (results != null) {
+            Iterator<OpResult> i = other.results.iterator();
+            for (OpResult result : results) {
+                if (i.hasNext()) {
+                    if (!result.equals(i.next())) {
+                        return false;
+                    }
+                } else {
+                    return false;
+                }
+            }
+            return !i.hasNext();
+        }
+        else return other.results == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = results.size();
+        for (OpResult result : results) {
+            hash = (hash * 35) + result.hashCode();
+        }
+        return hash;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c0aa3b3f/zookeeper-common/src/main/java/org/apache/zookeeper/MultiTransactionRecord.java
----------------------------------------------------------------------
diff --git a/zookeeper-common/src/main/java/org/apache/zookeeper/MultiTransactionRecord.java b/zookeeper-common/src/main/java/org/apache/zookeeper/MultiTransactionRecord.java
new file mode 100644
index 0000000..801969a
--- /dev/null
+++ b/zookeeper-common/src/main/java/org/apache/zookeeper/MultiTransactionRecord.java
@@ -0,0 +1,160 @@
+/*
+ * 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;
+
+import org.apache.jute.InputArchive;
+import org.apache.jute.OutputArchive;
+import org.apache.jute.Record;
+import org.apache.zookeeper.proto.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Encodes a composite transaction.  In the wire format, each transaction
+ * consists of a single MultiHeader followed by the appropriate request.
+ * Each of these MultiHeaders has a type which indicates
+ * the type of the following transaction or a negative number if no more transactions
+ * are included.
+ */
+public class MultiTransactionRecord implements Record, Iterable<Op> {
+    private List<Op> ops = new ArrayList<Op>();
+
+    public MultiTransactionRecord() {
+    }
+
+    public MultiTransactionRecord(Iterable<Op> ops) {
+        for (Op op : ops) {
+            add(op);
+        }
+    }
+
+    @Override
+    public Iterator<Op> iterator() {
+        return ops.iterator() ;
+    }
+
+    public void add(Op op) {
+        ops.add(op);
+    }
+
+    public int size() {
+        return ops.size();
+    }
+
+    @Override
+    public void serialize(OutputArchive archive, String tag) throws IOException {
+        archive.startRecord(this, tag);
+        int index = 0 ;
+        for (Op op : ops) {
+            MultiHeader h = new MultiHeader(op.getType(), false, -1);
+            h.serialize(archive, tag);
+            switch (op.getType()) {
+               case ZooDefs.OpCode.create:
+                    op.toRequestRecord().serialize(archive, tag);
+                    break;
+                case ZooDefs.OpCode.delete:
+                    op.toRequestRecord().serialize(archive, tag);
+                    break;
+                case ZooDefs.OpCode.setData:
+                    op.toRequestRecord().serialize(archive, tag);
+                    break;
+                case ZooDefs.OpCode.check:
+                    op.toRequestRecord().serialize(archive, tag);
+                    break;
+                default:
+                    throw new IOException("Invalid type of op");
+            }
+        }
+        new MultiHeader(-1, true, -1).serialize(archive, tag);
+        archive.endRecord(this, tag);
+    }
+
+    @Override
+    public void deserialize(InputArchive archive, String tag) throws IOException {
+        archive.startRecord(tag);
+        MultiHeader h = new MultiHeader();
+        h.deserialize(archive, tag);
+
+        while (!h.getDone()) {
+            switch (h.getType()) {
+               case ZooDefs.OpCode.create:
+                    CreateRequest cr = new CreateRequest();
+                    cr.deserialize(archive, tag);
+                    add(Op.create(cr.getPath(), cr.getData(), cr.getAcl(), cr.getFlags()));
+                    break;
+                case ZooDefs.OpCode.delete:
+                    DeleteRequest dr = new DeleteRequest();
+                    dr.deserialize(archive, tag);
+                    add(Op.delete(dr.getPath(), dr.getVersion()));
+                    break;
+                case ZooDefs.OpCode.setData:
+                    SetDataRequest sdr = new SetDataRequest();
+                    sdr.deserialize(archive, tag);
+                    add(Op.setData(sdr.getPath(), sdr.getData(), sdr.getVersion()));
+                    break;
+                case ZooDefs.OpCode.check:
+                    CheckVersionRequest cvr = new CheckVersionRequest();
+                    cvr.deserialize(archive, tag);
+                    add(Op.check(cvr.getPath(), cvr.getVersion()));
+                    break;
+                default:
+                    throw new IOException("Invalid type of op");
+            }
+            h.deserialize(archive, tag);
+        }
+        archive.endRecord(tag);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MultiTransactionRecord)) return false;
+
+        MultiTransactionRecord that = (MultiTransactionRecord) o;
+
+        if (ops != null) {
+            Iterator<Op> other = that.ops.iterator();
+            for (Op op : ops) {
+                boolean hasMoreData = other.hasNext();
+                if (!hasMoreData) {
+                    return false;
+                }
+                Op otherOp = other.next();
+                if (!op.equals(otherOp)) {
+                    return false;
+                }
+            }
+            return !other.hasNext();
+        } else {
+            return that.ops == null;
+        }
+
+    }
+
+    @Override
+    public int hashCode() {
+        int h = 1023;
+        for (Op op : ops) {
+            h = h * 25 + op.hashCode();
+        }
+        return h;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c0aa3b3f/zookeeper-common/src/main/java/org/apache/zookeeper/Op.java
----------------------------------------------------------------------
diff --git a/zookeeper-common/src/main/java/org/apache/zookeeper/Op.java b/zookeeper-common/src/main/java/org/apache/zookeeper/Op.java
new file mode 100644
index 0000000..1bc2061
--- /dev/null
+++ b/zookeeper-common/src/main/java/org/apache/zookeeper/Op.java
@@ -0,0 +1,351 @@
+/*
+ * 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;
+
+import org.apache.jute.Record;
+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.DeleteRequest;
+import org.apache.zookeeper.proto.SetDataRequest;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Represents a single operation in a multi-operation transaction.  Each operation can be a create, update
+ * or delete or can just be a version check.
+ *
+ * Sub-classes of Op each represent each detailed type but should not normally be referenced except via
+ * the provided factory methods.
+ *
+ * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode)
+ * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode, org.apache.zookeeper.AsyncCallback.StringCallback, Object)
+ * @see ZooKeeper#delete(String, int)
+ * @see ZooKeeper#setData(String, byte[], int)
+ */
+public abstract class Op {
+    private int type;
+    private String path;
+
+    // prevent untyped construction
+    private Op(int type, String path) {
+        this.type = type;
+        this.path = path;
+    }
+
+    /**
+     * Constructs a create operation.  Arguments are as for the ZooKeeper method of the same name.
+     * @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.
+     */
+    public static Op create(String path, byte[] data, List<ACL> acl, int flags) {
+        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)
+     *
+     * @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
+     */
+    public static Op create(String path, byte[] data, List<ACL> acl, CreateMode createMode) {
+        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)
+     *
+     * @param path
+     *                the path of the node to be deleted.
+     * @param version
+     *                the expected node version.
+     */
+    public static Op delete(String path, int version) {
+        return new Delete(path, version);
+    }
+
+    /**
+     * Constructs an update operation.  Arguments are as for the ZooKeeper method of the same name.
+     * @see ZooKeeper#setData(String, byte[], int)
+     *
+     * @param path
+     *                the path of the node
+     * @param data
+     *                the data to set
+     * @param version
+     *                the expected matching version
+     */
+    public static Op setData(String path, byte[] data, int version) {
+        return new SetData(path, data, version);
+    }
+
+
+    /**
+     * Constructs an version check operation.  Arguments are as for the ZooKeeper.setData method except that
+     * no data is provided since no update is intended.  The purpose for this is to allow read-modify-write
+     * operations that apply to multiple znodes, but where some of the znodes are involved only in the read,
+     * not the write.  A similar effect could be achieved by writing the same data back, but that leads to
+     * way more version updates than are necessary and more writing in general.
+     *
+     * @param path
+     *                the path of the node
+     * @param version
+     *                the expected matching version
+     */
+    public static Op check(String path, int version) {
+        return new Check(path, version);
+    }
+
+    /**
+     * Gets the integer type code for an Op.  This code should be as from ZooDefs.OpCode
+     * @see ZooDefs.OpCode
+     * @return  The type code.
+     */
+    public int getType() {
+        return type;
+    }
+
+    /**
+     * Gets the path for an Op.
+     * @return  The path.
+     */
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * Encodes an op for wire transmission.
+     * @return An appropriate Record structure.
+     */
+    public abstract Record toRequestRecord() ;
+
+    /**
+     * Reconstructs the transaction with the chroot prefix.
+     * 
+     * @return transaction with chroot.
+     */
+    abstract Op withChroot(String addRootPrefix);
+
+    /**
+     * Performs client path validations.
+     * 
+     * @throws IllegalArgumentException
+     *             if an invalid path is specified
+     * @throws KeeperException.BadArgumentsException
+     *             if an invalid create mode flag is specified
+     */
+    void validate() throws KeeperException {
+        PathUtils.validatePath(path);
+    }
+
+    //////////////////
+    // 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;
+
+        private Create(String path, byte[] data, List<ACL> acl, int flags) {
+            super(ZooDefs.OpCode.create, path);
+            this.data = data;
+            this.acl = acl;
+            this.flags = flags;
+        }
+
+        private Create(String path, byte[] data, List<ACL> acl, CreateMode createMode) {
+            super(ZooDefs.OpCode.create, path);
+            this.data = data;
+            this.acl = acl;
+            this.flags = createMode.toFlag();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Create)) return false;
+
+            Create op = (Create) o;
+
+            boolean aclEquals = true;
+            Iterator<ACL> i = op.acl.iterator();
+            for (ACL acl : op.acl) {
+                boolean hasMoreData = i.hasNext();
+                if (!hasMoreData) {
+                    aclEquals = false;
+                    break;
+                }
+                ACL otherAcl = i.next();
+                if (!acl.equals(otherAcl)) {
+                    aclEquals = false;
+                    break;
+                }
+            }
+            return !i.hasNext() && getType() == op.getType() && Arrays.equals(data, op.data) && flags == op.flags && aclEquals;
+        }
+
+        @Override
+        public int hashCode() {
+            return getType() + getPath().hashCode() + Arrays.hashCode(data);
+        }
+
+        @Override
+        public Record toRequestRecord() {
+            return new CreateRequest(getPath(), data, acl, flags);
+        }
+
+        @Override
+        Op withChroot(String path) {
+            return new Create(path, data, acl, flags);
+        }
+
+        @Override
+        void validate() throws KeeperException {
+            CreateMode createMode = CreateMode.fromFlag(flags);
+            PathUtils.validatePath(getPath(), createMode.isSequential());
+        }
+    }
+
+    public static class Delete extends Op {
+        private int version;
+
+        private Delete(String path, int version) {
+            super(ZooDefs.OpCode.delete, path);
+            this.version = version;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Delete)) return false;
+
+            Delete op = (Delete) o;
+
+            return getType() == op.getType() && version == op.version 
+                   && getPath().equals(op.getPath());
+        }
+
+        @Override
+        public int hashCode() {
+            return getType() + getPath().hashCode() + version;
+        }
+
+        @Override
+        public Record toRequestRecord() {
+            return new DeleteRequest(getPath(), version);
+        }
+
+        @Override
+        Op withChroot(String path) {
+            return new Delete(path, version);
+        }
+    }
+
+    public static class SetData extends Op {
+        private byte[] data;
+        private int version;
+
+        private SetData(String path, byte[] data, int version) {
+            super(ZooDefs.OpCode.setData, path);
+            this.data = data;
+            this.version = version;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof SetData)) return false;
+
+            SetData op = (SetData) o;
+
+            return getType() == op.getType() && version == op.version 
+                   && getPath().equals(op.getPath()) && Arrays.equals(data, op.data);
+        }
+
+        @Override
+        public int hashCode() {
+            return getType() + getPath().hashCode() + Arrays.hashCode(data) + version;
+        }
+
+        @Override
+        public Record toRequestRecord() {
+            return new SetDataRequest(getPath(), data, version);
+        }
+
+        @Override
+        Op withChroot(String path) {
+            return new SetData(path, data, version);
+        }
+    }
+
+    public static class Check extends Op {
+        private int version;
+
+        private Check(String path, int version) {
+            super(ZooDefs.OpCode.check, path);
+            this.version = version;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Check)) return false;
+
+            Check op = (Check) o;
+
+            return getType() == op.getType() && getPath().equals(op.getPath()) && version == op.version;
+        }
+
+        @Override
+        public int hashCode() {
+            return getType() + getPath().hashCode() + version;
+        }
+
+        @Override
+        public Record toRequestRecord() {
+            return new CheckVersionRequest(getPath(), version);
+        }
+
+        @Override
+        Op withChroot(String path) {
+            return new Check(path, version);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c0aa3b3f/zookeeper-common/src/main/java/org/apache/zookeeper/OpResult.java
----------------------------------------------------------------------
diff --git a/zookeeper-common/src/main/java/org/apache/zookeeper/OpResult.java b/zookeeper-common/src/main/java/org/apache/zookeeper/OpResult.java
new file mode 100644
index 0000000..45331d1
--- /dev/null
+++ b/zookeeper-common/src/main/java/org/apache/zookeeper/OpResult.java
@@ -0,0 +1,185 @@
+/*
+ * 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;
+
+
+import org.apache.zookeeper.data.Stat;
+
+/**
+ * Encodes the result of a single part of a multiple operation commit.
+ */
+public abstract class OpResult {
+    private int type;
+
+    private OpResult(int type) {
+        this.type = type;
+    }
+
+    /**
+     * Encodes the return type as from ZooDefs.OpCode.  Can be used
+     * to dispatch to the correct cast needed for getting the desired
+     * additional result data.
+     * @see ZooDefs.OpCode
+     * @return an integer identifying what kind of operation this result came from.
+     */
+    public int getType() {
+        return type;
+    }
+
+    /**
+     * A result from a create operation.  This kind of result allows the
+     * path to be retrieved since the create might have been a sequential
+     * create.
+     */
+    public static class CreateResult extends OpResult {
+        private String path;
+
+        public CreateResult(String path) {
+            super(ZooDefs.OpCode.create);
+            this.path = path;
+        }
+
+        public String getPath() {
+            return path;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof CreateResult)) return false;
+
+            CreateResult other = (CreateResult) o;
+            return getType() == other.getType() && path.equals(other.getPath());
+        }
+
+        @Override
+        public int hashCode() {
+            return getType() * 35 + path.hashCode();
+        }
+    }
+
+    /**
+     * A result from a delete operation.  No special values are available.
+     */
+    public static class DeleteResult extends OpResult {
+        public DeleteResult() {
+            super(ZooDefs.OpCode.delete);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof DeleteResult)) return false;
+
+            DeleteResult opResult = (DeleteResult) o;
+            return getType() == opResult.getType();
+        }
+
+        @Override
+        public int hashCode() {
+            return getType();
+        }
+    }
+
+    /**
+     * A result from a setData operation.  This kind of result provides access
+     * to the Stat structure from the update.
+     */
+    public static class SetDataResult extends OpResult {
+        private Stat stat;
+
+        public SetDataResult(Stat stat) {
+            super(ZooDefs.OpCode.setData);
+            this.stat = stat;
+        }
+
+        public Stat getStat() {
+            return stat;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof SetDataResult)) return false;
+
+            SetDataResult other = (SetDataResult) o;
+            return getType() == other.getType() && stat.getMzxid() == other.stat.getMzxid();
+        }
+
+        @Override
+        public int hashCode() {
+            return (int) (getType() * 35 + stat.getMzxid());
+        }
+    }
+
+    /**
+     * A result from a version check operation.  No special values are available.
+     */
+    public static class CheckResult extends OpResult {
+        public CheckResult() {
+            super(ZooDefs.OpCode.check);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof CheckResult)) return false;
+
+            CheckResult other = (CheckResult) o;
+            return getType() == other.getType();
+        }
+
+        @Override
+        public int hashCode() {
+            return getType();
+        }
+    }
+
+    /**
+     * An error result from any kind of operation.  The point of error results
+     * is that they contain an error code which helps understand what happened.
+     * @see KeeperException.Code
+     *
+     */
+    public static class ErrorResult extends OpResult {
+        private int err;
+
+        public ErrorResult(int err) {
+            super(ZooDefs.OpCode.error);
+            this.err = err;
+        }
+
+        public int getErr() {
+            return err;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof ErrorResult)) return false;
+
+            ErrorResult other = (ErrorResult) o;
+            return getType() == other.getType() && err == other.getErr();
+        }
+
+        @Override
+        public int hashCode() {
+            return getType() * 35 + err;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/c0aa3b3f/zookeeper-common/src/main/java/org/apache/zookeeper/Quotas.java
----------------------------------------------------------------------
diff --git a/zookeeper-common/src/main/java/org/apache/zookeeper/Quotas.java b/zookeeper-common/src/main/java/org/apache/zookeeper/Quotas.java
new file mode 100644
index 0000000..b82e339
--- /dev/null
+++ b/zookeeper-common/src/main/java/org/apache/zookeeper/Quotas.java
@@ -0,0 +1,68 @@
+/**
+ * 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;
+
+/**
+ * this class manages quotas
+ * and has many other utils
+ * for quota
+ */
+public class Quotas {
+
+    /** the zookeeper nodes that acts as the management and status node **/
+    public static final String procZookeeper = "/zookeeper";
+
+    /** the zookeeper quota node that acts as the quota
+     * management node for zookeeper */
+    public static final String quotaZookeeper = "/zookeeper/quota";
+
+    /**
+     * the limit node that has the limit of
+     * a subtree
+     */
+    public static final String limitNode = "zookeeper_limits";
+
+    /**
+     * the stat node that monitors the limit of
+     * a subtree.
+     */
+    public static final String statNode = "zookeeper_stats";
+
+    /**
+     * return the quota path associated with this
+     * prefix
+     * @param path the actual path in zookeeper.
+     * @return the limit quota path
+     */
+    public static String quotaPath(String path) {
+        return quotaZookeeper + path +
+        "/" + limitNode;
+    }
+
+    /**
+     * return the stat quota path associated with this
+     * prefix.
+     * @param path the actual path in zookeeper
+     * @return the stat quota path
+     */
+    public static String statPath(String path) {
+        return quotaZookeeper + path + "/" +
+        statNode;
+    }
+}


Mime
View raw message