[zookeeper] 01/07: New upstream version 3.4.10

Tony Mancill tmancill at moszumanska.debian.org
Sat Jun 3 18:28:52 UTC 2017


This is an automated email from the git hooks/post-receive script.

tmancill pushed a commit to branch experimental
in repository zookeeper.

commit 6376997fdf718d70a064843a231c6d804cf0e8a8
Author: tony mancill <tmancill at debian.org>
Date:   Thu Jun 1 08:46:30 2017 -0700

    New upstream version 3.4.10
---
 CHANGES.txt                                        | 2577 --------------------
 NOTICE.txt                                         |   96 +-
 bin/zkCli.cmd                                      |   48 +-
 bin/zkEnv.cmd                                      |   98 +-
 bin/zkServer.cmd                                   |   48 +-
 build.xml                                          |   96 +-
 ivy.xml                                            |   38 +-
 ivysettings.xml                                    |    9 +-
 .../{zookeeper_3.4.9.xml => zookeeper_3.4.10.xml}  |   38 +-
 src/NOTICE.txt                                     |   96 +-
 src/c/NOTICE.txt                                   |    2 +-
 src/c/configure.ac                                 |    2 +-
 src/c/include/winconfig.h                          |    6 +-
 src/c/include/zookeeper.h                          |   14 +-
 src/c/include/zookeeper_version.h                  |    2 +-
 src/c/src/load_gen.c                               |    2 +-
 src/c/src/recordio.c                               |   12 +-
 src/c/tests/TestDriver.cc                          |    2 +-
 src/c/zookeeper.sln                                |   50 +-
 src/contrib/build-contrib.xml                      |    2 +-
 src/contrib/zkfuse/src/event.h                     |    2 +-
 src/contrib/zkfuse/src/zkadapter.cc                |    2 +-
 src/contrib/zkpython/src/c/pyzk_docstrings.h       |    6 +-
 src/contrib/zkpython/src/c/zookeeper.c             |    4 +-
 src/contrib/zktreeutil/src/ZkAdaptor.cc            |    2 +-
 src/contrib/zktreeutil/src/ZkAdaptor.h             |    2 +-
 src/contrib/zktreeutil/src/ZkTreeUtil.cc           |    2 +-
 .../inspector/manager/ZooInspectorManagerImpl.java |    4 +-
 src/contrib/zooinspector/zooInspector.cmd          |   34 +-
 src/docs/src/documentation/conf/cli.xconf          |    2 +-
 .../documentation/content/xdocs/zookeeperAdmin.xml |   44 +
 .../content/xdocs/zookeeperProgrammers.xml         |    2 +-
 src/docs/src/documentation/skinconf.xml            |    2 +-
 .../{zookeeper_3.4.9.xml => zookeeper_3.4.10.xml}  |   38 +-
 src/java/main/org/apache/zookeeper/Login.java      |    7 +-
 .../zookeeper/SaslClientCallbackHandler.java       |  104 +
 src/java/main/org/apache/zookeeper/Version.java    |   13 +-
 .../main/org/apache/zookeeper/ZooKeeperMain.java   |    2 +-
 .../zookeeper/client/ZooKeeperSaslClient.java      |  161 +-
 .../main/org/apache/zookeeper/server/DataNode.java |    4 +-
 .../main/org/apache/zookeeper/server/DataTree.java |   48 +-
 .../zookeeper/server/DatadirCleanupManager.java    |    2 +-
 .../org/apache/zookeeper/server/NIOServerCnxn.java |  134 +-
 .../apache/zookeeper/server/NettyServerCnxn.java   |   60 +-
 .../zookeeper/server/PrepRequestProcessor.java     |    3 +-
 .../org/apache/zookeeper/server/PurgeTxnLog.java   |   51 +-
 .../org/apache/zookeeper/server/ServerCnxn.java    |   94 +-
 .../org/apache/zookeeper/server/ServerConfig.java  |    7 +-
 .../apache/zookeeper/server/SnapshotFormatter.java |    6 +-
 .../zookeeper/server/ZooKeeperSaslServer.java      |  114 +-
 .../apache/zookeeper/server/ZooKeeperServer.java   |   18 +-
 .../zookeeper/server/ZooKeeperServerMain.java      |    2 +-
 .../apache/zookeeper/server/ZooKeeperThread.java   |    2 +-
 .../main/org/apache/zookeeper/server/ZooTrace.java |   12 +-
 .../server/auth/SaslServerCallbackHandler.java     |   12 +-
 .../zookeeper/server/persistence/FileSnap.java     |   12 +-
 .../server/persistence/FileTxnSnapLog.java         |   16 +-
 .../server/quorum/FastLeaderElection.java          |    2 +
 .../apache/zookeeper/server/quorum/Follower.java   |   10 +-
 .../zookeeper/server/quorum/FollowerBean.java      |    7 +-
 .../zookeeper/server/quorum/FollowerMXBean.java    |    5 +
 .../org/apache/zookeeper/server/quorum/Leader.java |   17 +-
 .../apache/zookeeper/server/quorum/LeaderBean.java |    4 +
 .../zookeeper/server/quorum/LeaderMXBean.java      |    5 +
 .../apache/zookeeper/server/quorum/Learner.java    |   58 +-
 .../zookeeper/server/quorum/LearnerHandler.java    |   27 +-
 .../apache/zookeeper/server/quorum/Observer.java   |    8 +-
 .../zookeeper/server/quorum/QuorumCnxManager.java  |  348 ++-
 .../apache/zookeeper/server/quorum/QuorumPeer.java |  215 +-
 .../zookeeper/server/quorum/QuorumPeerConfig.java  |   48 +-
 .../zookeeper/server/quorum/QuorumPeerMain.java    |   39 +-
 .../NullQuorumAuthLearner.java}                    |   21 +-
 .../NullQuorumAuthServer.java}                     |   22 +-
 .../zookeeper/server/quorum/auth/QuorumAuth.java   |   96 +
 .../QuorumAuthLearner.java}                        |   24 +-
 .../server/quorum/auth/QuorumAuthServer.java}      |   32 +-
 .../server/quorum/auth/SaslQuorumAuthLearner.java  |  233 ++
 .../server/quorum/auth/SaslQuorumAuthServer.java   |  182 ++
 .../auth/SaslQuorumServerCallbackHandler.java}     |   98 +-
 .../org/apache/zookeeper/util/SecurityUtils.java   |  298 +++
 .../org/apache/zookeeper/version/util/VerGen.java  |   29 +-
 src/java/test/bin/test-github-pr.sh                |  616 +++++
 src/java/test/checkstyle.xml                       |   29 +-
 .../test/data/kerberos/minikdc-krb5.conf}          |   19 +-
 src/java/test/data/kerberos/minikdc.ldiff          |   52 +
 .../org/apache/zookeeper/ServerConfigTest.java     |   74 +
 src/java/test/org/apache/zookeeper/VerGenTest.java |    2 +-
 src/java/test/org/apache/zookeeper/ZKTestCase.java |    4 +
 .../test/org/apache/zookeeper/ZooKeeperTest.java   |   23 +
 .../org/apache/zookeeper/server/DataNodeTest.java  |   65 +
 .../apache/zookeeper/server/NIOServerCnxnTest.java |   39 +-
 .../org/apache/zookeeper/server/PurgeTxnTest.java  |  198 +-
 .../zookeeper/server/SessionTrackerTest.java       |    2 +-
 .../zookeeper/server/ZooKeeperServerMainTest.java  |  112 +-
 .../server/ZooKeeperServerStartupTest.java         |  302 +++
 .../zookeeper/server/quorum/CnxManagerTest.java    |   17 +-
 .../quorum/FLEBackwardElectionRoundTest.java       |    4 +-
 .../server/quorum/FLECompatibilityTest.java        |    4 +-
 .../zookeeper/server/quorum/FLEDontCareTest.java   |   10 +-
 .../server/quorum/FLELostMessageTest.java          |    2 +-
 .../zookeeper/server/quorum/LearnerTest.java       |    4 +-
 .../server/quorum/QuorumCnxManagerTest.java        |  925 +++++++
 .../server/quorum/QuorumPeerTestBase.java          |   65 +-
 .../apache/zookeeper/server/quorum/Zab1_0Test.java |   73 +-
 .../quorum/auth/KerberosSecurityTestcase.java      |  120 +
 .../server/quorum/auth/KerberosTestUtils.java      |   76 +
 .../zookeeper/server/quorum/auth/MiniKdc.java      |  574 +++++
 .../zookeeper/server/quorum/auth/MiniKdcTest.java  |  184 ++
 .../server/quorum/auth/QuorumAuthTestBase.java     |  248 ++
 .../server/quorum/auth/QuorumAuthUpgradeTest.java  |  239 ++
 .../server/quorum/auth/QuorumDigestAuthTest.java   |  380 +++
 .../server/quorum/auth/QuorumKerberosAuthTest.java |  115 +
 .../auth/QuorumKerberosHostBasedAuthTest.java      |  192 ++
 .../test/org/apache/zookeeper/test/ClientBase.java |   28 +
 .../apache/zookeeper/test/ClientPortBindTest.java  |   15 +-
 .../apache/zookeeper/test/FLEPredicateTest.java    |    2 +-
 .../test/FourLetterWordsWhiteListTest.java         |  252 ++
 .../zookeeper/test/HierarchicalQuorumTest.java     |   40 +-
 .../test/org/apache/zookeeper/test/JMXEnv.java     |   48 +
 .../org/apache/zookeeper/test/WatcherTest.java     |   18 +-
 src/lastRevision.bat                               |   45 +-
 src/lastRevision.sh                                |    2 +-
 src/pom.template                                   |   41 +
 src/zookeeper.jute                                 |    5 +
 zookeeper-3.4.10.jar.asc                           |   17 +
 zookeeper-3.4.10.jar.md5                           |    1 +
 zookeeper-3.4.10.jar.sha1                          |    1 +
 zookeeper-3.4.9.jar.asc                            |   17 -
 zookeeper-3.4.9.jar.md5                            |    1 -
 zookeeper-3.4.9.jar.sha1                           |    1 -
 130 files changed, 7788 insertions(+), 3611 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
deleted file mode 100644
index 40e0428..0000000
--- a/CHANGES.txt
+++ /dev/null
@@ -1,2577 +0,0 @@
-Release 3.4.9 - 2016-08-23
-
-Backward compatible changes:
-
-BUGFIXES:
-
-  ZOOKEEPER-2375: Prevent multiple initialization of login object in each
-  ZooKeeperSaslClient instance (yuemeng via rakeshr)
-
-  ZOOKEEPER-2379: recent commit broke findbugs qabot check
-  (rakeshr via cnauroth)
-
-  ZOOKEEPER-2294 Ant target generate-clover-reports is broken
-  (charlie via phunt)
-
-  ZOOKEEPER-2378 upgrade ivy to recent version (phunt)
-
-  ZOOKEEPER-2373 Licenses section missing from pom file (phunt)
-
-  ZOOKEEPER-2133 zkperl: Segmentation fault if getting a node with
-  null value (Botond Hejj via phunt)
-
-  ZOOKEEPER-2283 traceFile property is not used in the ZooKeeper, it
-  should be removed from documentation (Arshad Mohammad via phunt)
-
-  ZOOKEEPER-2385 Zookeeper trunk build is failing on windows
-  (Arshad Mohammad via phunt)
-
-  ZOOKEEPER-2195 fsync.warningthresholdms in zoo.cfg not working
-  (Biju Nair via phunt)
-  
-  ZOOKEEPER-2141 ACL cache in DataTree never removes entries
-  (Adam Milne-Smith via camille)
-
-  ZOOKEEPER-2423: Upgrade Netty version due to security vulnerability
-  (CVE-2014-3488) (Michael Han via phunt)
-
-  ZOOKEEPER-2405: getTGT() in Login.java mishandles confidential
-  information (Michael Han via phunt)
-
-  ZOOKEEPER-2477: documentation should refer to Java cli shell and not
-  C cli shell (Abraham Fine via phunt)
-
-  ZOOKEEPER-1256: ClientPortBindTest is failing on Mac OS X
-  (Camille via phunt)
-
-  ZOOKEEPER-2498: Potential resource leak in C client when processing
-  unexpected / out of order response (Michael Han via rgs)
-
-  Fix command handling in the C client shell (phunt via fpj)
-
-  ZOOKEEPER-2452: Back-port ZOOKEEPER-1460 to 3.4 for IPv6 literal
-  address support. (Abraham Fine via cnauroth)
-
-  ZOOKEEPER-2247: Zookeeper service becomes unavailable when leader
-  fails to write transaction log (Rakesh via fpj)
-
-IMPROVEMENTS:
-
-  ZOOKEEPER-2240 Make the three-node minimum more explicit in 
-  documentation and on website (Shawn Heisey and Arshad Mohammad via phunt)
-
-  ZOOKEEPER-2514: Simplify releasenotes creation for 3.4 branch -
-  consistent with newer branches. (phunt via cnauroth)
-
-Release 3.4.8 - 2016-02-05
-
-Backward compatible changes:
-
-BUGFIXES:
-
-  ZOOKEEPER-1929: std::length_error on update children
-  (Charles Strahan via rgs)
-
-  ZOOKEEPER-2211: PurgeTxnLog does not correctly purge when snapshots and
-  logs are at different locations (Arshad Mohammad via rgs)
-
-  ZOOKEEPER-2311: assert in setup_random
-  (Marshall McMullen via rgs)
-
-  ZOOKEEPER-2295: TGT refresh time logic is wrong
-  (Arshad Mohammad via rgs)
-
-  ZOOKEEPER-2281: ZK Server startup fails if there are spaces in the JAVA_HOME
-  path (Neha Bathra via cnauroth)
-
-  ZOOKEEPER-2340: JMX is disabled even if JMXDISABLE is false
-  (Arshad Mohammad via rgs)
-
-  ZOOKEEPER-2229: Several four-letter words are undocumented
-  (Chris Nauroth via rgs)
-
-  ZOOKEEPER-2347: Deadlock shutting down zookeeper
-  (Rakesh R via rgs)
-
-  ZOOKEEPER-2360: Update commons collections version used by tests/releaseaudit
-  (phunt via cnauroth)
-
-  ZOOKEEPER-2243: Supported platforms is completely out of date (cnauroth)
-
-Release 3.4.7 - 2015-11-08
-
-Backward compatible changes:
-
-BUGFIXES:
-
-  ZOOKEEPER-1888. ZkCli.cmd commands fail with "'java' is not recognized as an
-  internal or external command" (Ivan Mitic via michim)
-
-  ZOOKEEPER-1878. Inconsistent behavior in autocreation of dataDir and
-  dataLogDir (Rakesh R via michim)
-
-  ZOOKEEPER-1901. [JDK8] Sort children for comparison in AsyncOps tests
-  (Andrew Purtell via michim)
-
-  ZOOKEEPER-1906. zkpython: invalid data in GetData for empty node
-  (Nikita Vetoshkin via michim)
-
-  ZOOKEEPER-1897. ZK Shell/Cli not processing commands (stack via michim)
-
-  ZOOKEEPER-1913. Invalid manifest files due to bogus revision property value
-  (Raul Gutierrez Segales via michim)
-
-  ZOOKEEPER-1911. REST contrib module does not include all required files when
-  packaged (Sean Mackrory via michim)
-
-  ZOOKEEPER-1926. Unit tests should only use build/test/data for data (Enis
-  Soztutar via michim)
-
-  ZOOKEEPER-1062. Net-ZooKeeper: Net::ZooKeeper consumes 100% cpu on wait
-  (Botond Hejj via michim)
-
-  ZOOKEEPER-1797. PurgeTxnLog may delete data logs during roll (Rakesh R via
-  michim)
-
-  ZOOKEEPER-1945. deb - zkCli.sh, zkServer.sh and zkEnv.sh regression caused
-  by ZOOKEEPER-1663 (Mark Flickinger via fpj) 
-
-  ZOOKEEPER-1939. ReconfigRecoveryTest.testNextConfigUnreachable is
-  failing (Rakesh R via phunt)
-
-  ZOOKEEPER-1222. getACL should only call DataTree.copyStat when passed in
-  stat is not null (Michi Mutsuzaki via rakeshr)
-
-  ZOOKEEPER-2146 BinaryInputArchive readString should check length before
-  allocating memory (Hongchao Deng via michim)
-
-  ZOOKEEPER-2039. Jute compareBytes incorrect comparison index (Ian Dimayuga via fpj)
-
-  ZOOKEEPER-2047 testTruncationNullLog fails on windows (flavio via rakeshr)
-
-  ZOOKEEPER-2026 Startup order in ServerCnxnFactory-ies is wrong (Stevo Slavic via rakeshr)
-
-  ZOOKEEPER-2049 Yosemite build failure: htonll conflict (Till Toenshoff via
-  michim)
-
-  ZOOKEEPER-2052 Unable to delete a node when the node has no children 
-  (Hongchao Deng and Yip Ng via rakeshr)
- 
-  ZOOKEEPER-2060 Trace bug in NettyServerCnxnFactory (Ian via fpj)
-
-  ZOOKEEPER-2064 Prevent resource leak in various classes (Ted Yu via fpj)
-
-  ZOOKEEPER-1949 recipes jar not included in the distribution package (Rakesh R
-  via michim)
-
-  ZOOKEEPER-2114 jute generated allocate_* functions are not externally visible
-  (Tim Crowder via michim)
-
-  ZOOKEEPER-2073 Memory leak on zookeeper_close (Dave Gosselin via michim)
-
-  ZOOKEEPER-2056 Zookeeper 3.4.x and 3.5.0-alpha is not OSGi compliant
-  (Deiwin Sarjas via rgs)
-
-  ZOOKEEPER-2174 JUnit4ZKTestRunner logs test failure for all exceptions even
-  if the test method is annotated with an expected exception (Chris Nauroth
-  via rgs)
-
-  ZOOKEEPER-1077: C client lib doesn't build on Solaris (Chris Nauroth via rgs)
-
-  ZOOKEEPER-2186 QuorumCnxManager#receiveConnection may crash with random input
-  (rgs via michim)
-
-  ZOOKEEPER-2179: Typo in Watcher.java (Archana T via rgs)
-
-  ZOOKEEPER-2096: C client builds with incorrect error codes in VisualStudio 2010+
-  (Vitaly Stakhovsky via rgs)
-
-  ZOOKEEPER-2194: Let DataNode.getChildren() return an unmodifiable view of its children set
-  (Hitoshi Mitake via rgs)
-
-  ZOOKEEPER-2201: Network issues can cause cluster to hang due to near-deadlock
-  (Donny Nadolny via rgs)
-
-  ZOOKEEPER-2213: Empty path in Set crashes server and prevents restart
-  (Hongchao Deng via rgs)
-
-  ZOOKEEPER-706: Large numbers of watches can cause session re-establishment to fail
-  (Chris Thunes via rgs)
-
-  ZOOKEEPER-602: log all exceptions not caught by ZK threads
-  (Rakesh R via rgs)
-
-  ZOOKEEPER-2224: Four letter command hangs when network is slow
-  (Arshad Mohammad via rgs)
-
-  ZOOKEEPER-2235: License update (fpj via michim)
-
-  ZOOKEEPER-2239: JMX State from LocalPeerBean incorrect
-  (Kevin Lee via rgs)
-
-  ZOOKEEPER-1927: zkServer.sh fails to read dataDir (and others)
-  from zoo.cfg on Solaris 10 (grep issue, manifests as FAILED TO WRITE PID)
-  (Chris Nauroth via rgs)
-
-  ZOOKEEPER-2033: zookeeper follower fails to start after
-  a restart immediately following a new epoch (Asad Saeed via fpj)
-
-  ZOOKEEPER-2256: Zookeeper is not using specified JMX port in zkEnv.sh
-  (Arshad Mohammad via rakeshr)
- 
-  ZOOKEEPER-1506: Re-try DNS hostname -> IP resolution if node connection
-  fails (Robert Thille via fpj)
-
-  ZOOKEEPER-2279: QuorumPeer loadDataBase() error message is incorrect
-  (Arshad Mohammad via rakeshr)
-
-  ZOOKEEPER-1803: Add description for pzxid in programmer's guide
-  (Arshad Mohammad via rakeshr)
-
-  ZOOKEEPER-2253: C asserts ordering of ping requests, while Java client does not
-  (Chris Chen via rgs)
-
-  ZOOKEEPER-2268: Zookeeper doc creation fails on windows
-  (Arshad Mohammad via cnauroth)
-
-  ZOOKEEPER-2296: compilation broken for 3.4
-  (Raul Gutierrez Segales via cnauroth)
-
-  ZOOKEEPER-1029: C client bug in zookeeper_init (if bad hostname is given)
-  (fpj via cnauroth)
-
-  ZOOKEEPER-2142: JMX ObjectName is incorrect for observers (Edward Ribeiro
-  via michim)
-
-  ZOOKEEPER-1853: zkCli.sh can't issue a CREATE command containing
-  spaces in the data (Jun Gong via rgs)
-
-  ZOOKEEPER-2227: stmk four-letter word fails execution at server while reading
-  trace mask argument (Chris Nauroth via rgs)
-
-IMPROVEMENTS:
-
-  ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for
-  source and text files (Raja Aluri via michim)
-
-  ZOOKEEPER-657. Cut down the running time of ZKDatabase corruption
-  (Michi Mutsuzaki via rakeshr)
-
-  ZOOKEEPER-1746. AsyncCallback.*Callback don't have any Javadoc
-  (Hongchao Deng via phunt)
-
-  ZOOKEEPER-1917. Apache Zookeeper logs cleartext admin passwords (fpj via michim)
-
-  ZOOKEEPER-1948 Enable JMX remote monitoring (Biju Nair via rakeshr)
-
-  ZOOKEEPER-2126 Improve exit log messsage of EventThread and SendThread by
-  adding SessionId (surendra singh lilhore via rakeshr)
-
-  ZOOKEEPER-2124 Allow Zookeeper version string to have underscore '_'
-  (Chris Nauroth via michim)
-
-  ZOOKEEPER-2205: Log type of unexpected quorum packet in learner handler loop
-  (Hitoshi Mitake via rgs)
-
-  ZOOKEEPER-1907 Improve Thread handling
-  (Rakesh R via hdeng)
-
-  ZOOKEEPER-2270: Allow MBeanRegistry to be overridden for better unit tests
-  (Jordan Zimmerman via rgs)
-
-  ZOOKEEPER-2040: Server to log underlying cause of SASL connection problems.
-  (Steve Loughran via cnauroth)
-
-  ZOOKEEPER-2245: SimpleSysTest test cases fails (Arshad Mohammad via rakeshr)
-
-  ZOOKEEPER-2315: Change client connect zk service timeout log level from Info
-  to Warn level (Lin Yiqun via rgs)
-
-  ZOOKEEPER-2240: Make the three-node minimum more explicit in documentation
-  and on website (Shawn Heisey via rgs)
-
-NEW FEATURES:
-
-  ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr)
-
-Release 3.4.6 - 2014-03-10 
-
-Backward compatible changes:
-
-BUGFIXES:
-
-  ZOOKEEPER-1900. NullPointerException in truncate (Camille Fournier)
-
-  ZOOKEEPER-1474. Cannot build Zookeeper with IBM Java: use of Sun
-  MXBean classes (Adalberto Medeiros via phunt)
-
-  ZOOKEEPER-1596. Zab1_0Test should ensure that the file is closed
-  (Enis Soztutar via phunt)
-
-  ZOOKEEPER-1513. "Unreasonable length" exception while starting a
-  server (Skye W-M via phunt)
-
-  ZOOKEEPER-1581. change copyright in notice to 2012 (breed via phunt)
-
-  ZOOKEEPER-753. log4j dependency in the pom needs to have exclusion
-  lists (Sean Busbey via phunt)
-
-  ZOOKEEPER-1553. Findbugs configuration is missing some dependencies
-  (Sean Busbey via phunt)
-
-  ZOOKEEPER-1478. Small bug in QuorumTest.testFollowersStartAfterLeader( )
-  (Alexander Shraer via fpj, breed, phunt)
-
-  ZOOKEEPER-1387. Wrong epoch file created
-  (Benjamin Busjaeger via breed, phunt)
-
-  ZOOKEEPER-1578. org.apache.zookeeper.server.quorum.Zab1_0Test failed due to
-  hard code with 33556 port. (Li Ping via mahadev)
-
-  ZOOKEEPER-1334. Zookeeper 3.4.x is not OSGi compliant - MANIFEST.MF
-  is flawed (Claus Ibsen via phunt)
-  
-  ZOOKEEPER-1535. ZK Shell/Cli re-executes last command on exit 
-  (Edward Ribeiro via camille)
-
-  ZOOKEEPER-1495. ZK client hangs when using a function not available
-  on the server. (Skye W-M via phunt)
-
-  ZOOKEEPER-1613. The documentation still points to 2008 in the
-  copyright notice (Edward Ribeiro via phunt)
-
-  ZOOKEEPER-1562. Memory leaks in zoo_multi API
-  (Deepak Jagtap via phunt)
-
-  ZOOKEEPER-1645. ZooKeeper OSGi package imports not complete
-  (Arnoud Glimmerveen via phunt)
-
-  ZOOKEEPER-1648. Fix WatcherTest in JDK7
-  (Thawan Kooburat via phunt)
-
-  ZOOKEEPER-1606. intermittent failures in ZkDatabaseCorruptionTest on
-  jenkins (lixiaofeng via phunt)
-
-  ZOOKEEPER-1647. OSGi package import/export changes not applied to
-  bin-jar (Arnoud Glimmerveen via phunt)
-
-  ZOOKEEPER-1633. Introduce a protocol version to connection initiation
-  message (Alexander Shraer via michim)
-
-  ZOOKEEPER-1697.  large snapshots can cause continuous quorum failure
-  (phunt via fpj)
-
-  ZOOKEEPER-1706. Typo in Double Barriers example (Jingguo Yao via fpj)
-  
-  ZOOKEEPER-1642. Leader Loading Database Twice (fpj via camille)
-  
-  ZOOKEEPER-1663. scripts don't work when path contains spaces (Amichai Rothman via camille)
-
-  ZOOKEEPER-1714 perl client segfaults if ZOO_READ_ACL_UNSAFE constant is used
-  (Botond Hejj via camille)
-  
-  ZOOKEEPER-1719. zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663
-  (Marshall McMullen via camille)
-  
-  ZOOKEEPER-1702. ZooKeeper client may write operation packets before
-  receiving successful response to connection request, can cause TCP
-  RST (Chris Nauroth via phunt)
-
-  ZOOKEEPER-1629. testTransactionLogCorruption occasionally fails. (shralex via camille)
-
-  ZOOKEEPER-1731. Unsynchronized access to ServerCnxnFactory.connectionBeans results in 
-  deadlock. (Dave Latham via camille)
-
-  ZOOKEEPER-1713. wrong time calculation in zkfuse.cc (german via fpj)
-
-  ZOOKEEPER-1379. 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary (edward via fpj)
-
-  ZOOKEEPER-1448: Node+Quota creation in transaction log can crash leader startup (Botond Hejj via fpj) 
- 
-  ZOOKEEPER-1664. Kerberos auth doesn't work with native platform GSS integration. (Boaz Kelmer via camille) 
-
-  ZOOKEEPER-1750. Race condition producing NPE in NIOServerCnxn.toString
-  (Rakesh R via michim)
-
-  ZOOKEEPER-1754. Read-only server allows to create znode (Rakesh R via fpj)
-
-  ZOOKEEPER-1751. ClientCnxn#run could miss the second ping or connection get
-  dropped before a ping. (Jeffrey Zhong  via mahadev)
-
-  ZOOKEEPER-1657. Increased CPU usage by unnecessary SASL checks (Philip K. Warren via fpj)
-
-  ZOOKEEPER-1753. ClientCnxn is not properly releasing the resources, 
-                  which are used to ping RwServer (Rakesh R via fpj)
-
-  ZOOKEEPER-1096. Leader communication should listen on specified IP, not wildcard address (Jared Cantwell, German Blanco via fpj)
-
-  ZOOKEEPER-87. Follower does not shut itself down if its too far behind the leader. (German Blanco via fpj)
-
-  ZOOKEEPER-1603. StaticHostProviderTest testUpdateClientMigrateOrNot hangs (fpj)
-
-  ZOOKEEPER-1696. Fail to run zookeeper client on Weblogic application server.
-  (Jeffrey Zhong via mahadev)
-
-  ZOOKEEPER-1770. NullPointerException in SnapshotFormatter
-  (Germán Blanco via phunt)
-
-  ZOOKEEPER-732. Improper translation of error into Python exception (Andrei Savu, Lei Zhang, fpj via fpj)
-
-  ZOOKEEPER-1551. Observers ignore txns that come after snapshot and UPTODATE
-  (thawan, fpj via thawan)
-
-  ZOOKEEPER-1774. QuorumPeerMainTest fails consistently with
-  "complains about host" assertion failure (phunt)
-
-  ZOOKEEPER-877. zkpython does not work with python3.1
-  (Daniel Enman via phunt)
-  
-  ZOOKEEPER-1624. PrepRequestProcessor abort multi-operation incorrectly. (thawan via camille)
-
-  ZOOKEEPER-1610. Some classes are using == or != to compare
-  Long/String objects instead of .equals() (Edward Ribeiro via phunt)
-
-  ZOOKEEPER-1646. mt c client tests fail on Ubuntu Raring (phunt)
-
-  ZOOKEEPER-1558. Leader should not snapshot uncommitted state (fpj)
-
-  ZOOKEEPER-1732. ZooKeeper server unable to join established
-  ensemble (German Blanco via fpj)
-
-  ZOOKEEPER-1667. Watch event isn't handled correctly when 
-  a client reestablish to a server (jacky007, fpj via fpj)
-
-  ZOOKEEPER-1799. SaslAuthFailDesignatedClientTest.testAuth fails
-  frequently on SUSE (Jeffrey Zhong via phunt)
-
-  ZOOKEEPER-1557. jenkins jdk7 test failure in
-  testBadSaslAuthNotifiesWatch (Eugene Koontz via phunt)
-
-  ZOOKEEPER-1744. clientPortAddress breaks "zkServer.sh status"
-  (Nick Ohanian via phunt)
-
-  ZOOKEEPER-1798. Fix race condition in testNormalObserverRun
-  (thawan, fpj via thawan)
-
-  ZOOKEEPER-1808. Add version to FLE notifications for 3.4 branch (fpj)
-  
-  ZOOKEEPER-1812. ZooInspector reconnection always fails if first
-  connection fails (Benjamin Jaton via phunt)
-
-  ZOOKEEPER-1597. Windows build failing (michim via fpj)
-
-  ZOOKEEPER-1817. Fix don't care for b3.4 (fpj)
-
-  ZOOKEEPER-1653. zookeeper fails to start because of inconsistent
-  epoch (michim via fpj)
-
-  ZOOKEEPER-1821. very ugly warning when compiling load_gen.c
-  (german blanco via fpj)
-
-  ZOOKEEPER-1632. fix memory leaks in cli_st (fpj via michim)
-
-  ZOOKEEPER-1459. Standalone ZooKeeperServer is not closing 
-  the transaction log files on shutdown (Rakesh R via fpj)
-
-  ZOOKEEPER-1019. zkfuse doesn't list dependency on boost in README
-  (Raul Gutierrez Segales via michim)
-
-  ZOOKEEPER-1834. Catch IOException in FileTxnLog (fpj via michim)
-  
-  ZOOKEEPER-1382. Zookeeper server holds onto dead/expired session ids in the watch data structures
-  (Germán Blanco and Michael Morello via camille)
-
-  ZOOKEEPER-1837. Fix JMXEnv checks (potential race conditions)
-  (Germán Blanco via fpj)
-
-  ZOOKEEPER-1839. Deadlock in NettyServerCnxn (Rakesh R via michim)
-
-  ZOOKEEPER-1622. session ids will be negative in the year 2022
-  (Eric Newton via phunt)
-
-  ZOOKEEPER-1756. zookeeper_interest() in C client can return a timeval of 0
-  (Eric Lindvall via michim)
-
-  ZOOKEEPER-1388. Client side 'PathValidation' is missing for the
-  multi-transaction api. (Rakesh R via marshallm, phunt)
-
-  ZOOKEEPER-1841. problem in QuorumTest (Germán via fpj)
-
-  ZOOKEEPER-1733 FLETest#testLE is flaky oo.l windows boxes 
-  (michim, Jeffrey Zhong via fpj)
-
-  ZOOKEEPER-1849. Need to properly tear down tests in various
-  cases (Germán via fpj) 
-
-  ZOOKEEPER-1179. NettyServerCnxn does not properly close
-  socket on 4 letter word requests (Rakesh R, Germán Blanco
-  via fpj)
-
-  ZOOKEEPER-1852. ServerCnxnFactory instance is not properly
-  cleanedup (Rakesh R via fpj)
-
-  ZOOKEEPER-1414. QuorumPeerMainTest.testQuorum, testBadPackets are failing
-  intermittently (Rakesh R via michim)
-
-  ZOOKEEPER-1057. zookeeper c-client, connection to offline server fails to
-  successfully fallback to second zk host (Germán Blanco via michim)
-
-  ZOOKEEPER-1857. PrepRequestProcessotTest doesn't shutdown ZooKeeper server
-  (Germán Blanco via michim)
-
-  ZOOKEEPER-1238. when the linger time was changed for NIO the patch missed
-  Netty (Skye Wanderman-Milne via fpj)
-
-  ZOOKEEPER-1837. Fix JMXEnv checks (potential race conditions)
-  (Germán Blanco via fpj)
-
-  ZOOKEEPER-1858. JMX checks - potential race conditions while stopping
-  and starting server (Rakesh R via fpj)
-
-  ZOOKEEPER-1867. Bug in ZkDatabaseCorruptionTest (fpj)
- 
-  ZOOKEEPER-1872. QuorumPeer is not shutdown in few cases
-  (Rakesh R via fpj)
-
-  ZOOKEEPER-1573. Unable to load database due to missing parent node
-  (Vinayakumar B via phunt, fpj)
-
-  ZOOKEEPER-1811. The ZooKeeperSaslClient service name principal is
-  hardcoded to "zookeeper" (Harsh J via phunt)
-
-  ZOOKEEPER-1873. Unnecessarily InstanceNotFoundException is coming when
-  unregister failed jmxbeans (Rakesh R via michim)
-
-  ZOOKEEPER-1844. TruncateTest fails on windows (Rakesh R via fpj)
-  
-  ZOOKEEPER-1755. Concurrent operations of four letter 'dump' ephemeral 
-  command and killSession causing NPE (Rakesh R via camille)
-
-  ZOOKEEPER-1904. WatcherTest#testWatchAutoResetWithPending is failing
-  (Rakesh R via michim)
-
-IMPROVEMENTS:
-
-  ZOOKEEPER-1564. Allow JUnit test build with IBM Java
-    (Paulo Ricardo Paz Vital via phunt)
-
-  ZOOKEEPER-1598. Ability to support more digits in the version string
-  (Raja Aluri via phunt)
-
-  ZOOKEEPER-1583. Document maxClientCnxns in conf/zoo_sample.cfg
-  (Christopher Tubbs via phunt)
-
-  ZOOKEEPER-1584. Adding mvn-install target for deploying the
-  zookeeper artifacts to .m2 repository (Ashish Singh via phunt)
-
-  ZOOKEEPER-1324. Remove Duplicate NEWLEADER packets from the
-  Leader to the Follower. (thawan, fpj via fpj)
-
-  ZOOKEEPER-1615. minor typos in ZooKeeper Programmer's Guide web page
-  (Evan Zacks via phunt)
-
-  ZOOKEEPER-1552. Enable sync request processor in Observer (thawan, fpj)
-
-  ZOOKEEPER-1758. Add documentation for zookeeper.observer.syncEnabled flag 
-  (thawan, fpj via thawan)
-
-  ZOOKEEPER-1771. ZooInspector authentication (Benjamin Jaton via phunt)
-
-  ZOOKEEPER-1627. Add org.apache.zookeeper.common to exported packages
-  in OSGi MANIFEST (Arnoud Glimmerveen via phunt)
-  
-  ZOOKEEPER-1666. Avoid Reverse DNS lookup if the hostname in 
-  connection string is literal IP address. (George Cao via camille)
- 
-  ZOOKEEPER-1786. ZooKeeper data model documentation is incorrect
-  (Niraj Tolia via fpj)
-
-  ZOOKEEPER-1715. Upgrade netty version (Sean Bridges via michim)
-
-  ZOOKEEPER-1430. add maven deploy support to the build
-  (Giridharan Kesavan via phunt)
-
-Release 3.4.5 - 2012-09-30
-
-Backward compatible changes:
-
-BUGFIXES:
-
-  ZOOKEEPER-1376. zkServer.sh does not correctly check for
-  $SERVER_JVMFLAGS (Skye W-M via henryr)
-
-  ZOOKEEPER-1550. ZooKeeperSaslClient does not finish anonymous login on
-  OpenJDK. (Eugene Koontz via mahadev)
-
-
-Release 3.4.4 - 2012-09-17 
-
-Backward compatible changes:
-
-BUGFIXES:
-
-  ZOOKEEPER-1466. QuorumCnxManager.shutdown missing synchronization.
-  (Patrick Hunt via mahadev)
-
-  ZOOKEEPER-1386. avoid flaky URL redirection in "ant javadoc" : 
-  replace "http://java.sun.com/javase/6/docs/api/" with 
-  "http://download.oracle.com/javase/6/docs/api/" (Eugene Koontz via camille) 
-  
-  ZOOKEEPER-1354. AuthTest.testBadAuthThenSendOtherCommands fails
-  intermittently (phunt via camille)
- 
-  ZOOKEEPER-1277. servers stop serving when lower 32bits of zxid roll
-  over (phunt)
-
-  ZOOKEEPER-1412. java client watches inconsistently triggered on
-  reconnect (phunt)
-
-  ZOOKEEPER-1344. ZooKeeper client multi-update command is not
-  considering the Chroot request (Rakesh R via phunt)
-  
-  ZOOKEEPER-1307. zkCli.sh is exiting when an Invalid ACL exception is
-  thrown from setACL command through client (Kavita Sharma via phunt)
-  
-  ZOOKEEPER-1390. some expensive debug code not protected by a check
-  for debug (breed via camille)
-  
-  ZOOKEEPER-1406. dpkg init scripts don't restart - missing
-  check_priv_sep_dir (Chris Beauchamp via phunt)
-   
-  ZOOKEEPER-1403. zkCli.sh script quoting issue (James Page via phunt)
-   
-  ZOOKEEPER-1384. test-cppunit overrides LD_LIBRARY_PATH and fails if
-  gcc is in non-standard location (Jay Shrauner via phunt)
-  
-  ZOOKEEPER-1419. Leader election never settles for a 5-node cluster
-  (flavio via camille)
-  
-  ZOOKEEPER-1256. ClientPortBindTest is failing on Mac OS X
-  (Daniel Gómez Ferro via phunt)
-
-  ZOOKEEPER-1433. improve ZxidRolloverTest (test seems flakey) (phunt via henryr)
-
-  ZOOKEEPER-1395. node-watcher double-free redux (Mike Lundy via henryr)
-  
-  ZOOKEEPER-1450. Backport of ZOOKEEPER-1294 fix to 3.4 and 3.3 (Norman Bishop via camille)
-
-  ZOOKEEPER-1048. addauth command does not work in cli_mt/cli_st (allengao via michim)
-
-  ZOOKEEPER-1339. C client doesn't build with --enable-debug (Eric Liang via michim)
-   
-  ZOOKEEPER-1318. In Python binding, get_children (and get and exists, and probably others)
-  with expired session doesn't raise exception properly (henryr via michim)
-
-  ZOOKEEPER-1431. zkpython async calls leak memory (Kapil Thangavelu and Andre Cruz via henryr)
-
-  ZOOKEEPER-1163. Memory leak in zk_hashtable.c:do_insert_watcher_object()
-  (Anupam Chanda via michim)
-
-  ZOOKEEPER-1490. If the configured log directory does not exist
-    zookeeper will not start. Better to create the directory and start
-    (suja s via phunt)
-
-  ZOOKEEPER-1210. Can't build ZooKeeper RPM with RPM >= 4.6.0 (i.e. on
-    RHEL 6 and Fedora >= 10) (Tadeusz Andrzej Kadłubowski via phunt)
-
-  ZOOKEEPER-1236. Security uses proprietary Sun APIs
-    (Adalberto Medeiros via phunt)
-
-  ZOOKEEPER-1471. Jute generates invalid C++ code
-    (Michi Mutsuzaki via phunt)
-  
-  ZOOKEEPER-1465. Cluster availability following new leader election 
-    takes a long time with large datasets - is correlated to dataset size
-    (fpj and Thawan Kooburat via camille)
-
-  ZOOKEEPER-1427. Writing to local files is done non-atomically (phunt)
-
-  ZOOKEEPER-1489. Data loss after truncate on transaction log (phunt)
-
-  ZOOKEEPER-1521. LearnerHandler initLimit/syncLimit problems
-    specifying follower socket timeout limits (phunt)
-
-  ZOOKEEPER-1493. C Client: zookeeper_process doesn't invoke
-    completion callback if zookeeper_close has been called
-    (Michi Mutsuzaki via phunt and mahadev)
-
-  ZOOKEEPER-1522. intermittent failures in Zab test due to NPE in
-    recursiveDelete test function (phunt via flavio)
-
-  ZOOKEEPER-1514. FastLeaderElection - leader ignores the round
-  information when joining a quorum (flavio via henryr)
-
-  ZOOKEEPER-1536 c client : memory leak in winport.c (brooklin via michim)
-
-  ZOOKEEPER-1481 allow the C cli to run exists with a watcher (phunt via michim)
-
-  ZOOKEEPER-1380. zkperl: _zk_release_watch doesn't remove items properly from
-  the watch list. (Botond Hejj via mahadev)
-
-  ZOOKEEPER-1501. Nagios plugin always returns OK when it cannot connect to
-  zookeeper. (Brian Sutherland via mahadev)
-
-  ZOOKEEPER-1494. C client: socket leak after receive timeout in
-  zookeeper_interest() (Michi Mutsuzaki via mahadev)
-
-  ZOOKEEPER-1483. Fix leader election recipe documentation. (Michi Mutsuzaki
-  via mahadev)
-
-  ZOOKEEPER-1496. Ephemeral node not getting cleared even after client has
-  exited (Rakesh R via mahadev)
-
-IMPROVEMENTS:
-
-  ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA
-  in order to get rid of the intermediate shell process
-  (Roman Shaposhnik via phunt)
-
-  ZOOKEEPER-1377. add support for dumping a snapshot file content (similar to LogFormatter). (phunt via camille)
-
-  ZOOKEEPER-1454. Document how to run autoreconf if cppunit is
-    installed in a non-standard directory (Michi Mutsuzaki via phunt)
-
-  ZOOKEEPER-1503. remove redundant JAAS configuration code in SaslAuthTest and
-    SaslAuthFailTest (Eugene Koontz via phunt)
-
-  ZOOKEEPER-1510. Should not log SASL errors for non-secure usage
-    (Todd Lipcon via phunt)
-
-  ZOOKEEPER-1497. Allow server-side SASL login with JAAS configuration
-  to be programmatically set (rather than only by reading JAAS
-  configuration file) (Matteo Bertozzi via phunt)
- 
-  ZOOKEEPER-1437. Client uses session before SASL authentication complete.
-  (Eugene Koontz via mahadev)
-
-  ZOOKEEPER-1361. Leader.lead iterates over 'learners' set without proper
-  synchronisation. (Henry Robinson via mahadev)
-
-Release 3.4.3 - 2012-02-06
-
-Backward compatible changes:
-
-BUGFIXES: 
-
-  ZOOKEEPER-1089. zkServer.sh status does not work due to invalid
-  option of nc (Roman Shaposhnik via phunt)
-
-  ZOOKEEPER-1345. Add a .gitignore file with general exclusions and
-  Eclipse project files excluded (Harsh J via phunt)
-
-  ZOOKEEPER-1343. getEpochToPropose should check if lastAcceptedEpoch is greater or equal than epoch (fpj via breed)
-  
-  ZOOKEEPER-1358. In StaticHostProviderTest.java, testNextDoesNotSleepForZero tests that hostProvider.next(0) 
-  doesn't sleep by checking that the latency of this call is less than 10sec (Alex Shraer via camille)
-  
-  ZOOKEEPER-1351. invalid test verification in MultiTransactionTest (phunt via camille)
-
-  ZOOKEEPER-973. bind() could fail on Leader because it does not
-  setReuseAddress on its ServerSocket (Harsh J via phunt)
-
-  ZOOKEEPER-1367. Data inconsistencies and unexpired ephemeral nodes after cluster restart.
-  (Bejamin Reed via mahadev)
-
-  ZOOKEEPER-1353. C client test suite fails consistently.
-  (Clint Byrum via mahadev)
-
-  ZOOKEEPER-1373. Hardcoded SASL login context name clashes with Hadoop security 
-  configuration override. (Eugene Koontz and Thomas Weise via mahadev)
-
-  ZOOKEEPER-1352. server.InvalidSnapshotTest is using connection timeouts that 
-  are too short. (phunt via mahadev)
-
-  ZOOKEEPER-1336. javadoc for multi is confusing, references functionality that doesn't 
-  seem to exist (phunt via mahadev)
-
-  ZOOKEEPER-1340. multi problem - typical user operations are 
-  generating ERROR level messages in the server (phunt via mahadev)
-
-  ZOOKEEPER-1374. C client multi-threaded test suite fails to 
-  compile on ARM architectures. (James Page via mahadev)
-
-  ZOOKEEPER-1337. multi's "Transaction" class is missing tests.
-  (phunt and camille via mahadev)
-
-  ZOOKEEPER-1338. class cast exceptions may be thrown by multi 
-  ErrorResult class (invalid equals) (phunt via mahadev)
-
-IMPROVEMENTS:
- 
-  ZOOKEEPER-1322. Cleanup/fix logging in Quorum code. (phunt via mahadev)
- 
-  ZOOKEEPER-1327. there are still remnants of hadoop urls.
-  (Harsh J via mahadev)
-
-
-Release 3.4.2 - 2011-12-21
-
-Backward compatible changes:
-
-BUGFIXES: 
-
-  ZOOKEEPER-1323. c client doesn't compile on freebsd
-  (michi mutsuzaki via phunt)
-
-  ZOOKEEPER-1333. NPE in FileTxnSnapLog when restarting a cluster.
-  (Patrick Hunt and Andrew Mc Nair via mahadev)
-
-Release 3.4.1 - 2011-12-12
-
-Backward compatible changes:
-
-BUGFIXES: 
-
-  ZOOKEEPER-1311. ZooKeeper test jar is broken (Ivan Kelly via phunt)
-
-  ZOOKEEPER-1305. zookeeper.c:prepend_string func can dereference null ptr.
-  (Daniel Lescohier via mahadev)
-
-  ZOOKEEPER-1316. zookeeper_init leaks memory if chroot is just '/'.
-  (Akira Kitada via mahadev)
-
-  ZOOKEEPER-1315. zookeeper_init always reports sessionPasswd=<hidden>.
-  (Akira Kitada via mahadev)
-
-  ZOOKEEPER-1317. Possible segfault in zookeeper_init. 
-  (Akira Kitada via mahadev)
-
-  ZOOKEEPER-1319. Missing data after restarting+expanding a cluster.
-  (Patrick Hunt and Ben Reed via mahadev)
-
-  ZOOKEEPER-1269. Multi deserialization issues (Camille Fournier via 
-  mahadev)
-
-Release 3.4.0 - 2011-10-25
-
-Non-backward compatible changes:
-
-BUGFIXES: 
-
-Backward compatible changes:
-
-BUGFIXES: 
-
-  ZOOKEEPER-735. cppunit test testipv6 assumes that the machine is ipv6
-  enabled. (mahadev)
-
-  ZOOKEEPER-720. Use zookeeper-{version}-sources.jar instead of
-  zookeeper-{version}-src.jar to publish sources in the Maven repository
-  (paolo via phunt)
-
-  ZOOKEEPER-722. zkServer.sh uses sh's builtin echo on BSD, behaves
-  incorrectly. (Ivan Kelly via phunt)
-
-  ZOOKEEPER-741. root level create on REST proxy fails (phunt)
-
-  ZOOKEEPER-631. zkpython's C code could do with a style clean-up
-  (henry robinson via phunt)
-  
-  ZOOKEEPER-746. learner outputs session id to log in dec (phunt via
-  henryr)
-
-  ZOOKEEPER-738. zookeeper.jute.h fails to compile with -pedantic
-  (Jozef Hatala via phunt)
-
-  ZOOKEEPER-734. QuorumPeerTestBase.java and ZooKeeperServerMainTest.java
-  do not handle windows path correctly (Vishal K via phunt)
-
-  ZOOKEEPER-754. numerous misspellings "succesfully"
-  (Andrei Savu via phunt)
-
-  ZOOKEEPER-749. OSGi metadata not included in binary only jar (phunt
-  via henryr)
-
-  ZOOKEEPER-750. move maven artifacts into "dist-maven" subdir of the
-  release (package target) (phunt via henryr)
-
-  ZOOKEEPER-758. zkpython segfaults on invalid acl with missing key
-  (Kapil Thangavelu via henryr)
-
-  ZOOKEEPER-737. some 4 letter words may fail with netcat (nc). (mahadev)
-
-  ZOOKEEPER-764. Observer elected leader due to inconsistent voting view 
-  (henry via mahadev)
-
-  ZOOKEEPER-763. Deadlock on close w/ zkpython / c client
-  (henry via phunt)
-
-  ZOOKEEPER-774. Recipes tests are slightly outdated: they do not compile
-  against JUnit 4.8 (Sergey Doroshenko via phunt)
-
-  ZOOKEEPER-772. zkpython segfaults when watcher from async get children is
-  invoked. (henry via phunt)
-
-  ZOOKEEPER-636. configure.ac has instructions which override the contents of
-  CFLAGS and CXXFLAGS. (Maxim P. Dementiev via phunt)
-
-  ZOOKEEPER-796. zkServer.sh should support an external PIDFILE variable
-  (Alex Newman via phunt)
-
-  ZOOKEEPER-719. Add throttling to BookKeeper client (fpj via breed)
-
-  ZOOKEEPER-814. monitoring scripts are missing apache license headers
-  (andrei savu via mahadev)
-
-  ZOOKEEPER-783. committedLog in ZKDatabase is not properly synchronized
-  (henry via mahadev) 
-
-  ZOOKEEPER-790.  Last processed zxid set prematurely while establishing 
-  leadership (flavio via mahadev)
-
-  ZOOKEEPER-795. eventThread isn't shutdown after a connection 
-  "session expired" event coming (Sergey Doroshenko and Ben via mahadev)
-
-  ZOOKEEPER-792. zkpython memory leak (Lei Zhang via henryr)
-
-  ZOOKEEPER-854. BookKeeper does not compile due to changes in the ZooKeeper 
-  code (Flavio via mahadev)
-
-  ZOOKEEPER-861. Missing the test SSL certificate used for running junit tests.
-  (erwin tam via mahadev)
-
-  ZOOKEEPER-867. ClientTest is failing on hudson - fd cleanup (phunt)
-
-  ZOOKEEPER-785. Zookeeper 3.3.1 shouldn't infinite loop if someone creates a
-  server.0 line (phunt and Andrei Savu via breed)
-
-  ZOOKEEPER-785. Zookeeper 3.3.1 shouldn't infinite loop if someone creates a
-  server.0 line (part 2) (phunt)
-
-  ZOOKEEPER-870. Zookeeper trunk build broken. (mahadev via phunt)
-
-  ZOOKEEPER-831. BookKeeper: Throttling improved for reads (breed via fpj)
-
-  ZOOKEEPER-846. zookeeper client doesn't shut down cleanly on the close call
-  (phunt)
-  
-  ZOOKEEPER-804. c unit tests failing due to "assertion cptr failed" (michi
-  mutsuzaki via mahadev)
-
-  ZOOKEEPER-844. handle auth failure in java client
-  (Camille Fournier via phunt)
-
-  ZOOKEEPER-822. Leader election taking a long time to complete
-  (Vishal K via phunt)
-
-  ZOOKEEPER-866. Hedwig Server stays in "disconnected" state when
-  connection to ZK dies but gets reconnected (erwin tam via breed)
-
-  ZOOKEEPER-881. ZooKeeperServer.loadData loads database twice
-  (jared cantwell via breed)
-
-  ZOOKEEPER-855. clientPortBindAddress should be clientPortAddress
-  (Jared Cantwell via fpj)
-
-  ZOOKEEPER-888. c-client / zkpython: Double free corruption on
-  node watcher (Austin Shoemaker via henryr)
-
-  ZOOKEEPER-893. ZooKeeper high cpu usage when invalid requests
-  (Thijs Terlouw via phunt)
-
-  ZOOKEEPER-804. c unit tests failing due to "assertion cptr failed"
-  (second try - Jared Cantwell via phunt)
-
-  ZOOKEEPER-820. update c unit tests to ensure "zombie" java server
-  processes don't cause failure (Michi Mutsuzaki via phunt)
-
-  ZOOKEEPER-794. Callbacks are not invoked when the client is closed
-  (Alexis Midon via phunt)
-
-  ZOOKEEPER-800. zoo_add_auth returns ZOK if zookeeper handle is in
-  ZOO_CLOSED_STATE (michi mutsuzaki via mahadev konar)
-
-  ZOOKEEPER-904. super digest is not actually acting as a full superuser
-  (Camille Fournier via mahadev)
-
-  ZOOKEEPER-897. C Client seg faults during close (jared cantwell via mahadev)
-
-  ZOOKEEPER-898. C Client might not cleanup correctly during close 
-  (jared cantwell via mahadev)
-
-  ZOOKEEPER-907. Spurious "KeeperErrorCode = Session moved" messages (vishal k via breed)
-
-  ZOOKEEPER-884. Remove LedgerSequence references from BookKeeper documentation and comments in tests (fpj via breed)
-
-  ZOOKEEPER-916. Problem receiving messages from subscribed channels in c++ client (ivan via breed)
-
-  ZOOKEEPER-930. Hedwig c++ client uses a non thread safe logging library (ivan via breed)
-
-  ZOOKEEPER-900. FLE implementation should be improved to use non-blocking sockets (vishal via fpj)
-
-  ZOOKEEPER-937. test -e not available on solaris /bin/sh (Erik Hetzner via mahadev)
-
-  ZOOKEEPER-905. enhance zkServer.sh for easier zookeeper automation-izing (Nicholas Harteau via mahadev)
-
-  ZOOKEEPER-913. Version parser fails to parse "3.3.2-dev" from build.xml (Anthony Urso and phunt via breed)
-
-  ZOOKEEPER-957. zkCleanup.sh doesn't do anything (Ted Dunning via mahadev)
-
-  ZOOKEEPER-958. Flag to turn off autoconsume in hedwig c++ client (Ivan Kelly
-  via mahadev)
-
-  ZOOKEEPER-882. Startup loads last transaction from snapshot (j:ared via fpj)
-
-  ZOOKEEPER-962. leader/follower coherence issue when follower is receiving a DIFF
-  (camille fournier via breed)
-
-  ZOOKEEPER-902. Fix findbug issue in trunk "Malicious code vulnerability"
-  (flavio and phunt via phunt)
-
-  ZOOKEEPER-985. Test BookieRecoveryTest fails on trunk. (fpj via breed)
-
-  ZOOKEEPER-983. running zkServer.sh start remotely using ssh hangs (phunt)
-
-  ZOOKEEPER-976. ZooKeeper startup script doesn't use JAVA_HOME (phunt)
-
-  ZOOKEEPER-994 "eclipse" target in the build script doesnot include
-  libraray required for test classes in the classpath (MIS via phunt) 
-
-  ZOOKEEPER-1013 zkServer.sh usage message should mention all startup options
-  (eugene koontz via mahadev)
-
-  ZOOKEEPER-1007. iarchive leak in C client (jeremy stribling via mahadev)
-
-  ZOOKEEPER-993. Code improvements (MIS via fpj)
-
-  ZOOKEEPER-1012. support distinct JVMFLAGS for zookeeper server in zkServer.sh 
-  and zookeeper client in zkCli.sh (Eugene Koontz via breed)
-
-  ZOOKEEPER-880. QuorumCnxManager$SendWorker grows without bounds (vishal via breed)
-
-  ZOOKEEPER-1018. The connection permutation in get_addrs uses a weak and inefficient 
-  shuffle (Stephen Tyree via breed)
- 
-  ZOOKEEPER-1028. In python bindings, zookeeper.set2() should return a stat dict but 
-  instead returns None. (Chris Medaglia and Ivan Kelly via mahadev)
-
-  ZOOKEEPER-975. new peer goes in LEADING state even if ensemble is online. (vishal via fpj)
-
-  ZOOKEEPER-1049. Session expire/close flooding renders heartbeats to delay significantly. 
-  (chang song via mahadev)
-  
-  ZOOKEEPER-1033. c client should install includes into INCDIR/zookeeper, not INCDIR/c-client-src
-  (Nicholas Harteau via mahadev)
-
-  ZOOKEEPER-1061. Zookeeper stop fails if start called twice. (Ted Dunning via mahadev)
-
-  ZOOKEEPER-1059. stat command isses on non-existing node causes NPE. (Bhallamudi Kamesh via mahadev)
-  
-  ZOOKEEPER-1058. fix typo in opToString for getData. (camille)
-  
-  ZOOKEEPER-1046. Creating a new sequential node results in a ZNODEEXISTS error. (Vishal K via camille) 
-  
-  ZOOKEEPER-1069. Calling shutdown() on a QuorumPeer too quickly can lead to a corrupt log. (Vishal K via camille) 
-
-  ZOOKEEPER-1083. Javadoc for WatchedEvent not being generated. (Ivan Kelly via michim)
-
-  ZOOKEEPER-1086. zookeeper test jar has non mavenised dependency. (Ivan Kelly via michim)
-
-  ZOOKEEPER-335. zookeeper servers should commit the new leader txn to their logs. (breed)
-
-  ZOOKEEPER-1081. modify leader/follower code to correctly deal with new leader (breed)
-
-  ZOOKEEPER-1082. modify leader election to correctly take into account current epoch (fpj via breed)
-
-  ZOOKEEPER-1060. QuorumPeer takes a long time to shutdown (Vishal via fpj)
-
-  ZOOKEEPER-1087. ForceSync VM arguement not working when set to "no" (Nate Putnam via breed)
-
-  ZOOKEEPER-1068. Documentation and default config suggest incorrect
-  location for Zookeeper state (Roman Shaposhnik via phunt)
-
-  ZOOKEEPER-1103. In QuorumTest, use the same "for ( .. try { break }
-  catch { } )" pattern in testFollowersStartAfterLeaders as in
-  testSessionMove. (Eugene Koontz via phunt)
-  ZOOKEEPER-1046. Creating a new sequential node results in a ZNODEEXISTS error. (breed via camille)
-
-  ZOOKEEPER-1097. Quota is not correctly rehydrated on snapshot reload (camille via henryr)
-  
-  ZOOKEEPER-1046. Small fix: Creating a new sequential node results in a ZNODEEXISTS error. (Vishal K via camille)
-
-  ZOOKEEPER-782. Incorrect C API documentation for Watches. (mahadev via breed)
-
-  ZOOKEEPER-1063. Dubious synchronization in Zookeeper and ClientCnxnSocketNIO classes (Yanick Dufresne via breed)
-
-  ZOOKEEPER-1124. Multiop submitted to non-leader always fails due to timeout (Marshall McMullen via breed)
-
-  ZOOKEEPER-1111. JMXEnv uses System.err instead of logging
-  (Ivan Kelly via phunt)
-  
-  ZOOKEEPER-1027. chroot not transparent in zoo_create() (Thijs Terlouw via
-  mahadev)
-  
-  ZOOKEEPER-1109. Zookeeper service is down when SyncRequestProcessor meets
-  any exception. (Laxman via mahadev)
-
-  ZOOKEEPER-1134. ClientCnxnSocket string comparison using == rather than equals.
-  (phunt via mahadev) 
-
-  ZOOKEEPER-1119. zkServer stop command incorrectly reading comment lines in
-  zoo.cfg (phunt via mahadev)
-
-  ZOOKEEPER-1090. Race condition while taking snapshot can lead to not restoring data tree correctly (Vishal K via breed)
-
-  ZOOKEEPER-1138. release audit failing for a number of new files. (phunt via mahadev) 
-
-  ZOOKEEPER-1139. jenkins is reporting two warnings, fix these (phunt via mahadev)
-
-  ZOOKEEPER-1142. incorrect stat output (phunt via mahadev)
-  
-  ZOOKEEPER-1144. ZooKeeperServer not starting on leader due to a race condition (Vishal K via camille)
-
-  ZOOKEEPER-839. deleteRecursive does not belong to the other methods.
-   (mahadev)
- 
-  ZOOKEEPER-1146. significant regression in client (c/python) performance.
-  (phunt via mahadev)
-
-  ZOOKEEPER-1150. fix for this patch to compile on windows. (dheeraj
-  via mahadev)
-
-  ZOOKEEPER-1055. check for duplicate ACLs in addACL() and create().
-  (Eugene Koontz via mahadev)
-
-  ZOOKEEPER-1141. zkpython fails tests under python 2.4. (phunt via mahadev)
-  
-  ZOOKEEPER-1025. zkCli is overly sensitive to to spaces. (Laxman via camille)
-
-  ZOOKEEPER-1117. zookeeper 3.3.3 fails to build with gcc >= 4.6.1 on
-  Debian/Ubuntu (James Page via mahadev)
-
-  ZOOKEEPER-1140. server shutdown is not stopping threads. (laxman via mahadev)
-
-  ZOOKEEPER-1051. SIGPIPE in Zookeeper 0.3.* when send'ing after 
-  cluster disconnection (Stephen Tyree via mahadev)
-
-  ZOOKEEPER-1168. ZooKeeper fails to run with IKVM (Andrew Finnell via phunt)
-
-  ZOOKEEPER-1165. better eclipse support in tests (Warren Turkal via phunt)
-  
-  ZOOKEEPER-1154. Data inconsistency when the node(s) with the highest zxid is not present at the time of leader election. (Vishal Kathuria via camille)
-  
-  ZOOKEEPER-1156. Log truncation truncating log too much - can cause data loss. (Vishal Kathuria via camille)
-
-  ZOOKEEPER-1160. test timeouts are too small (breed via phunt)
-
-  ZOOKEEPER-731. Zookeeper#delete , #create - async versions miss a verb in the javadoc. (Thomas Koch via camille)
-
-  ZOOKEEPER-1108. Various bugs in zoo_add_auth in C. (Dheeraj Agrawal via mahadev)
- 
-  ZOOKEEPER-981. Hang in zookeeper_close() in the multi-threaded C client. 
-  (Jeremy Stribling via mahadev) 
-
-  ZOOKEEPER-1125. Intermittent java core test failures. (Vishar Kher via mahadev)
-
-  ZOOKEEPER-961. Watch recovery after disconnection when connection string contains a prefix.
-  (Matthias Spycher via mahadev)
-
-  ZOOKEEPER-1136. NEW_LEADER should be queued not sent to match the Zab 1.0 protocol 
-  on the twiki (breed via mahadev)
-
-  ZOOKEEPER-1189. For an invalid snapshot file(less than 10bytes size) RandomAccessFile 
-  stream is leaking. (Rakesh R via mahadev)
-
-  ZOOKEEPER-1185. Send AuthFailed event to client if SASL authentication fails.
-  (Eugene Kuntz via mahadev)
-  
-  ZOOKEEPER-1174. FD leak when network unreachable (Ted Dunning via camille)
-
-  ZOOKEEPER-1203. Zookeeper systest is missing Junit Classes
-  (Prashant Gokhale via phunt)
-
-  ZOOKEEPER-1206. Sequential node creation does not use always use
-  digits in node name given certain Locales. (Mark Miller via phunt)
-
-  ZOOKEEPER-1212. zkServer.sh stop action is not conformat with LSB
-  para 20.2 Init Script Actions (Roman Shaposhnik via phunt)
-
-  ZOOKEEPER-1190. ant package is not including many of the bin scripts
-  in the package (zkServer.sh for example) (Eric Yang via phunt)
-
-  ZOOKEEPER-1181. Fix problems with Kerberos TGT renewal.
-  (Eugene Koontz via mahadev)
-  
-  ZOOKEEPER-1264. FollowerResyncConcurrencyTest failing intermittently. (phunt via camille)
-
-  ZOOKEEPER-1268. problems with read only mode, intermittent test failures 
-  and ERRORs in the log. (phunt via mahadev)
-  
-  ZOOKEEPER-1246. Dead code in PrepRequestProcessor catch Exception block. (camille)
-
-  ZOOKEEPER-1271. testEarlyLeaderAbandonment failing on solaris -
-  clients not retrying connection (mahadev via phunt)
-
-  ZOOKEEPER-1192. Leader.waitForEpochAck() checks waitingForNewEpoch instead 
-  of checking electionFinished (Alexander Shraer via mahadev)
-
-  ZOOKEEPER-1270. testEarlyLeaderAbandonment failing intermittently, 
-  quorum formed, no serving. (Flavio, Camille and Alexander Shraer via mahadev)
-  
-  ZOOKEEPER-1264. FollowerResyncConcurrencyTest failing 
-  intermittently. (breed, camille and Alex Shraer via camille)
-  
-  ZOOKEEPER-1282. Learner.java not following Zab 1.0 protocol - 
-  setCurrentEpoch should be done upon receipt of NEWLEADER 
-  (before acking it) and not upon receipt of UPTODATE (breed via camille)
-  
-  ZOOKEEPER-1291. AcceptedEpoch not updated at leader before it proposes the epoch to followers. (Alex Shraer via camille)
-
-  ZOOKEEPER-1208. Ephemeral node not removed after the client session is long gone. (phunt via camille)
-  
-  ZOOKEEPER-1239. add logging/stats to identify fsync stalls. (phunt via camille)
-  
-  ZOOKEEPER-1299. Add winconfig.h file to ignore in release audit. (mahadev)
-
-IMPROVEMENTS:
-  ZOOKEEPER-724. Improve junit test integration - log harness information 
-  (phunt via mahadev)
-
-  ZOOKEEPER-766. forrest recipes docs don't mention the lock/queue recipe
-  implementations available in the release (phunt via mahadev)
-
-  ZOOKEEPER-769: Leader can treat observers as quorum members
-  (Sergey Doroshenko via henryr)
-
-  ZOOKEEPER-788: Add server id to message logs
-  (Ivan Kelly via flavio)
-
-  ZOOKEEPER-789. Improve FLE log messages (flavio via phunt)
-
-  ZOOKEEPER-798. Fixup loggraph for FLE changes (Ivan Kelly via phunt)
-
-  ZOOKEEPER-797 c client source with AI_ADDRCONFIG cannot be compiled with
-  early glibc (Qian Ye via phunt)
-
-  ZOOKEEPER-790. Last processed zxid set prematurely while establishing leadership
-  (fpj via breed)
-
-  ZOOKEEPER-821. Add ZooKeeper version information to zkpython (Rich
-  Schumacher via mahadev)
-
-  ZOOKEEPER-765.  Add python example script (Travis and Andrei via mahadev)
-
-  ZOOKEEPER-809. Improved REST Interface (Andrei Savu via phunt)
-
-  ZOOKEEPER-733. use netty to handle client connections (breed and phunt)
-
-  ZOOKEEPER-853. Make zookeeper.is_unrecoverable return True or False
-  in zkpython (Andrei Savu via henryr)
-
-  ZOOKEEPER-864. Hedwig C++ client improvements (Ivan Kelly via breed)
-
-  ZOOKEEPER-862. Hedwig created ledgers with hardcoded Bookkeeper ensemble and
-  quorum size. Make these a server config parameter instead. (Erwin T via breed)
-
-  ZOOKEEPER-926. Fork Hadoop common's test-patch.sh and modify for Zookeeper.
-  (nigel)
-
-  ZOOKEEPER-909. Extract NIO specific code from ClientCnxn
-  (Thomas Koch via phunt)
-
-  ZOOKEEPER-908. Remove code duplication and inconsistent naming in
-  ClientCnxn.Packet creation (Thomas Koch via phunt)
-
-  ZOOKEEPER-836. hostlist as string. (Thomas Koch via breed)
-
-  ZOOKEEPER-921. zkPython incorrectly checks for existence of required
-  ACL elements (Nicholas Knight via henryr)
-
-  ZOOKEEPER-963. Make Forrest work with	JDK6 (Carl Steinbach via henryr)
-
-  ZOOKEEPER-500. Async methods shouldnt throw exceptions (fpj via breed)
-
-  ZOOKEEPER-977. passing null for path_buffer in zoo_create (breed via mahadev)
-
-  ZOOKEEPER-465. Ledger size in bytes. (flavio via mahadev)
-
-  ZOOKEEPER-980. allow configuration parameters for log4j.properties
-  (phunt via mahadev)
-
-  ZOOKEEPER-1042. Generate zookeeper test jar for maven installation
-  (ivan kelly via breed)
-
-  ZOOKEEPER-1030: Increase default for maxClientCnxns
-  (Todd Lipcon via breed/mahadev/phunt)
-
-  ZOOKEEPER-850: Switch from log4j to slf4j (Olaf Krische via michim)
-
-  ZOOKEEPER-874. FileTxnSnapLog.restore does not call listener (diogo via fpj)
-
-  ZOOKEEPER-1052. Findbugs warning in QuorumPeer.ResponderThread.run()  (fpj via michim)
-
-  ZOOKEEPER-1094. Small improvements to LeaderElection and Vote classes (henryr via breed)
-
-  ZOOKEEPER-1074. zkServer.sh is missing nohup/sleep, which are necessary 
-  for remote invocation. (phunt via mahadev)
-
-  ZOOKEEPER-965. Need a multi-update command to allow multiple znodes to be updated safely (Marshall McMullen and Ted Dunning via breed)
-
-  ZOOKEEPER-1073. address a documentation issue in ZOOKEEPER-1030. (phunt via mahadev)
-
-  ZOOKEEPER-1095. Simple leader election recipe
-  (Eric Sammer via henry and phunt)
-
-  ZOOKEEPER-1076. some quorum tests are unnecessarily extending QuorumBase (phunt via mahadev)
-
-  ZOOKEEPER-1143. quorum send & recv workers are missing thread names
-  (phunt via mahadev)
-  
-  ZOOKEEPER-1104. CLONE - In QuorumTest, use the same "for ( .. try { break }
-  catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. 
-  (Eugene Koontz via mahadev)
- 
-  ZOOKEEPER-1034. perl bindings should automatically find the zookeeper
-  c-client headers (nicholas harteau via mahadev)
-
-  ZOOKEEPER-1166. Please add a few svn:ignore properties (via phunt)
-
-  ZOOKEEPER-1169. Fix compiler (eclipse) warnings in (generated) jute
-  code (Thomas Koch via phunt)
-
-  ZOOKEEPER-1171. fix build for java 7 (phunt via mahadev)
-
-  ZOOKEEPER-1201. Clean SaslServerCallbackHandler.java. (Thomas Koch
-  via mahadev) 
-  
-  ZOOKEEPER-1321. Add number of client connections metric in JMX and srvr. (Neha Narkhede via camille)
-
-NEW FEATURES:
-  ZOOKEEPER-729. Java client API to recursively delete a subtree.
-  (Kay Kay via henry)
-
-  ZOOKEEPER-747. Add C# generation to Jute (Eric Hauser via phunt)
-
-  ZOOKEEPER-464. Need procedure to garbage collect ledgers
-  (erwin via fpj)
-
-  ZOOKEEPER-773. Log visualisation (Ivan Kelly via phunt)
-
-  ZOOKEEPER-744. Add monitoring four-letter word (Andrei Savu via phunt)
-
-  ZOOKEEPER-712. Bookie recovery. (erwin tam via breed)
-
-  ZOOKEEPER-799. Add tools and recipes for monitoring as a contrib
-  (Andrei Savu via phunt)
-
-  ZOOKEEPER-808. Web-based Administrative Interface
-  (Andrei Savu via phunt)
-
-  ZOOKEEPER-775. A large scale pub/sub system (Erwin, Ivan and Ben via
-  mahadev)
-
-  ZOOKEEPER-1020. Implement function in C client to determine which host you're currently connected to. (stephen tyree via breed)
-
-  ZOOKEEPER-1038. Move bookkeeper and hedwig code in subversion (breed)
-
-  ZOOKEEPER-784. Server-side functionality for read-only mode (Sergey Doroshenko via henryr)
-
-  ZOOKEEPER-992. MT Native Version of Windows C Client (Dheeraj Agrawal via michim)
- 
-  ZOOKEEPER-938. Support Kerberos authentication of clients. (Eugene Koontz
-  via mahadev)
-
-  ZOOKEEPER-1152. Exceptions thrown from handleAuthentication can cause buffer corruption issues in NIOServer. (camille via breed)
-
-  ZOOKEEPER-999. Create an package integration project (Eric Yang via phunt)
-
-  ZOOKEEPER-1107. automating log and snapshot cleaning (Laxman via phunt)
-
-DEPRECATION:
-  ZOOKEEPER-1153. Deprecate AuthFLE and LE. (Flavio Junqueira via mahadev)
-  
-Release 3.3.0 - 2010-03-24
-
-Non-backward compatible changes:
-
-BUGFIXES: 
-
-Backward compatible changes:
-
-BUGFIXES: 
-  ZOOKEEPER-59. Synchronized block in NIOServerCnxn (fpj via breed)
-
-  ZOOKEEPER-524. DBSizeTest is not really testing anything (breed)
-
-  ZOOKEEPER-469. make sure CPPUNIT_CFLAGS isn't overwritten
-  (chris via mahadev)
-
-  ZOOKEEPER-471. update zkperl for 3.2.x branch (chris via mahadev)
-
-  ZOOKEEPER-470. include unistd.h for sleep() in c tests (chris via mahadev)
-
-  ZOOKEEPR-460. bad testRetry in cppunit tests (hudson failure)
-  (giri via mahadev)
-
-  ZOOKEEPER-467.  Change log level in BookieHandle. (flavio via mahadev)
-
-  ZOOKEEPER-482. ignore sigpipe in testRetry to avoid silent immediate
-  failure. (chris via mahadev)
-
-  ZOOKEEPER-487. setdata on root (/) crashes the servers (mahadev via phunt)
-
-  ZOOKEEPER-457. Make ZookeeperMain public, support for HBase (and other)
-  embedded clients (ryan rawson via phunt)
-
-  ZOOKEEPER-481. Add lastMessageSent to QuorumCnxManager. (flavio via mahadev)
-
-  ZOOKEEPER-479.  QuorumHierarchical does not count groups correctly
-  (flavio via mahadev)
-
-  ZOOKEEPER-466. crash on zookeeper_close() when using auth with empty cert
-  (Chris Darroch via phunt)
-
-  ZOOKEEPER-480. FLE should perform leader check when node is not leading and
-  add vote of follower (flavio via mahadev)
-
-  ZOOKEEPER-491. Prevent zero-weight servers from being elected.
-  (flavio via mahadev)
-
-  ZOOKEEPER-447. zkServer.sh doesn't allow different config files to be
-  specified on the command line (henry robinson via phunt)
-
-  ZOOKEEPER-493. patch for command line setquota (steve bendiola via phunt)
-
-  ZOOKEEPER-311. handle small path lengths in zoo_create()
-  (chris barroch via breed)
-
-  ZOOKEEPER-484.  Clients get SESSION MOVED exception when switching from
-  follower to a leader. (mahadev)
-
-  ZOOKEEPER-490. the java docs for session creation are misleading/incomplete
-  (phunt)
-
-  ZOOKEEPER-501. CnxManagerTest failed on hudson. (flavio via mahadev)
-  
-  ZOOKEEPER-499. electionAlg should default to FLE (3) - regression
-  (phunt via mahadev) 
-
-  ZOOKEEPER-477. zkCleanup.sh is flaky (fernando via mahadev)
-
-  ZOOKEEPER-498. Unending Leader Elections : WAN configuration
-  (flavio via mahadev)
-
-  ZOOKEEPER-508. proposals and commits for DIFF and Truncate messages from the
-  leader to the followers is buggy. (mahadev and ben via mahadev)
-
-  ZOOKEEPER-518. DEBUG message for outstanding proposals in leader should be
-   moved to trace. (phunt)
-
-  ZOOKEEPER-533.  ant error running clean twice (phunt via mahadev)
-
-  ZOOKEEPER-535. ivy task does not enjoy being defined twice 
-  (build error) (phunt via mahadev)
-
-  ZOOKEEPER-420. build/test should not require install in zkpython
-  (henry robinson via phunt)
-
-  ZOOKEEPER-538. zookeeper.async causes python to segfault
-  (henry robinson via phunt)
-
-  ZOOKEEPER-542. c-client can spin when server unresponsive (Christian
-  Wiedmann via mahadev)
-
-  ZOOKEEEPER-510. zkpython lumps all exceptions as IOError, needs specialized
-  exceptions for KeeperException types (henry & pat via mahadev)
-
-  ZOOKEEPER-541. zkpython limited to 256 handles (henry robinson via phunt)
-
-  ZOOKEEPER-554. zkpython can segfault when statting a deleted node
-  (henry robinson via phunt)
-
-  ZOOKEEPER-512. FLE election fails to elect leader (flavio via mahadev)
-
-  ZOOKEEPER-563. ant test for recipes is broken. (mahadev via phunt)
- 
-  ZOOKEEPER-562. c client can flood server with pings if tcp send queue
-  filled. (ben reed via mahadev)
-
-  ZOOKEEPER-537. The zookeeper jar includes the java source files
-  (Thomas Dudziak via phunt)
-
-  ZOOKEEPER-551. unnecessary SetWatches message on new session.
-  (phunt via flavio)
-
-  ZOOKEEPER-566. "reqs" four letter word (command port) returns no information
-  (phunt via breed)
-
-  ZOOKEEPER-567. javadoc for getchildren2 needs to mention "new in 3.3.0"
-  (phunt via breed)
-
-  ZOOKEEPER-547. Sanity check in QuorumCnxn Manager and quorum communication
-  port. (mahadev via breed)
-
-  ZOOKEEPER-532. java compiler should be target Java 1.5
-  (hiram chirino and phunt via breed)
-
-  ZOOKEEPER-519. Followerhandler should close the socket if it gets an exception
-  on a write. (mahadev via breed)
-
-  ZOOKEEPER-570. AsyncHammerTest is broken, callbacks need to validate rc
-  parameter (phunt via breed)
-
-  ZOOKEEPER-3. syncLimit has slightly different comments in the class header,
-  and > inline with the variable. (mahadev via breed)
-
-  ZOOKEEPER-576. docs need to be updated for session moved exception and how
-  to handle it (breed via phunt)
-
-  ZOOKEEPER-582. ZooKeeper can revert to old data when a snapshot is created
-  outside of normal processing (ben reed and mahadev via mahadev)
-
-  ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk
-  (Patrick Hunt via mahadev)
-
-  ZOOKEEPER-598. LearnerHandler is misspelt in the thread's constructor 
-  (Henry Robinson via fpj)
-
-  ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk (take 2)
-  (breed)
-
-  ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk
-  (take 3) (phunt via mahadev)
-
-  ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk
-  (take 4) (breed via mahadev)
-
-  ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk 
-  (take 5) (mahadev)
-
-  ZOOKEEPER-611.  hudson build failiure (mahadev)
-
-  ZOOKEEPER-611. hudson build failure (take 2) (mahadev)
-
-  ZOOKEEPER-615. wrong javadoc for create with a sequence flag
-  (mahadev via breed)
-
-  ZOOKEEPER-588. remove unnecessary/annoying log of tostring error in 
-  Request.toString() (phunt via breed)
-
-  ZOOKEEPER-587.  client should log timeout negotiated with server
-  (phunt via mahadev)
-
-  ZOOKEEPER-610. cleanup final fields, esp those used for locking
-  (phunt via henry)
-
-  ZOOKEEPER-614. Improper synchronisation in getClientCnxnCount
-  (henry via mahadev) 
-
-  ZOOKEEPER-609. ObserverTest failure "zk should not be connected expected not
-  same" (henry robinson via phunt)
-
-  ZOOKEEPER-630. Trunk has duplicate ObserverTest.java files
-  (henry robinson via phunt)
-
-  ZOOKEEPER-627. zkpython arbitrarily restricts the size of a 'get' to 512
-  bytes (henry robinson via mahadev)
-
-  ZOOKEEPER-534. The test target in contib/bookkeeper does not depend on jar
-  target. (phunt via mahadev)
-
-  ZOOKEEPER-623. ClientBase in bookkeeper.util requires junit (fpj via breed)
-
-  ZOOKEEPER-600. TODO pondering about allocation behavior in zkpython may be
-  removed (gustavo via mahadev)
-
-  ZOOKEEPER-596. The last logged zxid calculated by zookeeper servers could
-  cause problems in leader election if data gets corrupted. (mahadev)
-
-  ZOOKEEPER-637. Trunk build is failing (fpj via breed)
-  
-  ZOOKEEPER-637. Trunk build is failing - second patch (breed via fpj)
-
-  ZOOKEEPER-644. Nightly build failed on hudson. (pat via mahadev)
-
-  ZOOKEEPER-651: Log exception trace in QuorumCnxManager.SendWorker 
-  (flavio via henry)
-
-  ZOOKEEPER-608. Receipt of ACK from observer should not be logged as ERROR
-  (henry via mahadev)
-
-  ZOOKEEPER-647. hudson failure in testLeaderShutdown (flavio via mahadev)
-
-  ZOOKEEPER-574. the documentation on snapcount in the admin guide has the
-  wrong default (phunt via mahadev)
-
-  ZOOKEEPER-656. SledgeHammer test - thread.run() deprecated (kay kay via mahadev)
-
-  ZOOKEEPER-413. two flaws need addressing in the c tests that can cause false
-  positive failures (phunt via mahadev)
-
-  ZOOKEEPER-495. c client logs an invalid error when zookeeper_init is called
-  with chroot (phunt via mahadev)
-
-  ZOOKEEPER-589. When create a znode, a NULL ACL parameter cannot be accepted.
-  (breed via mahadev)
-
-  ZOOKEEPER-673.  Fix observer documentation regarding leader election (flavio
-  via mahadev)
-
-  ZOOKEEPER-672. typo nits across documentation (Kay Kay via mahadev)
-
-  ZOOKEEPER-668. Close method in LedgerInputStream doesn't do anything (flavio 
-  via mahadev)
-
-  ZOOKEEPER-569. Failure of elected leader can lead to never-ending leader 
-  election (henry via flavio)
-
-  ZOOKEEPER-669. watchedevent tostring should clearly output the
-  state/type/path (phunt via mahadev)
-
-  ZOOKEEPER-683. LogFormatter fails to parse transactional log files (phunt
-  via mahadev)
-
-  ZOOKEEPER-682. Event is not processed when the watcher is set to watch "/"
-  if chrooted (Scott Wang via mahadev)
-
-  ZOOKEEPER-687. LENonterminatetest fails on some machines. (mahadev)
-
-  ZOOKEEPER-681. Minor doc issue re unset maxClientCnxns (phunt via mahadev)
-
-  ZOOKEEPER-622. Test for pending watches in send_set_watches should be moved
-  (ben and steven via mahadev)
-
-  ZOOKEEPER-689.  release build broken - ivysettings.xml not copied during
-  "package" (phunt via mahadev)
-
-  ZOOKEEPER-59. Synchronized block in NIOServerCnxn (flavio via mahadev) 
-
-  ZOOKEEPER-691. Interface changed for NIOServer.Factory (breed via mahadev)
-
-  ZOOKEEPER-685.  Race in LENonTerminateTest (henry via breed)
-
-  ZOOKEEPER-677. c client doesn't allow ipv6 numeric connect string
-  (breed & phunt & mahadev via breed)
-
-  ZOOKEEPER-693. TestObserver stuck in tight notification loop in FLE
-  (flavio via phunt)
-
-  ZOOKEEPER-696. NPE in the hudson logs, seems nioservercnxn closed twice 
-  (phunt via mahadev)
-
-  ZOOKEEPER-511. bad error handling in FollowerHandler.sendPackets
-  (mahadev via flavio)
-
-  ZOOKEEPER-604. zk needs to prevent export of any symbol not listed in their
-  api (mahadev)
-
-  ZOOKEEPER-121. SyncRequestProcessor is not closing log stream during
-  shutdown (mahadev)
-
-  ZOOKEEPER-698. intermittent JMX test failures due to not verifying QuorumPeer
-  shutdown (phunt)
-
-  ZOOKEEPER-121_2. SyncRequestProcessor is not closing log stream during
-  shutdown (breed via mahadev)
-
-  ZOOKEEPER-121_3. SyncRequestProcessor is not closing log stream during
-  shutdown (mahadev via phunt)
-
-  ZOOKEEPER-121_4. SyncRequestProcessor is not closing log stream during
-  shutdown (mahadev via breed)
-
-  ZOOKEEPER-586. c client does not compile under cygwin (phunt, mahadev, breed via breed)
-
-  ZOOKEEPER-624. The C Client cause core dump when receive error data from
-  Zookeeper Server (mahadev)
-
-  ZOOKEEPER-591. The C Client cannot exit properly in some situation (mahadev)
-
-  ZOOKEEPER-591_2. The C Client cannot exit properly in some situation
-  (mahadev via phunt)
-
-  ZOOKEEPER-709. bookkeeper build failing with missing factory
-  (phunt)
-
-  ZOOKEEPER-708. zkpython failing due to undefined symbol
-  deallocate_String_vector (mahadev via phunt)
-
-  ZOOKEEPER-436. Bookies should auto register to ZooKeeper (erwin tam & fpj via breed)
-
-  ZOOKEEPER-710. permanent ZSESSIONMOVED error after client app reconnects to zookeeper cluster (phunt via breed)
-
-  ZOOKEEPER-718. the fatjar is missing libraries (ben via mahadev)
-
-  ZOOKEEPER-717.  add a preferred list to the instancemanager (breed via
-  mahadev)
-
-IMPROVEMENTS:
-  ZOOKEEPER-473. cleanup junit tests to eliminate false positives due to
-  "socket reuse" and failure to close client (phunt via mahadev)
- 
-  ZOOKEEPER-488. Fix zkServer.sh to add clover.jar in classpath
-  (Giridharan Kesavan via gkesavan)  
-
-  ZOOKEEPER-516. add support for 10 minute test ie "pre-commit" test (phunt)
-
-  ZOOKEEPER-529. Use Ivy to pull dependencies and also generate pom (phunt
-  via mahadev)
-  
-  ZOOKEEPER-530. Memory corruption: Zookeeper c client IPv6 implementation
-  does not honor struct sockaddr_in6 size (isabel drost via mahadev)
-
-  ZOOKEEPER-549. Refactor Followers and related classes into a Peer->Follower
-  hierarchy in preparation for Observers (henry robinson via mahadev)
-
-  ZOOKEEPER-472.  Making DataNode not instantiate a HashMap when the node is
-  ephmeral (Erik Holstad via mahadev)
-
-  ZOOKEEPER-425. Add OSGi metadata to zookeeper.jar (david bosschaert via breed)
-
-  ZOOKEEPER-599. Changes to FLE and QuorumCnxManager to support Observers
-  (fpj via breed)
-
-  ZOOKEEPER-506. QuorumBase should use default leader election (fpj via breed)
-
-  ZOOKEEPER-633. Fetch netty using ivy for bookkeeper (giri via fpj)
-
-  ZOOKEEPER-544. improve client testability - allow test client to access
-  connected server location (phunt via breed)
-
-  ZOOKEEPER-426. Windows versions of zookeeper scripts
-  (David Bosschaert via breed)
-
-  ZOOKEEPER-638. upgrade ivy to 2.1.0 final from 2.1.0 release
-  candidate (phunt via breed)
-
-  ZOOKEEPER-648. Fix releaseaudit warning count to zero (phunt via henry)
-
-  ZOOKEEPER-626. ensure the c/java cli's print xid/sessionid/etc... in hex
-  (pat via mahadev)
-
-  ZOOKEEPER-655. StringBuffer -> StringBuilder - conversion of references as
-  necessary (Kay Kay via henry)
-
-  ZOOKEEPER-612. Make Zookeeper C client can be compiled by gcc of early
-  version (qian via mahadev)
-
-  ZOOKEEPER-456. CREATOR_ALL_ACL has unnecessary PERMS.ADMIN in the
-  declartion. (phunt via mahadev)
-
-  ZOOKEEPER-593.  java client api does not allow client to access negotiated
-  session timeout (phunt via mahadev)
-
-  ZOOKEEPER-507. BookKeeper client re-write (Utkarsh and ben via mahadev)
-
-  ZOOKEEPER-665. Add BookKeeper streaming documentation (flavio via mahadev)
-
-  ZOOKEEPER-664.  BookKeeper API documentation (flavio via mahadev)
-
-  ZOOKEEPER-607. improve bookkeeper overview (flavio via mahadev)
-
-  ZOOKEEPER-485. Need ops documentation that details supervision of ZK server
-  processes. (phunt via mahadev)
-
-  ZOOKEEPER-658. update forrest docs - AuthFLE no longer supported (flavio via
-  mahadev)
-
-  ZOOKEEPER-640. make build.xml more configurable to ease packaging for linux
-  distros (phunt via mahadev)
-
-  ZOOKEEPER-579. zkpython needs more test coverage for ACL code paths (henry
-  via mahadev)
-
-  ZOOKEEPER-688. explain session expiration better in the docs & faq (phunt
-  via mahadev)
-
-  ZOOKEEPER-663. hudson failure in ZKDatabaseCorruptionTest (mahadev via henryr)
-
-  ZOOKEEPER-543. Tests for ZooKeeper examples (steven via mahadev)
-
-  ZOOKEEPER-692. upgrade junit to latest version (4.8.1) (phunt via mahadev)
-
-  ZOOKEEPER-601. allow configuration of session timeout min/max bounds (phunt
-  via mahadev)
-
-NEW FEATURES:
-  ZOOKEEPER-539. generate eclipse project via ant target. (phunt via mahadev)
-
-  ZOOKEEPER-555. Add stat information to GetChildrenResponse. (Arni Jonson and
-  phunt via mahadev)
-
-  ZOOKEEPER-550.  Java Queue Recipe. (steven cheng via mahadev)
-
-  ZOOKEEPER-368. Observers: core functionality (henry robinson via mahadev)
-
-  ZOOKEEPER-496. zookeeper-tree utility for export, import and incremental
-  updates (anirban roy via breed)
-
-  ZOOKEEPER-572. add ability for operator to examine state of watches
-  currently registered with a server (phunt via mahadev)
-
-  ZOOKEEPER-678. Gui browser application to view and edit the contents of a
-  zookeeper instance (Colin Goodheart-Smithe via phunt)
-
-  ZOOKEEPER-635. Server supports listening on a specified network address (phunt via breed)
-  
-Release 3.2.0 - 2009-06-30
-
-Non-backward compatible changes:
-
-BUGFIXES: 
-  ZOOKEEPER-444. perms definition for PERMS_ALL differ in C and java (mahadev)
-
-Backward compatible changes:
-
-BUGFIXES: 
-  ZOOKEEPER-303. Bin scripts dont work on a Mac. (tom white via mahadev)
-  
-  ZOOKEEPER-330. zookeeper standalone server does not startup with just a
-  port and datadir. (chris darroch and mahadev)
-
-  ZOOKEEPER-319. add locking around auth info in zhandle_t.
-  (chris darroch via mahadev)
-
-  ZOOKEEPER-320. call auth completion in free_completions().
-  (chris darroch via mahadev)
-
-  ZOOKEEPER-334. bookkeeper benchmark (testclient.java) has compiling errors.
-  (flavio and mahadev)
-
-  ZOOKEEPER-281. autoreconf fails for /zookeeper-3.0.1/src/c/ (phunt)
-
-  ZOOKEEPER-318. remove locking in zk_hashtable.c or add locking in
-  collect_keys() (chris darroch via mahadev)
-
-  ZOOKEEPER-333. helgrind thread issues identified in mt c client code
-  (mahadev via phunt)
-
-  ZOOKEEPER-309. core dump using zoo_get_acl() (mahadev via phunt)
-
-  ZOOKEEPER-341.  regression in QuorumPeerMain, 
-  tickTime from config is lost, cannot start quorum (phunt via mahadev)
-
-  ZOOKEEPER-360. WeakHashMap in Bookie.java causes NPE (flavio via mahadev)
-  
-  ZOOKEEPER-362. Issues with FLENewEpochTest. (fix bug in Fast leader election)
-  (flavio via mahadev)
-
-  ZOOKEEPER-363. NPE when recovering ledger with no hint. (flavio via mahadev)
-
-  ZOOKEEPER-370. Fix critical problems reported by findbugs.
-  (flavio via mahadev)
-
-  ZOOKEEPER-347. zkfuse uses non-standard String. (patrick hunt via mahadev)
-
-  ZOOKEEPER-355. make validatePath non public in Zookeeper client api.
-  (phunt via mahadev)
-
-  ZOOKEEPER-374. Uninitialized struct variable in C causes warning which 
-  is treated as an error (phunt via mahadev)
-
-  ZOOKEEPER-337. improve logging in leader election lookForLeader method when
-  address resolution fails (phunt via mahadev)
-
-  ZOOKEEPER-367. RecoveryTest failure - "unreasonable length" IOException
-  (mahadev via phunt)
-
-  ZOOKEEPER-346. remove the kill command fro mthe client port.
-  (phunt via mahadev)
-
-  ZOOKEEPER-377. running ant cppunit tests, a failure still results in 
-  BUILD SUCCESSFUL (giri via mahadev)
-
-  ZOOKEEPER-382. zookeeper cpp tests fails on 64 bit machines with gcc 4.1.2
-  (mahadev via phunt)
-
-  ZOOKEEPER-365. javadoc is wrong for setLast in LedgerHandle
-  (flavio via phunt)
-
-  ZOOKEEPER-392. Change log4j properties in bookkeeper. (flavio via mahadev)
-
-  ZOOKEEPER-400. Issues with procedure to close ledger. (flavio)
-
-  ZOOKEEPER-405. nullpointer exception in zookeeper java shell.
-  (mahadev via breed)
-
-  ZOOKEEPER-410. address all findbugs warnings in client/server classes.
-  (phunt via breed)
-  
-  ZOOKEEPER-403. cleanup javac compiler warnings. (flavio via breed)
-
-  ZOOKEEPER-407. address all findbugs warnings in
-  org.apache.zookeeper.server.quorum.** packages.
-  (flavio via breed)
-
-  ZOOKEEPER-411. Building zookeeper fails on RHEL 5 64 bit during test-cppunit
-  (mahadev via phunt)
-
-  ZOOKEEPER-402. zookeeper c library segfaults on data for a node in zookeeper
-  being null. (mahadev via phunt)
-
-  ZOOKEEPER-415. zookeeper c tests hang. (mahadev via phunt)
-
-  ZOOKEEPER-385. crctest failed on hudson patch test (mahadev via phunt)
-
-  ZOOKEEPER-192.  trailing whitespace in config file can cause number format
-  exceptions (phunt via breed)
- 
-  ZOOKEEPER-409. address all findbugs warnings in jute related classes
-  (phunt via breed)
-
-  ZOOKEEPER-416. bookkeeper jar includes unnnecessary files.
-  (flavio via mahadev)
-
-  ZOOKEEPER-419. Reference counting bug in Python bindings causes abort errors
-  (henry robinson via phunt)
-
-  ZOOKEEPER-421. zkpython run_tests.sh is missing #!
-  (henry robinson via phunt)
-
-  ZOOKEEPER-406. address all findbugs warnings in persistence classes.
-  (phunt et al via breed)
-
-  ZOOKEEPER-435. allow "super" admin digest based auth to be configurable
-  (phunt via breed)
-
-  ZOOKEEPER-375. zoo_add_auth only retains most recent auth on re-sync.
-  (mahadev)
-
-  ZOOKEEPER-433.  getacl on root znode (/) fails. (phunt via mahadev)
-
-  ZOOKEEPER-408. address all findbugs warnings in persistence classes.
-  (phunt, mahadev, flavio via mahadev)
-
-  ZOOKEEPER-427. ZooKeeper server unexpectedly high CPU utilisation
-  (Sergey Zhuravlev via breed)
-
-  ZOOKEEPER-446. some traces of the host auth scheme left (breed via mahadev)
-
-  ZOOKEEPER-438.  addauth fails to register auth on new client that's not yet
-  connected (breed via mahadev)
-
-  ZOOKEEPER-448. png files do nto work with forrest. (mahadev)
-
-  ZOOKEEPER-417. stray message problem when changing servers
-  (breed via mahadev)
-
-  ZOOKEEPER-449. sesssionmoved in java code and ZCLOSING in C have the same
-  value. (mahadev)
-
-  ZOOKEEPER-452. zookeeper performance graph should have percentage of reads
-  rather than percentage of writes - zkperfRW-3.2.jpg (mahadev)
-
-  ZOOKEEPER-450. emphemeral cleanup not happening with session timeout.
-  (breed via mahadev)
-
-  ZOOKEEPER-453. Worker is not removed in QuorumCnxManager upon crash.
-  (flavio via mahadev)
-
-  ZOOKEEPER-454. allow compilation with jdk1.5 (phunt)
-
-  ZOOKEEPER-455. zookeeper c client crashes with chroot specified in the string.
-  (phunt via mahadev)
-
-  ZOOKEEPER-468. avoid compile warning in send_auth_info().
-
-IMPROVEMENTS:
-  ZOOKEEPER-308. improve the atomic broadcast performance 3x.
-  (breed via mahadev)
-
-  ZOOKEEPER-326. standalone server ignores tickTime configuration.
-  (chris darroch via mahadev)
-
-  ZOOKEEPER-279. Allow specialization of quorum config parsing
-  (e.g. variable expansion in zoo.cfg) (Jean-Daniel Cryans via phunt)
-
-  ZOOKEEPER-351. to run checkstyle (giridharan kesavan via mahadev)
-
-  ZOOKEEPER-350. to run rats for releaseaudit.
-  (giridharan kesavan via mahadev)
-
-  ZOOKEEPER-352. to add standard ant targets required by test-patch.sh script
-  (giridharan kesavan via mahadev)
-
-  ZOOKEEPER-353. javadoc warnings needs to be fixed.
-  (giridharan kesavan via mahadev) 
-
-  ZOOKEEPER-354.  to fix javadoc warning in the source files. (mahadev)
-
-  ZOOKEEPER-349. to automate patch testing. (giridharan kesavan via mahadev)
-
-  ZOOKEEPER-288. Cleanup and fixes to BookKeeper (flavio via mahadev)
- 
-  ZOOKEEPER-305.  Replace timers with semaphores in FLENewEpochTest.
-  (flavio via mahadev)
-
-  ZOOKEEPER-60. Get cppunit tests running as part of Hudson CI.
-  (girish via mahadev)
-
-  ZOOKEEPER-343. add tests that specifically verify the zkmain and 
-  qpmain classes. (phunt via mahadev)
-
-  ZOOKEEPER-361. integrate cppunit testing as part of hudson patch process.
-  (giri via mahadev)
-
-  ZOOKEEPER-373. One thread per bookie (flavio via mahadev) 
-
-  ZOOKEEPER-384. keeper exceptions missing path (phunt via mahadev)
-
-  ZOOKEEPER-380. bookkeeper should have a streaming api so that its easier to
-  store checpoints/snapshots in bookkeeper. (mahadev via flavio)
- 
-  ZOOKEEPER-389. add help/usage to the c shell cli.c (phunt via mahadev)
-
-  ZOOKEEPER-376. ant test target re-compiles cppunit code every time
-  (phunt via mahadev)
-
-  ZOOKEEPER-391. bookeeper mainline code should not be calling
-  printStackTrace. (flavio via mahadev) 
- 
-  ZOOKEEPER-300. zk jmx code is calling printStackTrace when creating bean
-  name (should not be) (phunt via mahadev)
-
-  ZOOKEEPER-94. JMX tests are needed to verify that the JMX MBeans work
-  properly (phunt via mahadev)
-
-  ZOOKEEPER-404. nightly build failed on hudson.
-  (henry robinson and pat via mahadev)
-
-  ZOOKEEPER-345. the CLIs should allow addAuth to be invoked.
-  (henry robinson via breed)
-
-  ZOOKEEPER-292. commit configure scripts (autotools) to svn for c projects and
-  include in release (phunt via breed)
-
-  ZOOKEEPER-383. Asynchronous version of createLedger(). (flavio via mahadev)
-
-  ZOOKEEPER-358. Throw exception when ledger does not exist. (flavio via breed)
-
-  ZOOKEEPER-431. Expose methods to ease ZK integration. (Jean-Daniel via breed)
-
-  ZOOKEEPER-396. race condition in zookeeper client library between
-  zookeeper_close and zoo_synchronous api. (mahadev)
-
-  ZOOKEEPER-196. doxygen comment for state argument of watcher_fn typedef and
-  implementation differ ("...one of the *_STATE constants, otherwise -1")
-  (breed via mahadev)
-
-  ZOOKEEPER-336. single bad client can cause server to stop accepting
-  connections (henry robinson via breed)
- 
-  ZOOKEEPER-434. the java shell should indicate connection status on command
-  prompt (henry robinson via breed)
-
-  ZOOKEEPER-437. Variety of Documentation Updates (grant via mahadev)
-
-  ZOOKEEPER-443. trace logging in watch notification not wrapped with
-  istraceneabled - inefficient (pat via mahadev)
-
-  ZOOKEEPER-432. Various improvements to zkpython bindings.
-  (henry via mahadev)
-
-  ZOOKEEPER-428. logging should be makred as warn rathen than error in
-  NIOServerCnxn. (phunt via mahadev)
-
-  ZOOKEEPER-422. Java CLI should support ephemeral and sequential node creation
-  (henry via breed)
-
-  ZOOKEEPER-315. add forrest docs for bookkeeper. (flavio via mahadev)
-
-  ZOOKEEPER-329. document how to integrate 3rd party authentication into ZK
-  server ACLs. (breed via mahadev)
-
-  ZOOKEEPER-356. Masking bookie failure during writes to a ledger
-  (flavio via breed)
-
-  ZOOKEEPER-327. document effects (latency) of storing large amounts of data
-  in znodes. (breed via mahadev)
-
-  ZOOKEEPER-264. docs should include a state transition diagram for client
-  state (breed via mahadev)
-
-  ZOOKEEPER-440. update the performance documentation in forrest
-  (breed via phunt)
-
-NEW FEATURES:
-
-  ZOOKEEPER-371. jdiff documentation included in build/release (giri via phunt)
-
-  ZOOKEEPER-78. added a high level protocol/feature - for easy Leader
-  Election or exclusive Write Lock creation (mahadev via phunt)
-
-  ZOOKEEPER-29. Flexible quorums (flavio via mahadev) 
-
-  ZOOKEEPER-378. perl binding for zookeeper (chris darroch via mahadev)
-
-  ZOOKEEPER-386. improve java cli shell. (henry robinson via mahadev)
-
-  ZOOKEEPER-36. REST access to ZooKeeper (phunt via mahadev)
-
-  ZOOKEEPER-395. Python bindings. (henry robinson via mahadev)
-
-  ZOOKEEPER-237. Add a Chroot request (phunt and mahadev)
-
-
-Release 3.1.0 - 2009-02-06
-  
-Non-backward compatible changes:
-
-BUGFIXES: 
-
-  ZOOKEEPER-255. zoo_set() api does not return stat datastructure.
-  (avery ching via mahadev)
-  
-  ZOOKEEPER-246. review error code definition in both source and docs. 
-  (pat via mahadev)
-
-Backward compatible changes:
-
-BUGFIXES: 
-  ZOOKEEPER-211. Not all Mock tests are working (ben via phunt)
-
-  ZOOKEEPER-223. change default level in root logger to INFO.
-  (pat via mahadev) 
-   
-  ZOOKEEPER-212. fix the snapshot to be asynchronous. (mahadev and ben)
-
-  ZOOKEEPER-213. fix programmer guide C api docs to be  in sync with latest
-  zookeeper.h (pat via mahadev)
-
-  ZOOKEEPER-219. fix events.poll timeout in watcher test to be longer.
-  (pat via mahadev)
-   
-  ZOOKEEPER-217. Fix errors in config to be thrown as Exceptions. (mahadev)
-
-  ZOOKEEPER-228. fix apache header missing in DBTest. (mahadev)
-
-  ZOOKEEPER-218. fix the error in the barrier example code. (pat via mahadev)
-
-  ZOOKEEPER-206. documentation tab should contain the version number and 
-  other small site changes. (pat via mahadev) 
-
-  ZOOKEEPER-226. fix exists calls that fail on server if node has null data.
-  (mahadev) 
-
-  ZOOKEEPER-204. SetWatches needs to be the first message after auth
-  messages to the server (ben via mahadev)
-  
-  ZOOKEEPER-208. Zookeeper C client uses API that are not thread safe,
-  causing crashes when multiple instances are active.
-  (austin shoemaker, chris daroch and ben reed via mahadev) 
-
-  ZOOKEEPER-227. gcc warning from recordio.h (chris darroch via mahadev)
-
-  ZOOKEEPER-232. fix apache licence header in TestableZookeeper (mahadev)
-
-  ZOOKEEPER-249. QuorumPeer.getClientPort() always returns -1.
-  (nitay joffe via mahadev)
-
-  ZOOKEEPER-248.  QuorumPeer should use Map interface instead of HashMap
-  implementation. (nitay joffe via mahadev)
-
-  ZOOKEEPER-241. Build of a distro fails after clean target is run.
-  (patrick hunt via mahadev)
-
-  ZOOKEEPER-245. update readme/quickstart to be release tar, rather than
-  source, based (patrick hunt via mahadev)
-
-  ZOOKEEPER-251. NullPointerException stopping and starting Zookeeper servers
-  (mahadev via phunt)
-
-  ZOOKEEPER-250. isvalidsnapshot should handle the case of 0 snapshot
-  files better. (mahadev via phunt)
- 
-  ZOOKEEPER-265. remove (deprecate) unused NoSyncConnected from KeeperState.
-  (phunt via mahadev)
-
-  ZOOKEEPER-273. Zookeeper c client build should not depend on CPPUNIT. (pat
-and runping via mahadev)
-
-  ZOOKEEPER-268.  tostring on jute generated objects can cause NPE. (pat via mahadev)
-
-  ZOOKEEPER-267.  java client incorrectly generating syncdisconnected event when in disconnected state. (pat via breed)
-
-  ZOOKEEPER-263. document connection host:port as comma separated list in forrest docs (pat via breed)
-  
-  ZOOKEEPER-275. Bug in FastLeaderElection. (flavio via mahadev)
-
-  ZOOKEEPER-272. getchildren can fail for large number of children. (mahadev)
-
-  ZOOKEEPER-16. Need to do path validation. (pat, mahadev) 
-
-  ZOOKEEPER-252. PurgeTxnLog is not handling the new dataDir directory
-  structure (mahadev via phunt)
-
-  ZOOKEEPER-291. regression for legacy code using KeeperException.Code 
-  constants (due to 246). (pat via mahadev)
-
-  ZOOKEEPER-255. zoo_set() api does not return stat datastructure.
-  (avery ching via mahadev)
-
-  ZOOKEEPER-293. zoo_set needs to be abi compatible (3.1 changed the
-signature), fix this by adding zoo_set2 (pat via mahadev)
-
-  ZOOKEEPER-302. Quote values in JMX objectnames. (tom and pat via mahadev)
-
-IMPROVEMENTS:
-   
-  ZOOKEEPER-64. Log system env information when initializing server and
-  client (pat via mahadev)
-
-  ZOOKEEPER-243. add SEQUENCE flag documentation to the programming guide.
-  (patrick hunt via mahadev)
-  
-   ZOOKEEPER-161. Content needed: "Designing a ZooKeeper Deployment"
-  (breed via phunt)
-   
-   ZOOKEEPER-247. fix formatting of C API in ACL section of programmer guide.
-   (patrick hunt via mahadev)
-
-   ZOOKEEPER-230. Improvements to FLE. (Flavio via mahadev)
-
-   ZOOKEEPER-225. c client should log an info message in zookeeper_init
-   detailing connection parameters. (pat via mahadev)
-
-   ZOOKEEPER-222.  print C client log message timestamp in human readable
-   form. (pat via mahadev) 
-  
-   ZOOKEEPER-256. support use of JMX to manage log4j configuration at runtime.
-   (pat via mahadev)
-
-   ZOOKEEPER-214. add new "stat reset" command to server admin port.
-   (pat via mahadev)
-
-   ZOOKEEPER-258. docs incorrectly state max client timeout as 60 seconds
-   (it's based on server ticktime). (phunt via mahadev)
-
-   ZOOKEEPER-135. Fat jar build target. (phunt and breed via mahadev)
-
-   ZOOKEEPER-234. Eliminate using statics to initialize the sever. Should
-   allow server to be more embeddable in OSGi enviorments. (phunt)
-
-   ZOOKEEPER-259. cleanup the logging levels used (use the correct level)
-   and messages generated. (phunt via breed)
-
-   ZOOKEEPER-210. Require Java 6. (phunt via breed)
-    
-   ZOOKEEPER-177.  needed: docs for JMX (phunt via mahadev)
-  
-   ZOOKEEPER-253. documentation of DataWatcher state transition is misleading
-regarding auto watch reset on reconnect. (phunt via mahadev)
-
-   ZOOKEEPER-269. connectionloss- add more documentation to detail. (phunt and
-flavio via mahadev)
-
-   ZOOKEEPER-260. document the recommended values for server id's
-   (mahadev via phunt)
-
-   ZOOKEEPER-215. expand system test environment (breed via phunt)
-
-   ZOOKEEPER-229. improve documentation regarding user's responsibility to
-   cleanup datadir (snaps/logs) (mahadev via phunt)
-
-   ZOOKEEPER-69. ZooKeeper logo
-   
-   ZOOKEEPER-286. Make GenerateLoad use InstanceContainers. (breed via mahadev)
-
-   ZOOKEEPER-220. programming guide watches section should clarify
-   server/clientlib role in data/child watch maint. (breed via phunt)
- 
-   ZOOKEEPER-289. add debug messages to nioserver select loop. (mahadev)
-
-NEW FEATURES:
-
-   ZOOKEEPER-276. Bookkeeper contribution (Flavio and Luca Telloli via mahadev)
-  
-   ZOOKEEPER-231. Quotas in ZooKeeper. (mahadev)
-
-Release 3.0.0 - 2008-10-21
-
-Non-backward compatible changes:
-
-  ZOOKEEPER-43. Server side of auto reset watches. (breed via mahadev)
-
-  ZOOKEEPER-132. Create Enum to replace CreateFlag in ZooKepper.create 
-  method (Jakob Homan via phunt)
-
-  ZOOKEEPER-139. Create Enums for WatcherEvent's KeeperState and EventType
-  (Jakob Homan via phunt)
-
-  ZOOKEEPER-18. keeper state inconsistency (Jakob Homan via phunt)
-
-  ZOOKEEPER-38. headers (version+) in log/snap files (Andrew Kornev and Mahadev
-  Konar via breed)
-
-  ZOOKEEPER-8. Stat enchaned to include num of children and size
-  (phunt)
-
-  ZOOKEEPER-6. List of problem identifiers in zookeeper.h
-  (phunt)
-
-  ZOOKEEPER-7. Use enums rather than ints for types and state
-  (Jakob Homan via mahadev)
-
-  ZOOKEEPER-27. Unique DB identifiers for servers and clients
-  (mahadev)
-
-  ZOOKEEPER-32. CRCs for ZooKeeper data
-  (mahadev)
-
-  ZOOKEEPER-33. Better ACL management
-  (mahadev)
-   
-Backward compatible changes:
-
-  BUGFIXES: 
-
-  ZOOKEEPER-203. fix datadir typo in releasenotes (phunt)
-
-  ZOOKEEPER-145. write detailed release notes for users migrating from 2.x
-  to 3.0 (phunt)
-
-  ZOOKEEPER-23. Auto reset of watches on reconnect (breed via phunt)
-
-  ZOOKEEPER-191. forrest docs for upgrade. (mahadev via phunt)
-
-  ZOOKEEPER-201. validate magic number when reading snapshot and transaction
-  logs (mahadev via phunt)
-
-  ZOOKEEPER-200. the magic number for snapshot and log must be different
-  (currently same) (phunt)
-
-  ZOOKEEPER-199. fix log messages in persistence code (mahadev via phunt)
-
-  ZOOKEEPER-197. create checksums for snapshots (mahadev via phunt)
-
-  ZOOKEEPER-198. apache license header missing from FollowerSyncRequest.java
-  (phunt)
-
-  ZOOKEEPER-5. Upgrade Feature in Zookeeper server. (mahadev via phunt)
-
-  ZOOKEEPER-194. Fix terminology in zookeeperAdmin.xml
-  (Flavio Paiva Junqueira)
-
-  ZOOKEEPER-151. Document change to server configuration
-  (Flavio Paiva Junqueira)
-
-  ZOOKEEPER-193. update java example doc to compile with latest zookeeper
-  (phunt)
-
-  ZOOKEEPER-187. CreateMode api docs missing (phunt)
-
-  ZOOKEEPER-186. add new "releasenotes.xml" to forrest documentation
-  (phunt)
-
-  ZOOKEEPER-190. Reorg links to docs and navs to docs into related sections
-  (robbie via phunt)
-
-  ZOOKEEPER-189. forrest build not validated xml of input documents
-  (robbie via phunt)
-
-  ZOOKEEPER-188. Check that election port is present for all servers
-  (Flavio Paiva Junqueira via phunt)
-
-  ZOOKEEPER-185. Improved version of FLETest (Flavio Paiva Junqueira)
-
-  ZOOKEEPER-184. tests: An explicit include derective is needed for the usage
-  of memcpy(), memset(), strlen(), strdup() and free() functions
-  (Maxim P. Dementiev via phunt)
-
-  ZOOKEEPER-183. Array subscript is above array bounds in od_completion(),
-  src/cli.c. (Maxim P. Dementiev via phunt)
-
-  ZOOKEEPER-182.  zookeeper_init accepts empty host-port string and returns
-  valid pointer to zhandle_t. (Maxim P. Dementiev via phunt)
-
-  ZOOKEEPER-17. zookeeper_init doc needs clarification (phunt)
-
-  ZOOKEEPER-181. Some Source Forge Documents did not get moved over: 
-  javaExample, zookeeperTutorial, zookeeperInternals (robbie via phunt)
-
-  ZOOKEEPER-180. Placeholder sections needed in document for new topics that
-  the umbrella jira discusses (robbie via phunt)
-
-  ZOOKEEPER-179. Programmer's Guide "Basic Operations" section is missing 
-  content (robbie via phunt)
-
-  ZOOKEEPER-178. FLE test. (Flavio Paiva Junqueira)
-
-  ZOOKEEPER-159. Cover two corner cases of leader election
-  (Flavio Paiva Junqueira via phunt)
-
-  ZOOKEEPER-156. update programmer guide with acl details from old wiki page
-  (phunt)
-
-  ZOOKEEPER-154. reliability graph diagram in overview doc needs context
-  (phunt)
-
-  ZOOKEEPER-157. Peer can't find existing leader (Flavio Paiva Junqueira)
-
-  ZOOKEEPER-155. improve "the zookeeper project" section of overview doc
-  (phunt)
-
-  ZOOKEEPER-140. Deadlock in QuorumCnxManager (Flavio Paiva Junqueira)
-
-  ZOOKEEPER-147. This is version of the documents with most of the [tbd...]
-  scrubbed out (robbie via phunt)
-
-  ZOOKEEPER-150. zookeeper build broken (mahadev via phunt)
-
-  ZOOKEEPER-136. sync causes hang in all followers of quorum. (breed)
-
-  ZOOKEEPER-134. findbugs cleanup (phunt)
-
-  ZOOKEEPER-133. hudson tests failing intermittently (phunt)
-
-  ZOOKEEPER-144. add tostring support for watcher event, and enums for event
-  type/state (Jakob Homan via phunt)
-
-  ZOOKEEPER-21. Improve zk ctor/watcher (state transition) docs (phunt)
-
-  ZOOKEEPER-142. Provide Javadoc as to the maximum size of the data byte 
-  array that may be stored within a znode (Jakob Homan via phunt)
-
-  ZOOKEEPER-93. Create Documentation for Zookeeper (phunt)
-
-  ZOOKEEPER-117. threading issues in Leader election (fpj via breed)
-
-  ZOOKEEPER-137. client watcher objects can lose events (phunt via breed)
-
-  ZOOKEEPER-131. Old leader election can elect a dead leader over and over
-  again (breed via mahadev)
-
-  ZOOKEEPER-130. update build.xml to support apache release process
-  (phunt via mahadev)
-
-  ZOOKEEPER-118. findbugs flagged switch statement in 
-  followerrequestprocessor.run() (Flavio Paiva Junqueira via phunt)
-
-  ZOOKEEPER-115. Potential NPE in QuorumCnxManager
-  (Flavio Paiva Junqueira)
-
-  ZOOKEEPER-114. cleanup ugly event messages in zookeeper client 
-  (Jakob Homan)
-
-  ZOOKEEPER-112. src/java/main ZooKeeper.java has test code embedded into it.
-  (phunt)
-
-  ZOOKEEPER-39. Use Watcher objects rather than boolean on read operations.
-  (Andrew Kornev)
-
-  ZOOKEEPER-97. supports optional output directory in code generator. (Hiram
-  Chirino via phunt)
-
-  ZOOKEEPER-101. Integrate ZooKeeper with "violations" feature on hudson
-  (phunt)
- 
-  ZOOKEEPER-105. Catch Zookeeper exceptions and print on the stderr. 
-  (Anthony Urso via Mahadev)
- 
-  ZOOKEEPER-42. Change Leader Election to fast tcp. (Flavio Paiva Junqueira
-  via phunt)
-
-  ZOOKEEPER-48. auth_id now handled correctly when no auth ids present
-  (Benjamin Reed via phunt)
- 
-  ZOOKEEPER-44. Create sequence flag children with prefixes of 0's so that
-  they can be lexicographically sorted. (Jakob Homan via mahadev)
-  
-  ZOOKEEPER-108. Fix sync operation reordering on a Quorum. 
-  (Flavio Paiva Junqueira via Mahadev)
-
-  ZOOKEEPER-25. Fuse module for Zookeeper. (Swee Lim, Bart, Patrick Hunt and
-  Andrew Kornev via Mahadev)
-
-  ZOOKEEPER-58. Race condition on ClientCnxn.java (breed)
-
-  ZOOKEEPER-56. Add clover support to build.xml. (Patrick Hunt via mahadev)
-
-  ZOOKEEPER-75. register the ZooKeeper mailing lists with nabble.com (phunt)
-  
-  ZOOKEEPER-54. remove sleeps in the tests. (phunt)
-
-  ZOOKEEPER-55. build.xml failes to retrieve a release number from SVN and 
-  the ant target "dist" fails (Andrew Kornev)
-
-  ZOOKEEPER-89. invoke WhenOwnerListener.whenNotOwner() when the ZK 
-  connection fails (james strachan)
-
-  ZOOKEEPER-90. invoke WhenOwnerListener.whenNotOwner() when the ZK
-  session expires and the znode is the leader (james strachan)
-
-  ZOOKEEPER-82. Make the ZooKeeperServer more DI friendly. (Hiram Chirino via
-  mahadev)
-
-  ZOOKEEPER-110. Build script relies on svnant, which is not compatible 
-  with subversion 1.5 working copies (Jakob Homan)
-
-  ZOOKEEPER-111. Significant cleanup of existing tests. (Patrick Hunt via
-  mahadev)
- 
-  ZOOKEEPER-122. Fix  NPE in jute's Utils.toCSVString. (Anthony Urso via
-  mahadev)
-  
- ZOOKEEPER-123. Fix  the wrong class is specified for the logger. (Jakob Homan
- via mahadev)
-
- ZOOKEEPER-2. Fix synchronization issues in QuorumPeer and FastLeader
- election. (Flavio Paiva Junqueira via mahadev)
-
- ZOOKEEPER-125. Remove unwanted class declaration in FastLeaderElection. 
- (Flavio Paiva Junqueira via mahadev)
-
- ZOOKEEPER-61. Address (remove) use of sleep(#) in client/server test cases.
- (phunt)
-
- ZOOKEEPER-75. cleanup the library directory (phunt)
-
- ZOOKEEPER-109. cleanup of NPE and Resource issue nits found by static
- analysis (phunt)
-
- ZOOKEEPER-76. Commit 677109 removed the cobertura library, but not the 
- build targets. (phunt)
-
- ZOOKEEPER-63. Race condition in client close() operation. (phunt via breed)
-
- ZOOKEEPER-70. Add skeleton forrest doc structure for ZooKeeper (phunt)
-
- ZOOKEEPER-79. Document jacob's leader election on the wiki recipes page 
- (Flavio Junqueira)
-
- ZOOKEEPER-73. Move ZK wiki from SourceForge to Apache (phunt)
-
- ZOOKEEPER-72. Initial creation/setup of ZooKeeper ASF site. (phunt)
-
- ZOOKEEPER-71. Determine what to do re ZooKeeper Changelog(s) (mahadev)
-
- ZOOKEEPER-68. parseACLs in ZooKeeper.java fails to parse elements of ACL,
- should be lastIndexOf rather than IndexOf (mahadev)
-
- ZOOKEEPER-130. update build.xml to support apache release process. 
- (phunt via mahadev)
-
- ZOOKEEPER-131. Fix Old leader election can elect a dead leader over and over
- again. (breed via mahadev)
-
- ZOOKEEPER-137. client watcher objects can lose events (Patrick Hunt via breed)
-
- ZOOKEEPER-117. threading issues in Leader election (Flavio Junqueira and
- Patrick Hunt via breed)
-
- ZOOKEEPER-128. test coverage on async client operations needs to be improved
- (phunt)
-
- ZOOKEEPER-127.  Use of non-standard election ports in config breaks services
- (Mark Harwood and Flavio Junqueira via breed)
-
- ZOOKEEPER-53. tests failing on solaris. (phunt)
-
- ZOOKEEPER-172. FLE Test (Flavio Junqueira via breed)
-
- ZOOKEEPER-41. Sample startup script (mahadev)
-
- ZOOKEEPER-33. Better ACL management (Mahadev Konar)
-
- ZOOKEEPER-49. SetACL does not work (breed)
-
- ZOOKEEPER-20. Child watches are not triggered when the node is deleted
- (phunt)
-
- ZOOKEEPER-15. handle failure better in build.xml:test (phunt)
-
- ZOOKEEPER-11. ArrayList is used instead of List (phunt)
-
- ZOOKEEPER-45. Restructure the SVN repository after initial import (phunt)
-
- ZOOKEEPER-1. Initial ZooKeeper code contribution from Yahoo! (phunt)
diff --git a/NOTICE.txt b/NOTICE.txt
index 7e4c7de..80d0188 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -1,6 +1,100 @@
 Apache ZooKeeper
-Copyright 2009-2016 The Apache Software Foundation
+Copyright 2009-2017 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
 
+This product includes software developed by
+The Netty Project (http://netty.io/)
+Copyright 2011 The Netty Project
+
+The Netty NOTICE file contains the following items:
+This product contains the extensions to Java Collections Framework which has
+been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene:
+
+  * LICENSE:
+    * license/LICENSE.jsr166y.txt (Public Domain)
+  * HOMEPAGE:
+    * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/
+    * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/
+
+This product contains a modified version of Robert Harder's Public Domain
+Base64 Encoder and Decoder, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.base64.txt (Public Domain)
+  * HOMEPAGE:
+    * http://iharder.sourceforge.net/current/java/base64/
+
+This product contains a modified version of 'JZlib', a re-implementation of
+zlib in pure Java, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.jzlib.txt (BSD Style License)
+  * HOMEPAGE:
+    * http://www.jcraft.com/jzlib/
+
+This product contains a modified version of 'Webbit', a Java event based
+WebSocket and HTTP server:
+
+  * LICENSE:
+    * license/LICENSE.webbit.txt (BSD License)
+  * HOMEPAGE:
+    * https://github.com/joewalnes/webbit
+
+This product optionally depends on 'Protocol Buffers', Google's data
+interchange format, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.protobuf.txt (New BSD License)
+  * HOMEPAGE:
+    * http://code.google.com/p/protobuf/
+
+This product optionally depends on 'Bouncy Castle Crypto APIs' to generate
+a temporary self-signed X.509 certificate when the JVM does not provide the
+equivalent functionality.  It can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.bouncycastle.txt (MIT License)
+  * HOMEPAGE:
+    * http://www.bouncycastle.org/
+
+This product optionally depends on 'SLF4J', a simple logging facade for Java,
+which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.slf4j.txt (MIT License)
+  * HOMEPAGE:
+    * http://www.slf4j.org/
+
+This product optionally depends on 'Apache Commons Logging', a logging
+framework, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.commons-logging.txt (Apache License 2.0)
+  * HOMEPAGE:
+    * http://commons.apache.org/logging/
+
+This product optionally depends on 'Apache Log4J', a logging framework,
+which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.log4j.txt (Apache License 2.0)
+  * HOMEPAGE:
+    * http://logging.apache.org/log4j/
+
+This product optionally depends on 'JBoss Logging', a logging framework,
+which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.jboss-logging.txt (GNU LGPL 2.1)
+  * HOMEPAGE:
+    * http://anonsvn.jboss.org/repos/common/common-logging-spi/
+
+This product optionally depends on 'Apache Felix', an open source OSGi
+framework implementation, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.felix.txt (Apache License 2.0)
+  * HOMEPAGE:
+    * http://felix.apache.org/
diff --git a/bin/zkCli.cmd b/bin/zkCli.cmd
index 0ffa030..2feb815 100755
--- a/bin/zkCli.cmd
+++ b/bin/zkCli.cmd
@@ -1,24 +1,24 @@
- at echo off
-REM Licensed to the Apache Software Foundation (ASF) under one or more
-REM contributor license agreements.  See the NOTICE file distributed with
-REM this work for additional information regarding copyright ownership.
-REM The ASF licenses this file to You under the Apache License, Version 2.0
-REM (the "License"); you may not use this file except in compliance with
-REM the License.  You may obtain a copy of the License at
-REM
-REM     http://www.apache.org/licenses/LICENSE-2.0
-REM
-REM Unless required by applicable law or agreed to in writing, software
-REM distributed under the License is distributed on an "AS IS" BASIS,
-REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-REM See the License for the specific language governing permissions and
-REM limitations under the License.
-
-setlocal
-call "%~dp0zkEnv.cmd"
-
-set ZOOMAIN=org.apache.zookeeper.ZooKeeperMain
-call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% %*
-
-endlocal
-
+ at echo off
+REM Licensed to the Apache Software Foundation (ASF) under one or more
+REM contributor license agreements.  See the NOTICE file distributed with
+REM this work for additional information regarding copyright ownership.
+REM The ASF licenses this file to You under the Apache License, Version 2.0
+REM (the "License"); you may not use this file except in compliance with
+REM the License.  You may obtain a copy of the License at
+REM
+REM     http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+
+setlocal
+call "%~dp0zkEnv.cmd"
+
+set ZOOMAIN=org.apache.zookeeper.ZooKeeperMain
+call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% %*
+
+endlocal
+
diff --git a/bin/zkEnv.cmd b/bin/zkEnv.cmd
index 41eed11..3f74683 100755
--- a/bin/zkEnv.cmd
+++ b/bin/zkEnv.cmd
@@ -1,49 +1,49 @@
- at echo off
-REM Licensed to the Apache Software Foundation (ASF) under one or more
-REM contributor license agreements.  See the NOTICE file distributed with
-REM this work for additional information regarding copyright ownership.
-REM The ASF licenses this file to You under the Apache License, Version 2.0
-REM (the "License"); you may not use this file except in compliance with
-REM the License.  You may obtain a copy of the License at
-REM
-REM     http://www.apache.org/licenses/LICENSE-2.0
-REM
-REM Unless required by applicable law or agreed to in writing, software
-REM distributed under the License is distributed on an "AS IS" BASIS,
-REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-REM See the License for the specific language governing permissions and
-REM limitations under the License.
-
-set ZOOCFGDIR=%~dp0%..\conf
-set ZOO_LOG_DIR=%~dp0%..
-set ZOO_LOG4J_PROP=INFO,CONSOLE
-
-REM for sanity sake assume Java 1.6
-REM see: http://java.sun.com/javase/6/docs/technotes/tools/windows/java.html
-
-REM add the zoocfg dir to classpath
-set CLASSPATH=%ZOOCFGDIR%
-
-REM make it work in the release
-SET CLASSPATH=%~dp0..\*;%~dp0..\lib\*;%CLASSPATH%
-
-REM make it work for developers
-SET CLASSPATH=%~dp0..\build\classes;%~dp0..\build\lib\*;%CLASSPATH%
-
-set ZOOCFG=%ZOOCFGDIR%\zoo.cfg
-
- at REM setup java environment variables
-
-if not defined JAVA_HOME (
-  echo Error: JAVA_HOME is not set.
-  goto :eof
-)
-
-set JAVA_HOME=%JAVA_HOME:"=%
-
-if not exist "%JAVA_HOME%"\bin\java.exe (
-  echo Error: JAVA_HOME is incorrectly set.
-  goto :eof
-)
-
-set JAVA="%JAVA_HOME%"\bin\java
+ at echo off
+REM Licensed to the Apache Software Foundation (ASF) under one or more
+REM contributor license agreements.  See the NOTICE file distributed with
+REM this work for additional information regarding copyright ownership.
+REM The ASF licenses this file to You under the Apache License, Version 2.0
+REM (the "License"); you may not use this file except in compliance with
+REM the License.  You may obtain a copy of the License at
+REM
+REM     http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+
+set ZOOCFGDIR=%~dp0%..\conf
+set ZOO_LOG_DIR=%~dp0%..
+set ZOO_LOG4J_PROP=INFO,CONSOLE
+
+REM for sanity sake assume Java 1.6
+REM see: http://java.sun.com/javase/6/docs/technotes/tools/windows/java.html
+
+REM add the zoocfg dir to classpath
+set CLASSPATH=%ZOOCFGDIR%
+
+REM make it work in the release
+SET CLASSPATH=%~dp0..\*;%~dp0..\lib\*;%CLASSPATH%
+
+REM make it work for developers
+SET CLASSPATH=%~dp0..\build\classes;%~dp0..\build\lib\*;%CLASSPATH%
+
+set ZOOCFG=%ZOOCFGDIR%\zoo.cfg
+
+ at REM setup java environment variables
+
+if not defined JAVA_HOME (
+  echo Error: JAVA_HOME is not set.
+  goto :eof
+)
+
+set JAVA_HOME=%JAVA_HOME:"=%
+
+if not exist "%JAVA_HOME%"\bin\java.exe (
+  echo Error: JAVA_HOME is incorrectly set.
+  goto :eof
+)
+
+set JAVA="%JAVA_HOME%"\bin\java
diff --git a/bin/zkServer.cmd b/bin/zkServer.cmd
index 6b4cf02..5bca840 100755
--- a/bin/zkServer.cmd
+++ b/bin/zkServer.cmd
@@ -1,24 +1,24 @@
- at echo off
-REM Licensed to the Apache Software Foundation (ASF) under one or more
-REM contributor license agreements.  See the NOTICE file distributed with
-REM this work for additional information regarding copyright ownership.
-REM The ASF licenses this file to You under the Apache License, Version 2.0
-REM (the "License"); you may not use this file except in compliance with
-REM the License.  You may obtain a copy of the License at
-REM
-REM     http://www.apache.org/licenses/LICENSE-2.0
-REM
-REM Unless required by applicable law or agreed to in writing, software
-REM distributed under the License is distributed on an "AS IS" BASIS,
-REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-REM See the License for the specific language governing permissions and
-REM limitations under the License.
-
-setlocal
-call "%~dp0zkEnv.cmd"
-
-set ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain
-echo on
-call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %*
-
-endlocal
+ at echo off
+REM Licensed to the Apache Software Foundation (ASF) under one or more
+REM contributor license agreements.  See the NOTICE file distributed with
+REM this work for additional information regarding copyright ownership.
+REM The ASF licenses this file to You under the Apache License, Version 2.0
+REM (the "License"); you may not use this file except in compliance with
+REM the License.  You may obtain a copy of the License at
+REM
+REM     http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+
+setlocal
+call "%~dp0zkEnv.cmd"
+
+set ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain
+echo on
+call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %*
+
+endlocal
diff --git a/build.xml b/build.xml
index 8562000..e584294 100644
--- a/build.xml
+++ b/build.xml
@@ -20,7 +20,8 @@
 <project name="ZooKeeper" default="jar" 
 xmlns:ivy="antlib:org.apache.ivy.ant"
 xmlns:artifact="antlib:org.apache.maven.artifact.ant"
-xmlns:maven="antlib:org.apache.maven.artifact.ant">
+xmlns:maven="antlib:org.apache.maven.artifact.ant"
+xmlns:cs="antlib:com.puppycrawl.tools.checkstyle">
 
     <!-- read build.properties from the basedir if any -->
     <property file="${basedir}/build.properties" />
@@ -30,14 +31,14 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
 
     <property environment="env"/>
     
-    <property name="version" value="3.4.9" />
+    <property name="version" value="3.4.10" />
     <property name="final.name" value="${name}-${version}"/>
     <property name="revision.dir" value="${basedir}/.revision" />
     <property name="revision.properties" value="revision.properties" />
     <property file="${basedir}/src/java/${revision.properties}" />
     
-    <property name="javac.target" value="1.5" />
-    <property name="javac.source" value="1.5" />
+    <property name="javac.target" value="1.6" />
+    <property name="javac.source" value="1.6" />
 
     <property name="src.dir" value="${basedir}/src" />
     <property name="java.src.dir" value="${src.dir}/java/main" />
@@ -75,6 +76,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
     <property name="test.data.upgrade.dir" value="${test.data.dir}/upgrade" />
     <property name="test.data.invalid.dir" value="${test.data.dir}/invalidsnap" />
     <property name="test.data.buffersize.dir" value="${test.data.dir}/buffersize" />
+    <property name="test.data.kerberos.dir" value="${test.data.dir}/kerberos" />
     <property name="test.cppunit.dir" value="${test.java.build.dir}/test-cppunit"/>
     <property name="test.tmp.dir" value="${test.java.build.dir}/tmp" />
     <property name="test.output" value="no" />
@@ -83,6 +85,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
     <property name="test.junit.fork.mode" value="perTest" />
     <property name="test.junit.printsummary" value="yes" />
     <property name="test.junit.haltonfailure" value="no" />
+    <property name="test.junit.failbuild.ontestfailure" value="true" />
     <property name="config.dir" value="${src.dir}/java/test/config" />
     <property name="test.junit.maxmem" value="512m" />
     <property name="test.quick" value="no" />
@@ -107,7 +110,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
 
     <property name="ivy.version" value="2.4.0"/>
     <property name="ivy.url"
-              value="http://repo2.maven.org/maven2/org/apache/ivy/ivy" />
+              value="https://repo1.maven.org/maven2/org/apache/ivy/ivy" />
     <property name="ivy.home" value="${user.home}/.ant" />
     <property name="ivy.lib" value="${build.dir}/lib"/>
     <property name="ivy.package.lib" value="${build.dir}/package/lib"/>
@@ -115,7 +118,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
     <property name="ivy.jdiff.lib" value="${build.dir}/jdiff/lib"/>
     <property name="ivysettings.xml" value="${basedir}/ivysettings.xml"/>
     
-    <property name="mvnrepo" value="http://repo2.maven.org/maven2"/>
+    <property name="mvnrepo" value="https://repo1.maven.org/maven2"/>
     <property name="tsk.org" value="/org/apache/maven/maven-ant-tasks/"/>
     <property name="ant-task.version" value="2.1.3"/>
     <property name="ant_task_repo_url"
@@ -151,6 +154,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
     <property name="patch.cmd" value="patch"/>
     <property name="make.cmd" value="make"/>
     <property name="test_patch_sh" value="${test.src.dir}/bin/test-patch.sh"/>
+    <property name="test_pullrequest_sh" value="${test.src.dir}/bin/test-github-pr.sh"/>
 
 	<!-- jdiff.home property set -->
     <property name="jdiff.home" value="${ivy.jdiff.lib}"/>
@@ -308,7 +312,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
             includes="org/apache/zookeeper/version/util/**" debug="on" />
     </target>
     
-    <target name="svn-revision" unless="lastRevision">
+    <target name="git-revision" unless="lastRevision">
         <mkdir dir="${revision.dir}" />
         <condition property="shell.name" value="cmd" else="sh">
       	    <os family="windows"/>
@@ -323,7 +327,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
         <property file="${revision.dir}/${revision.properties}" />
     </target>
     
-    <target name="version-info" depends="ver-gen,svn-revision">
+    <target name="version-info" depends="ver-gen,git-revision">
         <mkdir dir="${src_generated.dir}" />
         <java classname="org.apache.zookeeper.version.util.VerGen" fork="true" 
                 dir="${src_generated.dir}">
@@ -610,7 +614,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
         </jar>
     </target>
 
-	<!-- ====================================================== -->
+    <!-- ====================================================== -->
     <!-- Make zookeeper-sources.jar                                 -->
     <!-- ====================================================== -->
     <target name="src-jar" depends="build-generated">
@@ -762,6 +766,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
 
       <copy todir="${dist.dir}/src" includeEmptyDirs="true">
         <fileset dir="src" excludes="**/*.template **/docs/build/**/* **/ivy*.jar"/>
+        <fileset file="src/pom.template"/>
       </copy>
   	  
       <chmod perm="ugo+x" type="file" parallel="false">
@@ -880,6 +885,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
 
       <copy todir="${dist.dir}/src" includeEmptyDirs="true">
         <fileset dir="src" excludes="**/*.template **/docs/build/**/* **/ivy*.jar"/>
+        <fileset file="src/pom.template"/>
       </copy>
 
       <copy todir="${dist.dir}/${package.share.dir}/templates/conf">
@@ -1242,6 +1248,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
         <delete dir="${test.data.upgrade.dir}" />
         <delete dir="${test.data.invalid.dir}" />
         <delete dir="${test.data.buffersize.dir}" />
+        <delete dir="${test.data.kerberos.dir}" />
         <delete dir="${test.data.dir}" />
         <mkdir dir="${test.log.dir}" />
         <mkdir dir="${test.tmp.dir}" />
@@ -1258,7 +1265,10 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
         <copy todir="${test.data.buffersize.dir}">
             <fileset dir="${basedir}/src/java/test/data/buffersize"/>
         </copy>
-       
+        <mkdir dir="${test.data.kerberos.dir}" />
+        <copy todir="${test.data.kerberos.dir}">
+            <fileset dir="${basedir}/src/java/test/data/kerberos"/>
+        </copy>
     </target>
 
     <condition property="quicktest">
@@ -1317,6 +1327,9 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
             <fileset dir="${test.src.dir}" includes="**/${testcase}.java"/>
           </batchtest>
         </junit>
+        <antcall target="fail.build.on.test.failure"/>
+    </target>
+    <target name="fail.build.on.test.failure" if="${test.junit.failbuild.ontestfailure}" >
         <fail if="tests.failed">Tests failed!</fail>
     </target>
 
@@ -1560,7 +1573,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
                 <fileset dir="${test.src.dir}" includes="**/${testcase}.java"/>
             </batchtest>
         </junit>
-        <fail if="tests.failed">Tests failed!</fail>
+        <antcall target="fail.build.on.test.failure"/>
     </target>
 
     <target name="cobertura-report" depends="cobertura-test">
@@ -1575,32 +1588,20 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
         </cobertura-report>
     </target>
 
-    <target name="checkstyle" depends="checkstyle.check, set-checkstyle-classpath" if="checkstyle.home" 
-			description="Run optional third-party tool targets">
-    	<taskdef resource="checkstyletask.properties">
-        	<classpath refid="checkstyle-classpath"/>
-        </taskdef>
-        <mkdir dir="${test.java.build.dir}"/>
-        <checkstyle config="${test.src.dir}/checkstyle.xml" failOnViolation="false">
-        	<fileset dir="${src.dir}/java" includes="**/*.java" excludes="**/generated/**"/>
-        	<formatter type="xml" toFile="${test.java.build.dir}/checkstyle-errors.xml"/>
-        </checkstyle>
-        <xslt style="${test.src.dir}/checkstyle-noframes-sorted.xsl" in="${test.java.build.dir}/checkstyle-errors.xml"
-        	out="${test.java.build.dir}/checkstyle-errors.html"/>
-    </target>
-
-    <target name="checkstyle.check" unless="checkstyle.home">
-    	<fail message="'checkstyle.home' is not defined. Please pass -Dcheckstyle.home=<base of checkstyle installation> 
-		to Ant on the command-line." />
+    <target name="checkstyle" depends="ivy-retrieve-test" description="Run Checkstyle coding standard checks">
+      <taskdef resource="checkstyletask.properties" uri="antlib:com.puppycrawl.tools.checkstyle">
+        <classpath>
+          <fileset dir="${ivy.test.lib}" includes="*.jar" />
+        </classpath>
+      </taskdef>
+      <mkdir dir="${test.java.build.dir}"/>
+      <cs:checkstyle config="${test.src.dir}/checkstyle.xml" failOnViolation="false">
+        <fileset dir="${src.dir}/java" includes="**/*.java" excludes="**/generated/**"/>
+        <formatter type="xml" toFile="${test.java.build.dir}/checkstyle-errors.xml"/>
+      </cs:checkstyle>
+      <xslt style="${test.src.dir}/checkstyle-noframes-sorted.xsl" in="${test.java.build.dir}/checkstyle-errors.xml"
+            out="${test.java.build.dir}/checkstyle-errors.html"/>
     </target>
-    
-    <target name="set-checkstyle-classpath">
-    	<path id="checkstyle-classpath">
-        	<fileset dir="${checkstyle.home}">
-			<include name="**/*.jar"/>
-         	</fileset>
- 	</path>
-    </target>	
 
     <!-- ================================================================== -->
     <!-- Perform audit activities for the release                           -->
@@ -1698,6 +1699,27 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
     	</exec>
      </target>
 
+   <target name="qa-test-pullrequest" depends="findbugs.check,forrest.check">
+        <exec executable="bash" failonerror="true">
+                <arg value="${test_pullrequest_sh}"/>
+                <arg value="QABUILD"/>
+                <arg value="${scratch.dir}"/>
+                <arg value="${ps.cmd}"/>
+                <arg value="${wget.cmd}"/>
+                <arg value="${jiracli.cmd}"/>
+                <arg value="${git.cmd}"/>
+                <arg value="${grep.cmd}"/>
+                <arg value="${patch.cmd}"/>
+                <arg value="${findbugs.home}"/>
+                <arg value="${forrest.home}"/>
+                <arg value="${basedir}"/>
+                <arg value="${jira.passwd}"/>
+                <arg value="${java5.home}"/>
+                <arg value="${curl.cmd}"/>
+        </exec>
+     </target>
+
+
      <!-- this target runs the hudson trunk build -->
      <target name="hudson-test-trunk" depends="docs,tar,findbugs"/>
 
@@ -1786,7 +1808,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
              description="Create eclipse project files">
        <ivy:resolve useOrigin="true" conf="test"/>
        <ivy:cachepath pathid="default.path.id" conf="default" />
-       <ivy:cachepath pathid="junit.path.id" conf="test" transitive="false"/>
+       <ivy:cachepath pathid="junit.path.id" conf="test" />
        <taskdef name="eclipse"
                 classname="prantl.ant.eclipse.EclipseTask"
                 classpathref="java.classpath" />
diff --git a/ivy.xml b/ivy.xml
index 95b0e5a..1a9af76 100644
--- a/ivy.xml
+++ b/ivy.xml
@@ -58,7 +58,7 @@
     <dependency org="junit" name="junit" rev="4.8.1" conf="test->default"/>
      <dependency org="org.mockito" name="mockito-all" rev="1.8.2"
                conf="test->default"/>
-    <dependency org="checkstyle" name="checkstyle" rev="5.0"
+    <dependency org="com.puppycrawl.tools" name="checkstyle" rev="6.1.1"
                 conf="test->default"/>
     <dependency org="commons-collections" name="commons-collections" 
                 rev="3.2.2" conf="test->default"/>
@@ -74,6 +74,42 @@
                 rev="2.4" conf="releaseaudit->default"/>
     <dependency org="commons-collections" name="commons-collections" 
                 rev="3.2.2" conf="releaseaudit->default"/>
+
+    <dependency org="commons-io" name="commons-io" rev="2.4"
+                conf="test->default"/>
+
+
+    <!-- Apache directory server project, org.apache.directory.* packages for miniKdc tests -->
+    <dependency org="org.apache.directory.server" name="apacheds-core-api" rev="2.0.0-M15" conf="test->default">
+        <exclude org="org.apache.directory.api" name="api-ldap-schema-data"/>
+    </dependency>
+    <dependency org="org.apache.directory.server" name="apacheds-interceptor-kerberos" rev="2.0.0-M15" conf="test->default">
+        <exclude org="org.apache.directory.api" name="api-ldap-schema-data"/>
+    </dependency>
+    <dependency org="org.apache.directory.server" name="apacheds-protocol-shared" rev="2.0.0-M15" conf="test->default">
+        <exclude org="org.apache.directory.api" name="api-ldap-schema-data"/>
+    </dependency>
+    <dependency org="org.apache.directory.server" name="apacheds-protocol-kerberos" rev="2.0.0-M15" conf="test->default">
+        <exclude org="org.apache.directory.api" name="api-ldap-schema-data"/>
+    </dependency>
+    <dependency org="org.apache.directory.server" name="apacheds-ldif-partition" rev="2.0.0-M15" conf="test->default">
+        <exclude org="org.apache.directory.api" name="api-ldap-schema-data"/>
+    </dependency>
+    <dependency org="org.apache.directory.server" name="apacheds-mavibot-partition" rev="2.0.0-M15" conf="test->default">
+        <exclude org="org.apache.directory.api" name="api-ldap-schema-data"/>
+    </dependency>
+    <dependency org="org.apache.directory.api" name="api-all" rev="1.0.0-M20" conf="test->default">
+        <exclude org="xml-apis" name="xml-apis"/>
+        <exclude org="xpp3" name="xpp3"/>
+        <exclude org="dom4j" name="dom4j"/>
+    </dependency>
+    <dependency org="org.apache.directory.server" name="apacheds-jdbm-partition" rev="2.0.0-M15" conf="test->default">
+        <exclude org="org.apache.directory.api" name="api-ldap-schema-data"/>
+    </dependency>
+    <dependency org="org.apache.directory.server" name="apacheds-protocol-ldap" rev="2.0.0-M15" conf="test->default">
+        <exclude org="org.apache.directory.api" name="api-ldap-schema-data"/>
+    </dependency>
+
   </dependencies>
 
 </ivy-module>
diff --git a/ivysettings.xml b/ivysettings.xml
index 52cfa52..1d06c40 100644
--- a/ivysettings.xml
+++ b/ivysettings.xml
@@ -18,11 +18,9 @@
 -->
 
   <property name="repo.maven.org"
-    value="http://repo1.maven.org/maven2/" override="false"/>
+    value="https://repo1.maven.org/maven2/" override="false"/>
   <property name="repo.jboss.org"
-    value="http://repository.jboss.org/nexus/content/groups/public/" override="false"/>
-  <property name="repo.sun.org"
-    value="http://download.java.net/maven/2/" override="false"/>
+    value="https://repository.jboss.org/nexus/content/groups/public/" override="false"/>
   <property name="maven2.pattern"
     value="[organisation]/[module]/[revision]/[module]-[revision]"/>
   <property name="maven2.pattern.ext" value="${maven2.pattern}.[ext]"/>
@@ -33,13 +31,10 @@
       pattern="${maven2.pattern.ext}" m2compatible="true"/>
     <ibiblio name="jboss-maven2" root="${repo.jboss.org}"
       pattern="${maven2.pattern.ext}" m2compatible="true"/>
-    <ibiblio name="sun-maven2" root="${repo.sun.org}"
-      pattern="${maven2.pattern.ext}" m2compatible="true"/>
 
     <chain name="default" dual="true">
       <resolver ref="maven2"/>
       <resolver ref="jboss-maven2"/>
-      <resolver ref="sun-maven2"/>
     </chain>
 
   </resolvers>
diff --git a/lib/jdiff/zookeeper_3.4.9.xml b/lib/jdiff/zookeeper_3.4.10.xml
similarity index 98%
rename from lib/jdiff/zookeeper_3.4.9.xml
rename to lib/jdiff/zookeeper_3.4.10.xml
index 85f5d4e..23a7564 100644
--- a/lib/jdiff/zookeeper_3.4.9.xml
+++ b/lib/jdiff/zookeeper_3.4.10.xml
@@ -1,15 +1,15 @@
 <?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
 <!-- Generated by the JDiff Javadoc doclet -->
 <!-- (http://www.jdiff.org) -->
-<!-- on Tue Aug 23 13:09:29 IST 2016 -->
+<!-- on Thu Mar 23 16:54:38 IST 2017 -->
 
 <api
   xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
   xsi:noNamespaceSchemaLocation='api.xsd'
-  name="zookeeper 3.4.9"
+  name="zookeeper 3.4.10"
   jdversion="1.0.9">
 
-<!--  Command line arguments =  -doclet jdiff.JDiff -docletpath /home/sunil/Rakesh/ZKRelease/branch-3.4/build/jdiff/lib/jdiff-1.0.9.jar:/home/sunil/Rakesh/ZKRelease/branch-3.4/build/jdiff/lib/xerces-1.4.4.jar -classpath /home/sunil/Rakesh/ZKRelease/branch-3.4/build/classes:/home/sunil/Rakesh/ZKRelease/branch-3.4/src/java/lib/ivy-2.4.0.jar:/home/sunil/Rakesh/ZKRelease/branch-3.4/build/lib/jline-0.9.94.jar:/home/sunil/Rakesh/ZKRelease/branch-3.4/build/lib/log4j-1.2.16.jar:/home/sunil/Rakes [...]
+<!--  Command line arguments =  -doclet jdiff.JDiff -docletpath /home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/jdiff/lib/jdiff-1.0.9.jar:/home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/jdiff/lib/xerces-1.4.4.jar -classpath /home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/classes:/home/sunil/Rakesh/rc-3.4.10/b-3.4-new/src/java/lib/ivy-2.4.0.jar:/home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/lib/jline-0.9.94.jar:/home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/lib/log4j-1.2.16.jar:/home/sunil/Rakesh/rc-3 [...]
 <package name="org.apache.zookeeper">
   <!-- start interface org.apache.zookeeper.AsyncCallback -->
   <interface name="AsyncCallback"    abstract="true"
@@ -2288,6 +2288,33 @@
     </doc>
   </class>
   <!-- end class org.apache.zookeeper.Quotas -->
+  <!-- start class org.apache.zookeeper.SaslClientCallbackHandler -->
+  <class name="SaslClientCallbackHandler" extends="java.lang.Object"
+    abstract="false"
+    static="false" final="false" visibility="public"
+    deprecated="not deprecated">
+    <implements name="javax.security.auth.callback.CallbackHandler"/>
+    <constructor name="SaslClientCallbackHandler" type="java.lang.String, java.lang.String"
+      static="false" final="false" visibility="public"
+      deprecated="not deprecated">
+    </constructor>
+    <method name="handle"
+      abstract="false" native="false" synchronized="false"
+      static="false" final="false" visibility="public"
+      deprecated="not deprecated">
+      <param name="callbacks" type="javax.security.auth.callback.Callback[]"/>
+      <exception name="UnsupportedCallbackException" type="javax.security.auth.callback.UnsupportedCallbackException"/>
+    </method>
+    <doc>
+    <![CDATA[This is used by the SASL mechanisms to get further information to complete
+ the authentication. For example, a SASL mechanism might use this callback
+ handler to do verification operation. The CallbackHandler interface here
+ refers to javax.security.auth.callback.CallbackHandler. It should not be
+ confused with ZooKeeper packet callbacks like
+ org.apache.zookeeper.server.auth.SaslServerCallbackHandler.]]>
+    </doc>
+  </class>
+  <!-- end class org.apache.zookeeper.SaslClientCallbackHandler -->
   <!-- start class org.apache.zookeeper.ServerAdminClient -->
   <class name="ServerAdminClient" extends="java.lang.Object"
     abstract="false"
@@ -2854,6 +2881,11 @@
       static="true" final="false" visibility="public"
       deprecated="not deprecated">
     </method>
+    <method name="getRevisionHash" return="java.lang.String"
+      abstract="false" native="false" synchronized="false"
+      static="true" final="false" visibility="public"
+      deprecated="not deprecated">
+    </method>
     <method name="getBuildDate" return="java.lang.String"
       abstract="false" native="false" synchronized="false"
       static="true" final="false" visibility="public"
diff --git a/src/NOTICE.txt b/src/NOTICE.txt
index 7e4c7de..80d0188 100644
--- a/src/NOTICE.txt
+++ b/src/NOTICE.txt
@@ -1,6 +1,100 @@
 Apache ZooKeeper
-Copyright 2009-2016 The Apache Software Foundation
+Copyright 2009-2017 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
 
+This product includes software developed by
+The Netty Project (http://netty.io/)
+Copyright 2011 The Netty Project
+
+The Netty NOTICE file contains the following items:
+This product contains the extensions to Java Collections Framework which has
+been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene:
+
+  * LICENSE:
+    * license/LICENSE.jsr166y.txt (Public Domain)
+  * HOMEPAGE:
+    * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/
+    * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/
+
+This product contains a modified version of Robert Harder's Public Domain
+Base64 Encoder and Decoder, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.base64.txt (Public Domain)
+  * HOMEPAGE:
+    * http://iharder.sourceforge.net/current/java/base64/
+
+This product contains a modified version of 'JZlib', a re-implementation of
+zlib in pure Java, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.jzlib.txt (BSD Style License)
+  * HOMEPAGE:
+    * http://www.jcraft.com/jzlib/
+
+This product contains a modified version of 'Webbit', a Java event based
+WebSocket and HTTP server:
+
+  * LICENSE:
+    * license/LICENSE.webbit.txt (BSD License)
+  * HOMEPAGE:
+    * https://github.com/joewalnes/webbit
+
+This product optionally depends on 'Protocol Buffers', Google's data
+interchange format, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.protobuf.txt (New BSD License)
+  * HOMEPAGE:
+    * http://code.google.com/p/protobuf/
+
+This product optionally depends on 'Bouncy Castle Crypto APIs' to generate
+a temporary self-signed X.509 certificate when the JVM does not provide the
+equivalent functionality.  It can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.bouncycastle.txt (MIT License)
+  * HOMEPAGE:
+    * http://www.bouncycastle.org/
+
+This product optionally depends on 'SLF4J', a simple logging facade for Java,
+which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.slf4j.txt (MIT License)
+  * HOMEPAGE:
+    * http://www.slf4j.org/
+
+This product optionally depends on 'Apache Commons Logging', a logging
+framework, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.commons-logging.txt (Apache License 2.0)
+  * HOMEPAGE:
+    * http://commons.apache.org/logging/
+
+This product optionally depends on 'Apache Log4J', a logging framework,
+which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.log4j.txt (Apache License 2.0)
+  * HOMEPAGE:
+    * http://logging.apache.org/log4j/
+
+This product optionally depends on 'JBoss Logging', a logging framework,
+which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.jboss-logging.txt (GNU LGPL 2.1)
+  * HOMEPAGE:
+    * http://anonsvn.jboss.org/repos/common/common-logging-spi/
+
+This product optionally depends on 'Apache Felix', an open source OSGi
+framework implementation, which can be obtained at:
+
+  * LICENSE:
+    * license/LICENSE.felix.txt (Apache License 2.0)
+  * HOMEPAGE:
+    * http://felix.apache.org/
diff --git a/src/c/NOTICE.txt b/src/c/NOTICE.txt
index 07ef1ad..440b74d 100644
--- a/src/c/NOTICE.txt
+++ b/src/c/NOTICE.txt
@@ -1,5 +1,5 @@
 Apache ZooKeeper
-Copyright 2009-2011 The Apache Software Foundation
+Copyright 2009-2017 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/src/c/configure.ac b/src/c/configure.ac
index be00af9..21d752c 100644
--- a/src/c/configure.ac
+++ b/src/c/configure.ac
@@ -3,7 +3,7 @@
 
 AC_PREREQ(2.59)
 
-AC_INIT([zookeeper C client],3.4.9,[user at zookeeper.apache.org],[zookeeper])
+AC_INIT([zookeeper C client],3.4.10,[user at zookeeper.apache.org],[zookeeper])
 AC_CONFIG_SRCDIR([src/zookeeper.c])
 
 # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX
diff --git a/src/c/include/winconfig.h b/src/c/include/winconfig.h
index ce42b26..9d936ec 100644
--- a/src/c/include/winconfig.h
+++ b/src/c/include/winconfig.h
@@ -117,7 +117,7 @@
 #define PACKAGE_NAME "zookeeper C client"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "zookeeper C client 3.4.9 win32"
+#define PACKAGE_STRING "zookeeper C client 3.4.10 win32"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "c-client-src"
@@ -126,7 +126,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "3.4.9"
+#define PACKAGE_VERSION "3.4.10"
 
 /* poll() second argument type */
 #define POLL_NFDS_TYPE
@@ -138,7 +138,7 @@
 #define TIME_WITH_SYS_TIME
 
 /* Version number of package */
-#define VERSION "3.4.9"
+#define VERSION "3.4.10"
 
 /* Define to empty if `const' does not conform to ANSI C. */
 /* #undef const */
diff --git a/src/c/include/zookeeper.h b/src/c/include/zookeeper.h
index 7d1066a..005f1f0 100644
--- a/src/c/include/zookeeper.h
+++ b/src/c/include/zookeeper.h
@@ -468,8 +468,8 @@ ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn,
  * ZBADARGUMENTS - invalid input parameters
  * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
  * ZOPERATIONTIMEOUT - failed to flush the buffers within the specified timeout.
- * ZCONNECTIONLOSS - a network error occured while attempting to send request to server
- * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details
+ * ZCONNECTIONLOSS - a network error occurred while attempting to send request to server
+ * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details
  */
 ZOOAPI int zookeeper_close(zhandle_t *zh);
 
@@ -523,12 +523,12 @@ ZOOAPI struct sockaddr* zookeeper_get_connected_host(zhandle_t *zh,
  * ZOK - success
  * ZBADARGUMENTS - invalid input parameters
  * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
- * ZCONNECTIONLOSS - a network error occured while attempting to establish 
+ * ZCONNECTIONLOSS - a network error occurred while attempting to establish 
  * a connection to the server
  * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
  * ZOPERATIONTIMEOUT - hasn't received anything from the server for 2/3 of the
  * timeout value specified in zookeeper_init()
- * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details
+ * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details
  */
 #ifdef WIN32
 ZOOAPI int zookeeper_interest(zhandle_t *zh, SOCKET *fd, int *interest, 
@@ -547,11 +547,11 @@ ZOOAPI int zookeeper_interest(zhandle_t *zh, int *fd, int *interest,
  * ZOK - success
  * ZBADARGUMENTS - invalid input parameters
  * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
- * ZCONNECTIONLOSS - a network error occured while attempting to send request to server
+ * ZCONNECTIONLOSS - a network error occurred while attempting to send request to server
  * ZSESSIONEXPIRED - connection attempt failed -- the session's expired
  * ZAUTHFAILED - authentication request failed, e.i. invalid credentials
  * ZRUNTIMEINCONSISTENCY - a server response came out of order
- * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details
+ * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details
  * ZNOTHING -- not an error; simply indicates that there no more data from the server 
  *              to be processed (when called with ZOOKEEPER_READ flag).
  */
@@ -1166,7 +1166,7 @@ ZOOAPI const char* zerror(int c);
  * ZBADARGUMENTS - invalid input parameters
  * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE
  * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory
- * ZSYSTEMERROR - a system error occured
+ * ZSYSTEMERROR - a system error occurred
  */
 ZOOAPI int zoo_add_auth(zhandle_t *zh,const char* scheme,const char* cert, 
 	int certLen, void_completion_t completion, const void *data);
diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h
index 57790c4..186dc74 100644
--- a/src/c/include/zookeeper_version.h
+++ b/src/c/include/zookeeper_version.h
@@ -24,7 +24,7 @@ extern "C" {
 
 #define ZOO_MAJOR_VERSION 3
 #define ZOO_MINOR_VERSION 4
-#define ZOO_PATCH_VERSION 9
+#define ZOO_PATCH_VERSION 10
 
 #ifdef __cplusplus
 }
diff --git a/src/c/src/load_gen.c b/src/c/src/load_gen.c
index 72b5950..0e172e8 100644
--- a/src/c/src/load_gen.c
+++ b/src/c/src/load_gen.c
@@ -256,7 +256,7 @@ int main(int argc, char **argv) {
         deletedCounter=0;
         rc=recursiveDelete(argv[2]);
         if(rc==ZOK){
-            LOG_INFO(("Succesfully deleted a subtree starting at %s (%d nodes)",
+            LOG_INFO(("Successfully deleted a subtree starting at %s (%d nodes)",
                     argv[2],deletedCounter));
             exit(0);
         }
diff --git a/src/c/src/recordio.c b/src/c/src/recordio.c
index 41797fb..b17cb79 100644
--- a/src/c/src/recordio.c
+++ b/src/c/src/recordio.c
@@ -296,9 +296,11 @@ static struct oarchive oa_default = { STRUCT_INITIALIZER (start_record , oa_star
 
 struct iarchive *create_buffer_iarchive(char *buffer, int len)
 {
-    struct iarchive *ia = malloc(sizeof(*ia));
-    struct buff_struct *buff = malloc(sizeof(struct buff_struct));
+    struct iarchive *ia;
+    struct buff_struct *buff;
+    ia = malloc(sizeof(*ia));
     if (!ia) return 0;
+    buff = malloc(sizeof(struct buff_struct));
     if (!buff) {
         free(ia);
         return 0;
@@ -313,9 +315,11 @@ struct iarchive *create_buffer_iarchive(char *buffer, int len)
 
 struct oarchive *create_buffer_oarchive()
 {
-    struct oarchive *oa = malloc(sizeof(*oa));
-    struct buff_struct *buff = malloc(sizeof(struct buff_struct));
+    struct oarchive *oa;
+    struct buff_struct *buff;
+    oa = malloc(sizeof(*oa));
     if (!oa) return 0;
+    buff = malloc(sizeof(struct buff_struct));
     if (!buff) {
         free(oa);
         return 0;
diff --git a/src/c/tests/TestDriver.cc b/src/c/tests/TestDriver.cc
index 3f32ba4..d60db69 100644
--- a/src/c/tests/TestDriver.cc
+++ b/src/c/tests/TestDriver.cc
@@ -144,7 +144,7 @@ int main( int argc, char* argv[] ) {
    runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() );
  
    try {
-     CPPUNIT_NS::stdCOut() << "Running " << endl;
+     CPPUNIT_NS::stdCOut() << endl << "Running " << endl;
 
      zoo_set_debug_level(ZOO_LOG_LEVEL_INFO);
      //zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);
diff --git a/src/c/zookeeper.sln b/src/c/zookeeper.sln
index 42f41c9..bdf869f 100644
--- a/src/c/zookeeper.sln
+++ b/src/c/zookeeper.sln
@@ -1,25 +1,25 @@
-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual Studio 2008
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zookeeper", "zookeeper.vcproj", "{5754FB2B-5EA5-4988-851D-908CA533A626}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cli", "Cli.vcproj", "{050228F9-070F-4806-A2B5-E6B95D8EC4AF}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Win32 = Debug|Win32
-		Release|Win32 = Release|Win32
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.ActiveCfg = Debug|Win32
-		{5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.Build.0 = Debug|Win32
-		{5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.ActiveCfg = Release|Win32
-		{5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.Build.0 = Release|Win32
-		{050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.ActiveCfg = Debug|Win32
-		{050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.Build.0 = Debug|Win32
-		{050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.ActiveCfg = Release|Win32
-		{050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.Build.0 = Release|Win32
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-EndGlobal
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zookeeper", "zookeeper.vcproj", "{5754FB2B-5EA5-4988-851D-908CA533A626}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cli", "Cli.vcproj", "{050228F9-070F-4806-A2B5-E6B95D8EC4AF}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.ActiveCfg = Debug|Win32
+		{5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.Build.0 = Debug|Win32
+		{5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.ActiveCfg = Release|Win32
+		{5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.Build.0 = Release|Win32
+		{050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.ActiveCfg = Debug|Win32
+		{050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.Build.0 = Debug|Win32
+		{050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.ActiveCfg = Release|Win32
+		{050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/src/contrib/build-contrib.xml b/src/contrib/build-contrib.xml
index fab8a5f..276516e 100644
--- a/src/contrib/build-contrib.xml
+++ b/src/contrib/build-contrib.xml
@@ -43,7 +43,7 @@
 
   <property name="ivy.version" value="2.4.0"/>
   <property name="ivy.url"
-            value="http://repo2.maven.org/maven2/org/apache/ivy/ivy" />
+            value="https://repo1.maven.org/maven2/org/apache/ivy/ivy" />
   <property name="ivy.home" value="${user.home}/.ant" />
   <property name="ivy.lib" value="${build.dir}/lib"/>
   <property name="ivy.test.lib" value="${build.test}/lib"/>
diff --git a/src/contrib/zkfuse/src/event.h b/src/contrib/zkfuse/src/event.h
index 0506932..936ecc6 100644
--- a/src/contrib/zkfuse/src/event.h
+++ b/src/contrib/zkfuse/src/event.h
@@ -213,7 +213,7 @@ class GenericEvent {
         /**
          * The event represented as abstract wrapper.
          */
-        shared_ptr<AbstractEventWrapper> m_eventWrapper;
+        boost::shared_ptr<AbstractEventWrapper> m_eventWrapper;
         
 };
     
diff --git a/src/contrib/zkfuse/src/zkadapter.cc b/src/contrib/zkfuse/src/zkadapter.cc
index 7dfb907..7f02fa3 100644
--- a/src/contrib/zkfuse/src/zkadapter.cc
+++ b/src/contrib/zkfuse/src/zkadapter.cc
@@ -673,7 +673,7 @@ ZooKeeperAdapter::deleteNode(const string &path,
             LOG_WARN( LOG, "Error %d for %s", rc, path.c_str() );
             //get all children and delete them recursively...
             vector<string> nodeList;
-            getNodeChildren( nodeList, path, false );
+            getNodeChildren( nodeList, path, NULL );
             for (vector<string>::const_iterator i = nodeList.begin();
                  i != nodeList.end();
                  ++i) {
diff --git a/src/contrib/zkpython/src/c/pyzk_docstrings.h b/src/contrib/zkpython/src/c/pyzk_docstrings.h
index d2c4d60..1f38d53 100644
--- a/src/contrib/zkpython/src/c/pyzk_docstrings.h
+++ b/src/contrib/zkpython/src/c/pyzk_docstrings.h
@@ -276,7 +276,7 @@ const char pyzk_add_auth_doc[] =
 "BADARGUMENTS - invalid input parameters\n"
 "INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n"
 "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"
-  "SYSTEMERROR - a system error occured\n";
+  "SYSTEMERROR - a system error occurred\n";
 
 const char pyzk_is_unrecoverable_doc[] = 
 " checks if the current zookeeper connection state can't be recovered.\n"
@@ -513,8 +513,8 @@ static const char pyzk_close_doc[] =
 "BADARGUMENTS - invalid input parameters\n"
 "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"
 "OPERATIONTIMEOUT - failed to flush the buffers within the specified timeout.\n"
-"CONNECTIONLOSS - a network error occured while attempting to send request to server\n"
-  "SYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details\n";
+"CONNECTIONLOSS - a network error occurred while attempting to send request to server\n"
+  "SYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details\n";
 
 static const char pyzk_set2_doc[] = 
 "\n"
diff --git a/src/contrib/zkpython/src/c/zookeeper.c b/src/contrib/zkpython/src/c/zookeeper.c
index 0bf6c59..4474661 100644
--- a/src/contrib/zkpython/src/c/zookeeper.c
+++ b/src/contrib/zkpython/src/c/zookeeper.c
@@ -721,7 +721,7 @@ PyObject *pyzoo_adelete(PyObject *self, PyObject *args)
   return Py_BuildValue("i", err);
 }
 
-/* Asynchronous node existance check, returns integer error code */
+/* Asynchronous node existence check, returns integer error code */
 PyObject *pyzoo_aexists(PyObject *self, PyObject *args)
 {
   int zkhid; char *path; 
@@ -1059,7 +1059,7 @@ static PyObject *pyzoo_delete(PyObject *self, PyObject *args)
   return Py_BuildValue("i", err);
 }
 
-/* Synchronous node existance check, returns stat if exists, None if
+/* Synchronous node existence check, returns stat if exists, None if
    absent */
 static PyObject *pyzoo_exists(PyObject *self, PyObject *args)
 {
diff --git a/src/contrib/zktreeutil/src/ZkAdaptor.cc b/src/contrib/zktreeutil/src/ZkAdaptor.cc
index baec8f9..1df175a 100644
--- a/src/contrib/zktreeutil/src/ZkAdaptor.cc
+++ b/src/contrib/zktreeutil/src/ZkAdaptor.cc
@@ -445,7 +445,7 @@ namespace zktreeutil
             if (rc == ZNONODE)
                 return false;
             // Some error
-            std::cerr << "[zktreeutil] Error in checking existance of " << path << std::endl;
+            std::cerr << "[zktreeutil] Error in checking existence of " << path << std::endl;
             throw ZooKeeperException( string("Unable to check existence of node ") + path, rc );
         } else {
             return true;        
diff --git a/src/contrib/zktreeutil/src/ZkAdaptor.h b/src/contrib/zktreeutil/src/ZkAdaptor.h
index d94b033..4b68e28 100644
--- a/src/contrib/zktreeutil/src/ZkAdaptor.h
+++ b/src/contrib/zktreeutil/src/ZkAdaptor.h
@@ -255,7 +255,7 @@ namespace zktreeutil
             vector<string> getNodeChildren( const string &path) throw(ZooKeeperException);
 
             /**
-             * \brief Check the existance of path to a znode.
+             * \brief Check the existence of path to a znode.
              * 
              * @param path the absolute path name of the znode
              * @return TRUE if the znode exists; FALSE otherwise
diff --git a/src/contrib/zktreeutil/src/ZkTreeUtil.cc b/src/contrib/zktreeutil/src/ZkTreeUtil.cc
index 83f0cbf..270bf31 100644
--- a/src/contrib/zktreeutil/src/ZkTreeUtil.cc
+++ b/src/contrib/zktreeutil/src/ZkTreeUtil.cc
@@ -347,7 +347,7 @@ namespace zktreeutil
         std::cerr << "[zktreeutil] connected to ZK serverfor reading"
             << std::endl;
 
-        // Check the existance of the path to znode
+        // Check the existence of the path to znode
         if (!zkHandle->nodeExists (path))
         {
             string errMsg = string("[zktreeutil] path does not exists : ") + path;
diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java
index 63d82c1..d8194d5 100644
--- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java
+++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java
@@ -625,7 +625,7 @@ public class ZooInspectorManagerImpl implements ZooInspectorManager {
                                 zooKeeper));
                     } catch (Exception e) {
                         LoggerFactory.getLogger().error(
-                                "Error occured adding node watcher for node: "
+                                "Error occurred adding node watcher for node: "
                                         + node, e);
                     }
                 }
@@ -700,7 +700,7 @@ public class ZooInspectorManagerImpl implements ZooInspectorManager {
                     }
                 } catch (Exception e) {
                     LoggerFactory.getLogger().error(
-                            "Error occured re-adding node watcherfor node "
+                            "Error occurred re-adding node watcherfor node "
                                     + nodePath, e);
                 }
                 nodeListener.processEvent(event.getPath(), event.getType()
diff --git a/src/contrib/zooinspector/zooInspector.cmd b/src/contrib/zooinspector/zooInspector.cmd
index 4fa3ab2..0b298a2 100644
--- a/src/contrib/zooinspector/zooInspector.cmd
+++ b/src/contrib/zooinspector/zooInspector.cmd
@@ -1,18 +1,18 @@
-#!/bin/sh
-
-# 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.
-
+#!/bin/sh
+
+# 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.
+
 java -cp zookeeper-dev-ZooInspector.jar;lib/log4j-1.2.15.jar;lib/zookeeper-3.3.0.jar;lib/jToaster-1.0.4.jar;lib org.apache.zookeeper.inspector.ZooInspector
\ No newline at end of file
diff --git a/src/docs/src/documentation/conf/cli.xconf b/src/docs/src/documentation/conf/cli.xconf
index 99eedf5..c671340 100644
--- a/src/docs/src/documentation/conf/cli.xconf
+++ b/src/docs/src/documentation/conf/cli.xconf
@@ -98,7 +98,7 @@
        |     <broken-links type="none"/>
        |
        |   Two attributes to this node specify whether a page should
-       |   be generated when an error has occured. 'generate' specifies
+       |   be generated when an error has occurred. 'generate' specifies
        |   whether a page should be generated (default: true) and
        |   extension specifies an extension that should be appended
        |   to the generated page's filename (default: none)
diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
index 5aefa9a..6d45121 100644
--- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
+++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
@@ -1042,6 +1042,40 @@ server.3=zoo3:2888:3888</programlisting>
               </note>
             </listitem>
           </varlistentry>
+
+          <varlistentry>
+            <term>4lw.commands.whitelist</term>
+
+            <listitem>
+              <para>(Java system property: <emphasis
+                      role="bold">zookeeper.4lw.commands.whitelist</emphasis>)</para>
+
+              <para><emphasis role="bold">New in 3.4.10:</emphasis>
+                This property contains a list of comma separated
+                <ulink url="#sc_zkCommands">Four Letter Words</ulink> commands. It is introduced
+                to provide fine grained control over the set of commands ZooKeeper can execute,
+                so users can turn off certain commands if necessary.
+                By default it contains all supported four letter word commands except "wchp" and "wchc",
+                if the property is not specified. If the property is specified, then only commands listed
+                in the whitelist are enabled.
+              </para>
+
+              <para>Here's an example of the configuration that enables stat, ruok, conf, and isro
+                command while disabling the rest of Four Letter Words command:</para>
+              <programlisting>
+                4lw.commands.whitelist=stat, ruok, conf, isro
+              </programlisting>
+
+              <para>Users can also use asterisk option so they don't have to include every command one by one in the list.
+                As an example, this will enable all four letter word commands:
+              </para>
+              <programlisting>
+                4lw.commands.whitelist=*
+              </programlisting>
+
+            </listitem>
+          </varlistentry>
+
         </variablelist>
         <para></para>
       </section>
@@ -1667,6 +1701,16 @@ imok
             usage limit that would cause the system to swap.</para>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term>Publicly accessible deployment</term>
+          <listitem>
+            <para>
+              A ZooKeeper ensemble is expected to operate in a trusted computing environment.
+              It is thus recommended to deploy ZooKeeper behind a firewall.
+            </para>
+          </listitem>
+        </varlistentry>
       </variablelist>
     </section>
 
diff --git a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
index f6d50b6..f0c05b3 100644
--- a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
+++ b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
@@ -1506,7 +1506,7 @@ authProvider.2=com.f.MyAuth2
         <para>If you are using watches, you must look for the connected watch
         event. When a ZooKeeper client disconnects from a server, you will
         not receive notification of changes until reconnected. If you are
-        watching for a znode to come into existance, you will miss the event
+        watching for a znode to come into existence, you will miss the event
         if the znode is created and deleted while you are disconnected.</para>
       </listitem>
 
diff --git a/src/docs/src/documentation/skinconf.xml b/src/docs/src/documentation/skinconf.xml
index 685a86a..43f3a49 100644
--- a/src/docs/src/documentation/skinconf.xml
+++ b/src/docs/src/documentation/skinconf.xml
@@ -84,7 +84,7 @@ which will be used to configure the chosen Forrest skin.
   <favicon-url>images/favicon.ico</favicon-url>
 
   <!-- The following are used to construct a copyright statement -->
-  <year>2008-2013</year>
+  <year></year>
   <vendor>The Apache Software Foundation.</vendor>
   <copyright-link>http://www.apache.org/licenses/</copyright-link>
 
diff --git a/src/java/lib/jdiff/zookeeper_3.4.9.xml b/src/java/lib/jdiff/zookeeper_3.4.10.xml
similarity index 98%
rename from src/java/lib/jdiff/zookeeper_3.4.9.xml
rename to src/java/lib/jdiff/zookeeper_3.4.10.xml
index 85f5d4e..23a7564 100644
--- a/src/java/lib/jdiff/zookeeper_3.4.9.xml
+++ b/src/java/lib/jdiff/zookeeper_3.4.10.xml
@@ -1,15 +1,15 @@
 <?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
 <!-- Generated by the JDiff Javadoc doclet -->
 <!-- (http://www.jdiff.org) -->
-<!-- on Tue Aug 23 13:09:29 IST 2016 -->
+<!-- on Thu Mar 23 16:54:38 IST 2017 -->
 
 <api
   xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
   xsi:noNamespaceSchemaLocation='api.xsd'
-  name="zookeeper 3.4.9"
+  name="zookeeper 3.4.10"
   jdversion="1.0.9">
 
-<!--  Command line arguments =  -doclet jdiff.JDiff -docletpath /home/sunil/Rakesh/ZKRelease/branch-3.4/build/jdiff/lib/jdiff-1.0.9.jar:/home/sunil/Rakesh/ZKRelease/branch-3.4/build/jdiff/lib/xerces-1.4.4.jar -classpath /home/sunil/Rakesh/ZKRelease/branch-3.4/build/classes:/home/sunil/Rakesh/ZKRelease/branch-3.4/src/java/lib/ivy-2.4.0.jar:/home/sunil/Rakesh/ZKRelease/branch-3.4/build/lib/jline-0.9.94.jar:/home/sunil/Rakesh/ZKRelease/branch-3.4/build/lib/log4j-1.2.16.jar:/home/sunil/Rakes [...]
+<!--  Command line arguments =  -doclet jdiff.JDiff -docletpath /home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/jdiff/lib/jdiff-1.0.9.jar:/home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/jdiff/lib/xerces-1.4.4.jar -classpath /home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/classes:/home/sunil/Rakesh/rc-3.4.10/b-3.4-new/src/java/lib/ivy-2.4.0.jar:/home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/lib/jline-0.9.94.jar:/home/sunil/Rakesh/rc-3.4.10/b-3.4-new/build/lib/log4j-1.2.16.jar:/home/sunil/Rakesh/rc-3 [...]
 <package name="org.apache.zookeeper">
   <!-- start interface org.apache.zookeeper.AsyncCallback -->
   <interface name="AsyncCallback"    abstract="true"
@@ -2288,6 +2288,33 @@
     </doc>
   </class>
   <!-- end class org.apache.zookeeper.Quotas -->
+  <!-- start class org.apache.zookeeper.SaslClientCallbackHandler -->
+  <class name="SaslClientCallbackHandler" extends="java.lang.Object"
+    abstract="false"
+    static="false" final="false" visibility="public"
+    deprecated="not deprecated">
+    <implements name="javax.security.auth.callback.CallbackHandler"/>
+    <constructor name="SaslClientCallbackHandler" type="java.lang.String, java.lang.String"
+      static="false" final="false" visibility="public"
+      deprecated="not deprecated">
+    </constructor>
+    <method name="handle"
+      abstract="false" native="false" synchronized="false"
+      static="false" final="false" visibility="public"
+      deprecated="not deprecated">
+      <param name="callbacks" type="javax.security.auth.callback.Callback[]"/>
+      <exception name="UnsupportedCallbackException" type="javax.security.auth.callback.UnsupportedCallbackException"/>
+    </method>
+    <doc>
+    <![CDATA[This is used by the SASL mechanisms to get further information to complete
+ the authentication. For example, a SASL mechanism might use this callback
+ handler to do verification operation. The CallbackHandler interface here
+ refers to javax.security.auth.callback.CallbackHandler. It should not be
+ confused with ZooKeeper packet callbacks like
+ org.apache.zookeeper.server.auth.SaslServerCallbackHandler.]]>
+    </doc>
+  </class>
+  <!-- end class org.apache.zookeeper.SaslClientCallbackHandler -->
   <!-- start class org.apache.zookeeper.ServerAdminClient -->
   <class name="ServerAdminClient" extends="java.lang.Object"
     abstract="false"
@@ -2854,6 +2881,11 @@
       static="true" final="false" visibility="public"
       deprecated="not deprecated">
     </method>
+    <method name="getRevisionHash" return="java.lang.String"
+      abstract="false" native="false" synchronized="false"
+      static="true" final="false" visibility="public"
+      deprecated="not deprecated">
+    </method>
     <method name="getBuildDate" return="java.lang.String"
       abstract="false" native="false" synchronized="false"
       static="true" final="false" visibility="public"
diff --git a/src/java/main/org/apache/zookeeper/Login.java b/src/java/main/org/apache/zookeeper/Login.java
index aaa220c..3e21aae 100644
--- a/src/java/main/org/apache/zookeeper/Login.java
+++ b/src/java/main/org/apache/zookeeper/Login.java
@@ -32,8 +32,9 @@ import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 import javax.security.auth.callback.CallbackHandler;
 
-import org.apache.log4j.Logger;
 import org.apache.zookeeper.client.ZooKeeperSaslClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import javax.security.auth.kerberos.KerberosTicket;
 import javax.security.auth.Subject;
 import java.util.Date;
@@ -41,7 +42,7 @@ import java.util.Random;
 import java.util.Set;
 
 public class Login {
-    Logger LOG = Logger.getLogger(Login.class);
+    private static final Logger LOG = LoggerFactory.getLogger(Login.class);
     public CallbackHandler callbackHandler;
 
     // LoginThread will sleep until 80% of time from last refresh to
@@ -291,7 +292,7 @@ public class Login {
         }
         LoginContext loginContext = new LoginContext(loginContextName,callbackHandler);
         loginContext.login();
-        LOG.info("successfully logged in.");
+        LOG.info("{} successfully logged in.", loginContextName);
         return loginContext;
     }
 
diff --git a/src/java/main/org/apache/zookeeper/SaslClientCallbackHandler.java b/src/java/main/org/apache/zookeeper/SaslClientCallbackHandler.java
new file mode 100644
index 0000000..d6f5549
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/SaslClientCallbackHandler.java
@@ -0,0 +1,104 @@
+/**
+ * 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 javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.RealmCallback;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is used by the SASL mechanisms to get further information to complete
+ * the authentication. For example, a SASL mechanism might use this callback
+ * handler to do verification operation. The CallbackHandler interface here
+ * refers to javax.security.auth.callback.CallbackHandler. It should not be
+ * confused with ZooKeeper packet callbacks like
+ * org.apache.zookeeper.server.auth.SaslServerCallbackHandler.
+ */
+public class SaslClientCallbackHandler implements CallbackHandler {
+    private String password = null;
+    private static final Logger LOG = LoggerFactory.getLogger(SaslClientCallbackHandler.class);
+    private final String entity;
+    public SaslClientCallbackHandler(String password, String client) {
+        this.password = password;
+        this.entity = client;
+    }
+
+    public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
+        for (Callback callback : callbacks) {
+            if (callback instanceof NameCallback) {
+                NameCallback nc = (NameCallback) callback;
+                nc.setName(nc.getDefaultName());
+            }
+            else {
+                if (callback instanceof PasswordCallback) {
+                    PasswordCallback pc = (PasswordCallback)callback;
+                    if (password != null) {
+                        pc.setPassword(this.password.toCharArray());
+                    } else {
+                        LOG.warn("Could not login: the {} is being asked for a password, but the ZooKeeper {}" +
+                          " code does not currently support obtaining a password from the user." +
+                          " Make sure that the {} is configured to use a ticket cache (using" +
+                          " the JAAS configuration setting 'useTicketCache=true)' and restart the {}. If" +
+                          " you still get this message after that, the TGT in the ticket cache has expired and must" +
+                          " be manually refreshed. To do so, first determine if you are using a password or a" +
+                          " keytab. If the former, run kinit in a Unix shell in the environment of the user who" +
+                          " is running this Zookeeper {} using the command" +
+                          " 'kinit <princ>' (where <princ> is the name of the {}'s Kerberos principal)." +
+                          " If the latter, do" +
+                          " 'kinit -k -t <keytab> <princ>' (where <princ> is the name of the Kerberos principal, and" +
+                          " <keytab> is the location of the keytab file). After manually refreshing your cache," +
+                          " restart this {}. If you continue to see this message after manually refreshing" +
+                          " your cache, ensure that your KDC host's clock is in sync with this host's clock.",
+                          new Object[]{entity, entity, entity, entity, entity, entity, entity});
+                    }
+                }
+                else {
+                    if (callback instanceof RealmCallback) {
+                        RealmCallback rc = (RealmCallback) callback;
+                        rc.setText(rc.getDefaultText());
+                    }
+                    else {
+                        if (callback instanceof AuthorizeCallback) {
+                            AuthorizeCallback ac = (AuthorizeCallback) callback;
+                            String authid = ac.getAuthenticationID();
+                            String authzid = ac.getAuthorizationID();
+                            if (authid.equals(authzid)) {
+                                ac.setAuthorized(true);
+                            } else {
+                                ac.setAuthorized(false);
+                            }
+                            if (ac.isAuthorized()) {
+                                ac.setAuthorizedID(authzid);
+                            }
+                        }
+                        else {
+                            throw new UnsupportedCallbackException(callback, "Unrecognized SASL " + entity + "Callback");
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/java/main/org/apache/zookeeper/Version.java b/src/java/main/org/apache/zookeeper/Version.java
index 4657371..1f5cf1a 100644
--- a/src/java/main/org/apache/zookeeper/Version.java
+++ b/src/java/main/org/apache/zookeeper/Version.java
@@ -20,10 +20,21 @@ package org.apache.zookeeper;
 
 public class Version implements org.apache.zookeeper.version.Info {
 
+    /*
+     * Since the SVN to Git port this field doesn't return the revision anymore
+     * TODO: remove this method and associated field declaration in VerGen
+     * @see {@link #getHashRevision()}
+     * @return the default value -1
+     */
+    @Deprecated
     public static int getRevision() {
         return REVISION;
     }
 
+    public static String getRevisionHash() {
+        return REVISION_HASH;
+    }
+
     public static String getBuildDate() {
         return BUILD_DATE;
     }
@@ -34,7 +45,7 @@ public class Version implements org.apache.zookeeper.version.Info {
     }
 
     public static String getVersionRevision() {
-        return getVersion() + "-" + getRevision();
+        return getVersion() + "-" + getRevisionHash();
     }
 
     public static String getFullVersion() {
diff --git a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java
index baf1c49..26721f5 100644
--- a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java
+++ b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java
@@ -649,7 +649,7 @@ public class ZooKeeperMain {
             System.exit(0);
         } else if (cmd.equals("redo") && args.length >= 2) {
             Integer i = Integer.decode(args[1]);
-            if (commandCount <= i){ // don't allow redoing this redo
+            if (commandCount <= i || i < 0){ // don't allow redoing this redo
                 System.out.println("Command index out of range");
                 return false;
             }
diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java
index 21ef0fa..f3c9d3c 100644
--- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java
+++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java
@@ -19,22 +19,13 @@
 package org.apache.zookeeper.client;
 
 import java.io.IOException;
-import java.security.Principal;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 
 import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.AppConfigurationEntry;
 import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginException;
-import javax.security.sasl.AuthorizeCallback;
-import javax.security.sasl.RealmCallback;
-import javax.security.sasl.Sasl;
 import javax.security.sasl.SaslClient;
 import javax.security.sasl.SaslException;
 
@@ -42,17 +33,13 @@ import org.apache.zookeeper.AsyncCallback;
 import org.apache.zookeeper.ClientCnxn;
 import org.apache.zookeeper.Environment;
 import org.apache.zookeeper.Login;
+import org.apache.zookeeper.SaslClientCallbackHandler;
 import org.apache.zookeeper.Watcher.Event.KeeperState;
 import org.apache.zookeeper.ZooDefs;
 import org.apache.zookeeper.data.Stat;
 import org.apache.zookeeper.proto.GetSASLRequest;
 import org.apache.zookeeper.proto.SetSASLResponse;
-import org.apache.zookeeper.server.auth.KerberosName;
-import org.ietf.jgss.GSSContext;
-import org.ietf.jgss.GSSCredential;
-import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.Oid;
+import org.apache.zookeeper.util.SecurityUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -226,83 +213,14 @@ public class ZooKeeperSaslClient {
                         }
                         // note that the login object is static: it's shared amongst all zookeeper-related connections.
                         // in order to ensure the login is initialized only once, it must be synchronized the code snippet.
-                        login = new Login(loginContext, new ClientCallbackHandler(null));
+                        login = new Login(loginContext, new SaslClientCallbackHandler(null, "Client"));
                         login.startThreadIfNeeded();
                         initializedLogin = true;
                     }
                 }
             }
-            Subject subject = login.getSubject();
-            SaslClient saslClient;
-            // Use subject.getPrincipals().isEmpty() as an indication of which SASL mechanism to use:
-            // if empty, use DIGEST-MD5; otherwise, use GSSAPI.
-            if (subject.getPrincipals().isEmpty()) {
-                // no principals: must not be GSSAPI: use DIGEST-MD5 mechanism instead.
-                LOG.info("Client will use DIGEST-MD5 as SASL mechanism.");
-                String[] mechs = {"DIGEST-MD5"};
-                String username = (String)(subject.getPublicCredentials().toArray()[0]);
-                String password = (String)(subject.getPrivateCredentials().toArray()[0]);
-                // "zk-sasl-md5" is a hard-wired 'domain' parameter shared with zookeeper server code (see ServerCnxnFactory.java)
-                saslClient = Sasl.createSaslClient(mechs, username, "zookeeper", "zk-sasl-md5", null, new ClientCallbackHandler(password));
-                return saslClient;
-            }
-            else { // GSSAPI.
-            	boolean usingNativeJgss =
-            			Boolean.getBoolean("sun.security.jgss.native");
-            	if (usingNativeJgss) {
-            		// http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
-            		// """
-            		// In addition, when performing operations as a particular
-            		// Subject, e.g. Subject.doAs(...) or Subject.doAsPrivileged(...),
-            		// the to-be-used GSSCredential should be added to Subject's
-            		// private credential set. Otherwise, the GSS operations will
-            		// fail since no credential is found.
-            		// """
-            		try {
-            			GSSManager manager = GSSManager.getInstance();
-            			Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
-            			GSSCredential cred = manager.createCredential(null,
-            					GSSContext.DEFAULT_LIFETIME,
-            					krb5Mechanism,
-            					GSSCredential.INITIATE_ONLY);
-            			subject.getPrivateCredentials().add(cred);
-            			if (LOG.isDebugEnabled()) {
-            				LOG.debug("Added private credential to subject: " + cred);
-            			}
-            		} catch (GSSException ex) {
-            			LOG.warn("Cannot add private credential to subject; " +
-            					"authentication at the server may fail", ex);
-            		}
-            	}
-                final Object[] principals = subject.getPrincipals().toArray();
-                // determine client principal from subject.
-                final Principal clientPrincipal = (Principal)principals[0];
-                final KerberosName clientKerberosName = new KerberosName(clientPrincipal.getName());
-                // assume that server and client are in the same realm (by default; unless the system property
-                // "zookeeper.server.realm" is set).
-                String serverRealm = System.getProperty("zookeeper.server.realm",clientKerberosName.getRealm());
-                KerberosName serviceKerberosName = new KerberosName(servicePrincipal+"@"+serverRealm);
-                final String serviceName = serviceKerberosName.getServiceName();
-                final String serviceHostname = serviceKerberosName.getHostName();
-                final String clientPrincipalName = clientKerberosName.toString();
-                try {
-                    saslClient = Subject.doAs(subject,new PrivilegedExceptionAction<SaslClient>() {
-                        public SaslClient run() throws SaslException {
-                            LOG.info("Client will use GSSAPI as SASL mechanism.");
-                            String[] mechs = {"GSSAPI"};
-                            LOG.debug("creating sasl client: client="+clientPrincipalName+";service="+serviceName+";serviceHostname="+serviceHostname);
-                            SaslClient saslClient = Sasl.createSaslClient(mechs,clientPrincipalName,serviceName,serviceHostname,null,new ClientCallbackHandler(null));
-                            return saslClient;
-                        }
-                    });
-                    return saslClient;
-                }
-                catch (Exception e) {
-                	LOG.error("Exception while trying to create SASL client", e);
-                    e.printStackTrace();
-                    return null;
-                }
-            }
+            return SecurityUtils.createSaslClient(login.getSubject(),
+                    servicePrincipal, "zookeeper", "zk-sasl-md5", LOG, "Client");
         } catch (LoginException e) {
             // We throw LoginExceptions...
             throw e;
@@ -471,75 +389,6 @@ public class ZooKeeperSaslClient {
         }
     }
 
-    // The CallbackHandler interface here refers to
-    // javax.security.auth.callback.CallbackHandler.
-    // It should not be confused with Zookeeper packet callbacks like
-    //  org.apache.zookeeper.server.auth.SaslServerCallbackHandler.
-    public static class ClientCallbackHandler implements CallbackHandler {
-        private String password = null;
-
-        public ClientCallbackHandler(String password) {
-            this.password = password;
-        }
-
-        public void handle(Callback[] callbacks) throws
-          UnsupportedCallbackException {
-            for (Callback callback : callbacks) {
-                if (callback instanceof NameCallback) {
-                    NameCallback nc = (NameCallback) callback;
-                    nc.setName(nc.getDefaultName());
-                }
-                else {
-                    if (callback instanceof PasswordCallback) {
-                        PasswordCallback pc = (PasswordCallback)callback;
-                        if (password != null) {
-                            pc.setPassword(this.password.toCharArray());
-                        } else {
-                            LOG.warn("Could not login: the client is being asked for a password, but the Zookeeper" +
-                              " client code does not currently support obtaining a password from the user." +
-                              " Make sure that the client is configured to use a ticket cache (using" +
-                              " the JAAS configuration setting 'useTicketCache=true)' and restart the client. If" +
-                              " you still get this message after that, the TGT in the ticket cache has expired and must" +
-                              " be manually refreshed. To do so, first determine if you are using a password or a" +
-                              " keytab. If the former, run kinit in a Unix shell in the environment of the user who" +
-                              " is running this Zookeeper client using the command" +
-                              " 'kinit <princ>' (where <princ> is the name of the client's Kerberos principal)." +
-                              " If the latter, do" +
-                              " 'kinit -k -t <keytab> <princ>' (where <princ> is the name of the Kerberos principal, and" +
-                              " <keytab> is the location of the keytab file). After manually refreshing your cache," +
-                              " restart this client. If you continue to see this message after manually refreshing" +
-                              " your cache, ensure that your KDC host's clock is in sync with this host's clock.");
-                        }
-                    }
-                    else {
-                        if (callback instanceof RealmCallback) {
-                            RealmCallback rc = (RealmCallback) callback;
-                            rc.setText(rc.getDefaultText());
-                        }
-                        else {
-                            if (callback instanceof AuthorizeCallback) {
-                                AuthorizeCallback ac = (AuthorizeCallback) callback;
-                                String authid = ac.getAuthenticationID();
-                                String authzid = ac.getAuthorizationID();
-                                if (authid.equals(authzid)) {
-                                    ac.setAuthorized(true);
-                                } else {
-                                    ac.setAuthorized(false);
-                                }
-                                if (ac.isAuthorized()) {
-                                    ac.setAuthorizedID(authzid);
-                                }
-                            }
-                            else {
-                                throw new UnsupportedCallbackException(callback,"Unrecognized SASL ClientCallback");
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     public boolean clientTunneledAuthenticationInProgress() {
     	if (!isSASLConfigured) {
     	    return false;
diff --git a/src/java/main/org/apache/zookeeper/server/DataNode.java b/src/java/main/org/apache/zookeeper/server/DataNode.java
index 4c79d55..8efdaf8 100644
--- a/src/java/main/org/apache/zookeeper/server/DataNode.java
+++ b/src/java/main/org/apache/zookeeper/server/DataNode.java
@@ -60,6 +60,8 @@ public class DataNode implements Record {
      */
     private Set<String> children = null;
 
+    private static final Set<String> EMPTY_SET = Collections.emptySet();
+
     /**
      * default constructor for the datanode
      */
@@ -130,7 +132,7 @@ public class DataNode implements Record {
      */
     public synchronized Set<String> getChildren() {
         if (children == null) {
-            return children;
+            return EMPTY_SET;
         }
 
         return Collections.unmodifiableSet(children);
diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java
index 25f2975..656e4e9 100644
--- a/src/java/main/org/apache/zookeeper/server/DataTree.java
+++ b/src/java/main/org/apache/zookeeper/server/DataTree.java
@@ -385,10 +385,8 @@ public class DataTree {
         }
         synchronized (parent) {
             Set<String> children = parent.getChildren();
-            if (children != null) {
-                if (children.contains(childName)) {
-                    throw new KeeperException.NodeExistsException();
-                }
+            if (children.contains(childName)) {
+                throw new KeeperException.NodeExistsException();
             }
             
             if (parentCVersion == -1) {
@@ -598,14 +596,7 @@ public class DataTree {
             if (stat != null) {
                 n.copyStat(stat);
             }
-            ArrayList<String> children;
-            Set<String> childs = n.getChildren();
-            if (childs != null) {
-                children = new ArrayList<String>(childs.size());
-                children.addAll(childs);
-            } else {
-                children = new ArrayList<String>(0);
-            }
+            List<String> children = new ArrayList<String>(n.getChildren());
 
             if (watcher != null) {
                 childWatches.addWatch(path, watcher);
@@ -937,17 +928,12 @@ public class DataTree {
         int len = 0;
         synchronized (node) {
             Set<String> childs = node.getChildren();
-            if (childs != null) {
-                children = childs.toArray(new String[childs.size()]);
-            }
+            children = childs.toArray(new String[childs.size()]);
             len = (node.data == null ? 0 : node.data.length);
         }
         // add itself
         counts.count += 1;
         counts.bytes += len;
-        if (children == null || children.length == 0) {
-            return;
-        }
         for (String child : children) {
             getCounts(path + "/" + child, counts);
         }
@@ -987,11 +973,9 @@ public class DataTree {
         String children[] = null;
         synchronized (node) {
             Set<String> childs = node.getChildren();
-            if (childs != null) {
-                children = childs.toArray(new String[childs.size()]);
-            }
+            children = childs.toArray(new String[childs.size()]);
         }
-        if (children == null || children.length == 0) {
+        if (children.length == 0) {
             // this node does not have a child
             // is the leaf node
             // check if its the leaf node
@@ -1051,23 +1035,19 @@ public class DataTree {
             //are never changed
             nodeCopy = new DataNode(node.parent, node.data, node.acl, statCopy);
             Set<String> childs = node.getChildren();
-            if (childs != null) {
-                children = childs.toArray(new String[childs.size()]);
-            }
+            children = childs.toArray(new String[childs.size()]);
         }
         oa.writeString(pathString, "path");
         oa.writeRecord(nodeCopy, "node");
         path.append('/');
         int off = path.length();
-        if (children != null) {
-            for (String child : children) {
-                // since this is single buffer being resused
-                // we need
-                // to truncate the previous bytes of string.
-                path.delete(off, Integer.MAX_VALUE);
-                path.append(child);
-                serializeNode(oa, path);
-            }
+        for (String child : children) {
+            // since this is single buffer being resused
+            // we need
+            // to truncate the previous bytes of string.
+            path.delete(off, Integer.MAX_VALUE);
+            path.append(child);
+            serializeNode(oa, path);
         }
     }
 
diff --git a/src/java/main/org/apache/zookeeper/server/DatadirCleanupManager.java b/src/java/main/org/apache/zookeeper/server/DatadirCleanupManager.java
index 8c47971..e8dbeb7 100644
--- a/src/java/main/org/apache/zookeeper/server/DatadirCleanupManager.java
+++ b/src/java/main/org/apache/zookeeper/server/DatadirCleanupManager.java
@@ -139,7 +139,7 @@ public class DatadirCleanupManager {
             try {
                 PurgeTxnLog.purge(new File(logsDir), new File(snapsDir), snapRetainCount);
             } catch (Exception e) {
-                LOG.error("Error occured while purging.", e);
+                LOG.error("Error occurred while purging.", e);
             }
             LOG.info("Purge task completed.");
         }
diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java
index 0ed1c64..456d4c2 100644
--- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java
+++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java
@@ -62,7 +62,7 @@ public class NIOServerCnxn extends ServerCnxn {
 
     final SocketChannel sock;
 
-    private final SelectionKey sk;
+    protected final SelectionKey sk;
 
     boolean initialized;
 
@@ -74,7 +74,7 @@ public class NIOServerCnxn extends ServerCnxn {
 
     int sessionTimeout;
 
-    private final ZooKeeperServer zkServer;
+    protected final ZooKeeperServer zkServer;
 
     /**
      * The number of requests that have been submitted but not yet responded to.
@@ -144,38 +144,49 @@ public class NIOServerCnxn extends ServerCnxn {
     
     public void sendBuffer(ByteBuffer bb) {
         try {
-            if (bb != ServerCnxnFactory.closeConn) {
-                // We check if write interest here because if it is NOT set,
-                // nothing is queued, so we can try to send the buffer right
-                // away without waking up the selector
-                if ((sk.interestOps() & SelectionKey.OP_WRITE) == 0) {
-                    try {
-                        sock.write(bb);
-                    } catch (IOException e) {
-                        // we are just doing best effort right now
-                    }
-                }
-                // if there is nothing left to send, we are done
-                if (bb.remaining() == 0) {
-                    packetSent();
-                    return;
+            internalSendBuffer(bb);
+        } catch(Exception e) {
+            LOG.error("Unexpected Exception: ", e);
+        }
+    }
+
+    /**
+     * This method implements the internals of sendBuffer. We
+     * have separated it from send buffer to be able to catch
+     * exceptions when testing.
+     *
+     * @param bb Buffer to send.
+     */
+    protected void internalSendBuffer(ByteBuffer bb) {
+        if (bb != ServerCnxnFactory.closeConn) {
+            // We check if write interest here because if it is NOT set,
+            // nothing is queued, so we can try to send the buffer right
+            // away without waking up the selector
+            if(sk.isValid() &&
+                    ((sk.interestOps() & SelectionKey.OP_WRITE) == 0)) {
+                try {
+                    sock.write(bb);
+                } catch (IOException e) {
+                    // we are just doing best effort right now
                 }
             }
+            // if there is nothing left to send, we are done
+            if (bb.remaining() == 0) {
+                packetSent();
+                return;
+            }
+        }
 
-            synchronized(this.factory){
-                sk.selector().wakeup();
-                if (LOG.isTraceEnabled()) {
-                    LOG.trace("Add a buffer to outgoingBuffers, sk " + sk
-                            + " is valid: " + sk.isValid());
-                }
-                outgoingBuffers.add(bb);
-                if (sk.isValid()) {
-                    sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE);
-                }
+        synchronized(this.factory){
+            sk.selector().wakeup();
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Add a buffer to outgoingBuffers, sk " + sk
+                        + " is valid: " + sk.isValid());
+            }
+            outgoingBuffers.add(bb);
+            if (sk.isValid()) {
+                sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE);
             }
-            
-        } catch(Exception e) {
-            LOG.error("Unexpected Exception: ", e);
         }
     }
 
@@ -412,7 +423,7 @@ public class NIOServerCnxn extends ServerCnxn {
     }
 
     private void readConnectRequest() throws IOException, InterruptedException {
-        if (zkServer == null) {
+        if (!isZKServerRunning()) {
             throw new IOException("ZooKeeperServer not running");
         }
         zkServer.processConnectRequest(this, incomingBuffer);
@@ -575,7 +586,7 @@ public class NIOServerCnxn extends ServerCnxn {
             
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             } else {
                 zkServer.dumpConf(pw);
@@ -590,7 +601,7 @@ public class NIOServerCnxn extends ServerCnxn {
         
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             }
             else { 
@@ -607,7 +618,7 @@ public class NIOServerCnxn extends ServerCnxn {
         
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             } else {
                 synchronized(factory.cnxns){
@@ -627,7 +638,7 @@ public class NIOServerCnxn extends ServerCnxn {
         
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             }
             else {
@@ -649,7 +660,7 @@ public class NIOServerCnxn extends ServerCnxn {
         @SuppressWarnings("unchecked")
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             }
             else {   
@@ -691,7 +702,7 @@ public class NIOServerCnxn extends ServerCnxn {
         @SuppressWarnings("unchecked")
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             } else {
                 // clone should be faster than iteration
@@ -718,7 +729,7 @@ public class NIOServerCnxn extends ServerCnxn {
 
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             } else {
                 DataTree dt = zkServer.getZKDatabase().getDataTree();
@@ -742,7 +753,7 @@ public class NIOServerCnxn extends ServerCnxn {
 
         @Override
         public void commandRun() {
-            if(zkServer == null) {
+            if(!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
                 return;
             }
@@ -804,7 +815,7 @@ public class NIOServerCnxn extends ServerCnxn {
 
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.print("null");
             } else if (zkServer instanceof ReadOnlyZooKeeperServer) {
                 pw.print("ro");
@@ -814,18 +825,30 @@ public class NIOServerCnxn extends ServerCnxn {
         }
     }
 
+    private class NopCommand extends CommandThread {
+        private String msg;
+
+        public NopCommand(PrintWriter pw, String msg) {
+            super(pw);
+            this.msg = msg;
+        }
+
+        @Override
+        public void commandRun() {
+            pw.println(msg);
+        }
+    }
+
     /** Return if four letter word found and responded to, otw false **/
     private boolean checkFourLetterWord(final SelectionKey k, final int len)
     throws IOException
     {
         // We take advantage of the limited size of the length to look
         // for cmds. They are all 4-bytes which fits inside of an int
-        String cmd = cmd2String.get(len);
-        if (cmd == null) {
+        if (!ServerCnxn.isKnown(len)) {
             return false;
         }
-        LOG.info("Processing " + cmd + " command from "
-                + sock.socket().getRemoteSocketAddress());
+
         packetReceived();
 
         /** cancel the selection key to remove the socket handling
@@ -847,6 +870,19 @@ public class NIOServerCnxn extends ServerCnxn {
 
         final PrintWriter pwriter = new PrintWriter(
                 new BufferedWriter(new SendBufferWriter()));
+
+        String cmd = ServerCnxn.getCommandString(len);
+        // ZOOKEEPER-2693: don't execute 4lw if it's not enabled.
+        if (!ServerCnxn.isEnabled(cmd)) {
+            LOG.debug("Command {} is not executed because it is not in the whitelist.", cmd);
+            NopCommand nopCmd = new NopCommand(pwriter, cmd + " is not executed because it is not in the whitelist.");
+            nopCmd.start();
+            return true;
+        }
+
+        LOG.info("Processing " + cmd + " command from "
+                + sock.socket().getRemoteSocketAddress());
+
         if (len == ruokCmd) {
             RuokCommand ruok = new RuokCommand(pwriter);
             ruok.start();
@@ -928,7 +964,7 @@ public class NIOServerCnxn extends ServerCnxn {
         if (len < 0 || len > BinaryInputArchive.maxBuffer) {
             throw new IOException("Len error " + len);
         }
-        if (zkServer == null) {
+        if (!isZKServerRunning()) {
             throw new IOException("ZooKeeperServer not running");
         }
         incomingBuffer = ByteBuffer.allocate(len);
@@ -1154,10 +1190,16 @@ public class NIOServerCnxn extends ServerCnxn {
 
     @Override
     protected ServerStats serverStats() {
-        if (zkServer == null) {
+        if (!isZKServerRunning()) {
             return null;
         }
         return zkServer.serverStats();
     }
 
+    /**
+     * @return true if the server is running, false otherwise.
+     */
+    boolean isZKServerRunning() {
+        return zkServer != null && zkServer.isRunning();
+    }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java
index 35c8518..203f0e6 100644
--- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java
+++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java
@@ -382,7 +382,7 @@ public class NettyServerCnxn extends ServerCnxn {
             
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             } else {
                 zkServer.dumpConf(pw);
@@ -397,7 +397,7 @@ public class NettyServerCnxn extends ServerCnxn {
         
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             }
             else { 
@@ -414,7 +414,7 @@ public class NettyServerCnxn extends ServerCnxn {
         
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             } else {
                 synchronized(factory.cnxns){
@@ -434,7 +434,7 @@ public class NettyServerCnxn extends ServerCnxn {
         
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             }
             else {
@@ -455,7 +455,7 @@ public class NettyServerCnxn extends ServerCnxn {
         
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             }
             else {   
@@ -495,7 +495,7 @@ public class NettyServerCnxn extends ServerCnxn {
         
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             } else {
                 // clone should be faster than iteration
@@ -522,7 +522,7 @@ public class NettyServerCnxn extends ServerCnxn {
 
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
             } else {
                 DataTree dt = zkServer.getZKDatabase().getDataTree();
@@ -546,7 +546,7 @@ public class NettyServerCnxn extends ServerCnxn {
 
         @Override
         public void commandRun() {
-            if(zkServer == null) {
+            if(!isZKServerRunning()) {
                 pw.println(ZK_NOT_SERVING);
                 return;
             }
@@ -608,7 +608,7 @@ public class NettyServerCnxn extends ServerCnxn {
 
         @Override
         public void commandRun() {
-            if (zkServer == null) {
+            if (!isZKServerRunning()) {
                 pw.print("null");
             } else if (zkServer instanceof ReadOnlyZooKeeperServer) {
                 pw.print("ro");
@@ -618,23 +618,47 @@ public class NettyServerCnxn extends ServerCnxn {
         }
     }
 
+    private class NopCommand extends CommandThread {
+        private String msg;
+
+        public NopCommand(PrintWriter pw, String msg) {
+            super(pw);
+            this.msg = msg;
+        }
+
+        @Override
+        public void commandRun() {
+            pw.println(msg);
+        }
+    }
+
     /** Return if four letter word found and responded to, otw false **/
     private boolean checkFourLetterWord(final Channel channel,
             ChannelBuffer message, final int len) throws IOException
     {
         // We take advantage of the limited size of the length to look
         // for cmds. They are all 4-bytes which fits inside of an int
-        String cmd = cmd2String.get(len);
-        if (cmd == null) {
+        if (!ServerCnxn.isKnown(len)) {
             return false;
         }
+
         channel.setInterestOps(0).awaitUninterruptibly();
-        LOG.info("Processing " + cmd + " command from "
-                + channel.getRemoteAddress());
         packetReceived();
 
         final PrintWriter pwriter = new PrintWriter(
                 new BufferedWriter(new SendBufferWriter()));
+
+        String cmd = ServerCnxn.getCommandString(len);
+        // ZOOKEEPER-2693: don't execute 4lw if it's not enabled.
+        if (!ServerCnxn.isEnabled(cmd)) {
+            LOG.debug("Command {} is not executed because it is not in the whitelist.", cmd);
+            NopCommand nopCmd = new NopCommand(pwriter, cmd + " is not executed because it is not in the whitelist.");
+            nopCmd.start();
+            return true;
+        }
+
+        LOG.info("Processing " + cmd + " command from " + channel.getRemoteAddress());
+
         if (len == ruokCmd) {
             RuokCommand ruok = new RuokCommand(pwriter);
             ruok.start();
@@ -735,7 +759,7 @@ public class NettyServerCnxn extends ServerCnxn {
                         bb.flip();
 
                         ZooKeeperServer zks = this.zkServer;
-                        if (zks == null) {
+                        if (zks == null || !zks.isRunning()) {
                             throw new IOException("ZK down");
                         }
                         if (initialized) {
@@ -846,10 +870,16 @@ public class NettyServerCnxn extends ServerCnxn {
 
     @Override
     protected ServerStats serverStats() {
-        if (zkServer == null) {
+        if (!isZKServerRunning()) {
             return null;
         }
         return zkServer.serverStats();
     }
 
+    /**
+     * @return true if the server is running, false otherwise.
+     */
+    boolean isZKServerRunning() {
+        return zkServer != null && zkServer.isRunning();
+    }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
index fe02b8f..1248b08 100644
--- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
+++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
@@ -154,8 +154,7 @@ public class PrepRequestProcessor extends ZooKeeperCriticalThread implements
                     synchronized(n) {
                         children = n.getChildren();
                     }
-                    lastChange = new ChangeRecord(-1, path, n.stat,
-                        children != null ? children.size() : 0,
+                    lastChange = new ChangeRecord(-1, path, n.stat, children.size(),
                             zks.getZKDatabase().aclForNode(n));
                 }
             }
diff --git a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java
index 25b949a..d4effaf 100644
--- a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java
+++ b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java
@@ -24,10 +24,14 @@ import java.io.IOException;
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
 import org.apache.zookeeper.server.persistence.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * this class is used to clean up the 
@@ -38,6 +42,7 @@ import org.apache.zookeeper.server.persistence.Util;
  * and the corresponding logs.
  */
 public class PurgeTxnLog {
+    private static final Logger LOG = LoggerFactory.getLogger(PurgeTxnLog.class);
 
     private static final String COUNT_ERR_MSG = "count should be greater than or equal to 3";
 
@@ -72,18 +77,43 @@ public class PurgeTxnLog {
         FileTxnSnapLog txnLog = new FileTxnSnapLog(dataDir, snapDir);
 
         List<File> snaps = txnLog.findNRecentSnapshots(num);
-        retainNRecentSnapshots(txnLog, snaps);
+        int numSnaps = snaps.size();
+        if (numSnaps > 0) {
+            purgeOlderSnapshots(txnLog, snaps.get(numSnaps - 1));
+        }
     }
 
     // VisibleForTesting
-    static void retainNRecentSnapshots(FileTxnSnapLog txnLog, List<File> snaps) {
-        // found any valid recent snapshots?
-        if (snaps.size() == 0)
-            return;
-        File snapShot = snaps.get(snaps.size() -1);
+    static void purgeOlderSnapshots(FileTxnSnapLog txnLog, File snapShot) {
         final long leastZxidToBeRetain = Util.getZxidFromName(
                 snapShot.getName(), PREFIX_SNAPSHOT);
 
+        /**
+         * We delete all files with a zxid in their name that is less than leastZxidToBeRetain.
+         * This rule applies to both snapshot files as well as log files, with the following
+         * exception for log files.
+         *
+         * A log file with zxid less than X may contain transactions with zxid larger than X.  More
+         * precisely, a log file named log.(X-a) may contain transactions newer than snapshot.X if
+         * there are no other log files with starting zxid in the interval (X-a, X].  Assuming the
+         * latter condition is true, log.(X-a) must be retained to ensure that snapshot.X is
+         * recoverable.  In fact, this log file may very well extend beyond snapshot.X to newer
+         * snapshot files if these newer snapshots were not accompanied by log rollover (possible in
+         * the learner state machine at the time of this writing).  We can make more precise
+         * determination of whether log.(leastZxidToBeRetain-a) for the smallest 'a' is actually
+         * needed or not (e.g. not needed if there's a log file named log.(leastZxidToBeRetain+1)),
+         * but the complexity quickly adds up with gains only in uncommon scenarios.  It's safe and
+         * simple to just preserve log.(leastZxidToBeRetain-a) for the smallest 'a' to ensure
+         * recoverability of all snapshots being retained.  We determine that log file here by
+         * calling txnLog.getSnapshotLogs().
+         */
+        final Set<File> retainedTxnLogs = new HashSet<File>();
+        retainedTxnLogs.addAll(Arrays.asList(txnLog.getSnapshotLogs(leastZxidToBeRetain)));
+
+        /**
+         * Finds all candidates for deletion, which are files with a zxid in their name that is less
+         * than leastZxidToBeRetain.  There's an exception to this rule, as noted above.
+         */
         class MyFileFilter implements FileFilter{
             private final String prefix;
             MyFileFilter(String prefix){
@@ -92,6 +122,9 @@ public class PurgeTxnLog {
             public boolean accept(File f){
                 if(!f.getName().startsWith(prefix + "."))
                     return false;
+                if (retainedTxnLogs.contains(f)) {
+                    return false;
+                }
                 long fZxid = Util.getZxidFromName(f.getName(), prefix);
                 if (fZxid >= leastZxidToBeRetain) {
                     return false;
@@ -108,9 +141,11 @@ public class PurgeTxnLog {
         // remove the old files
         for(File f: files)
         {
-            System.out.println("Removing file: "+
+            final String msg = "Removing file: "+
                 DateFormat.getDateTimeInstance().format(f.lastModified())+
-                "\t"+f.getPath());
+                "\t"+f.getPath();
+            LOG.info(msg);
+            System.out.println(msg);
             if(!f.delete()){
                 System.err.println("Failed to remove "+f.getPath());
             }
diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java
index 6dd509b..b554df9 100644
--- a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java
+++ b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java
@@ -26,10 +26,17 @@ import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
+import java.util.Map;
 import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import org.apache.jute.Record;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
@@ -227,8 +234,91 @@ public abstract class ServerCnxn implements Stats, Watcher {
     protected final static int isroCmd = ByteBuffer.wrap("isro".getBytes())
             .getInt();
 
-    protected final static HashMap<Integer, String> cmd2String =
-        new HashMap<Integer, String>();
+    protected final static Map<Integer, String> cmd2String = new HashMap<Integer, String>();
+
+    private static final String ZOOKEEPER_4LW_COMMANDS_WHITELIST = "zookeeper.4lw.commands.whitelist";
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServerCnxn.class);
+
+    private static final Set<String> whiteListedCommands = new HashSet<String>();
+
+    private static boolean whiteListInitialized = false;
+
+    // @VisibleForTesting
+    public synchronized static void resetWhiteList() {
+        whiteListInitialized = false;
+        whiteListedCommands.clear();
+    }
+
+    /**
+     * Return the string representation of the specified command code.
+     */
+    public static String getCommandString(int command) {
+        return cmd2String.get(command);
+    }
+
+    /**
+     * Check if the specified command code is from a known command.
+     *
+     * @param command The integer code of command.
+     * @return true if the specified command is known, false otherwise.
+     */
+    public static boolean isKnown(int command) {
+        return cmd2String.containsKey(command);
+    }
+
+    /**
+     * Check if the specified command is enabled.
+     *
+     * In ZOOKEEPER-2693 we introduce a configuration option to only
+     * allow a specific set of white listed commands to execute.
+     * A command will only be executed if it is also configured
+     * in the white list.
+     *
+     * @param command The command string.
+     * @return true if the specified command is enabled.
+     */
+    public synchronized static boolean isEnabled(String command) {
+        if (whiteListInitialized) {
+            return whiteListedCommands.contains(command);
+        }
+
+        String commands = System.getProperty(ZOOKEEPER_4LW_COMMANDS_WHITELIST);
+        if (commands != null) {
+            String[] list = commands.split(",");
+            for (String cmd : list) {
+                if (cmd.trim().equals("*")) {
+                    for (Map.Entry<Integer, String> entry : cmd2String.entrySet()) {
+                        whiteListedCommands.add(entry.getValue());
+                    }
+                    break;
+                }
+                if (!cmd.trim().isEmpty()) {
+                    whiteListedCommands.add(cmd.trim());
+                }
+            }
+        } else {
+            for (Map.Entry<Integer, String> entry : cmd2String.entrySet()) {
+                String cmd = entry.getValue();
+                if (cmd.equals("wchc") || cmd.equals("wchp")) {
+                    // ZOOKEEPER-2693 / disable these exploitable commands by default.
+                    continue;
+                }
+                whiteListedCommands.add(cmd);
+            }
+        }
+
+        // Readonly mode depends on "isro".
+        if (System.getProperty("readonlymode.enabled", "false").equals("true")) {
+            whiteListedCommands.add("isro");
+        }
+        // zkServer.sh depends on "srvr".
+        whiteListedCommands.add("srvr");
+        whiteListInitialized = true;
+        LOG.info("The list of known four letter word commands is : {}", Arrays.asList(cmd2String));
+        LOG.info("The list of enabled four letter word commands is : {}", Arrays.asList(whiteListedCommands));
+        return whiteListedCommands.contains(command);
+    }
 
     // specify all of the commands that are available
     static {
diff --git a/src/java/main/org/apache/zookeeper/server/ServerConfig.java b/src/java/main/org/apache/zookeeper/server/ServerConfig.java
index ec710cd..626ae11 100644
--- a/src/java/main/org/apache/zookeeper/server/ServerConfig.java
+++ b/src/java/main/org/apache/zookeeper/server/ServerConfig.java
@@ -47,20 +47,19 @@ public class ServerConfig {
 
     /**
      * Parse arguments for server configuration
-     * @param args clientPort dataDir and optional tickTime
+     * @param args clientPort dataDir and optional tickTime and maxClientCnxns
      * @return ServerConfig configured wrt arguments
      * @throws IllegalArgumentException on invalid usage
      */
     public void parse(String[] args) {
         if (args.length < 2 || args.length > 4) {
-            throw new IllegalArgumentException("Invalid args:"
-                    + Arrays.toString(args));
+            throw new IllegalArgumentException("Invalid number of arguments:" + Arrays.toString(args));
         }
 
         clientPortAddress = new InetSocketAddress(Integer.parseInt(args[0]));
         dataDir = args[1];
         dataLogDir = dataDir;
-        if (args.length == 3) {
+        if (args.length >= 3) {
             tickTime = Integer.parseInt(args[2]);
         }
         if (args.length == 4) {
diff --git a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java
index f94c54d..bc43402 100644
--- a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java
+++ b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java
@@ -94,10 +94,8 @@ public class SnapshotFormatter {
             }
             children = n.getChildren();
         }
-        if (children != null) {
-            for (String child : children) {
-                printZnode(dataTree, name + (name.equals("/") ? "" : "/") + child);
-            }
+        for (String child : children) {
+            printZnode(dataTree, name + (name.equals("/") ? "" : "/") + child);
         }
     }
 
diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java
index 71870ce..dd6ee8f 100644
--- a/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java
+++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java
@@ -18,22 +18,12 @@
 
 package org.apache.zookeeper.server;
 
-import java.security.Principal;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-
 import javax.security.auth.Subject;
-import javax.security.sasl.Sasl;
 import javax.security.sasl.SaslException;
 import javax.security.sasl.SaslServer;
 
 import org.apache.zookeeper.Login;
-import org.ietf.jgss.GSSContext;
-import org.ietf.jgss.GSSCredential;
-import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.GSSName;
-import org.ietf.jgss.Oid;
+import org.apache.zookeeper.util.SecurityUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,107 +41,9 @@ public class ZooKeeperSaslServer {
     private SaslServer createSaslServer(final Login login) {
         synchronized (login) {
             Subject subject = login.getSubject();
-            if (subject != null) {
-                // server is using a JAAS-authenticated subject: determine service principal name and hostname from zk server's subject.
-                if (subject.getPrincipals().size() > 0) {
-                    try {
-                        final Object[] principals = subject.getPrincipals().toArray();
-                        final Principal servicePrincipal = (Principal)principals[0];
-
-                        // e.g. servicePrincipalNameAndHostname := "zookeeper/myhost.foo.com at FOO.COM"
-                        final String servicePrincipalNameAndHostname = servicePrincipal.getName();
-
-                        int indexOf = servicePrincipalNameAndHostname.indexOf("/");
-
-                        // e.g. servicePrincipalName := "zookeeper"
-                        final String servicePrincipalName = servicePrincipalNameAndHostname.substring(0, indexOf);
-
-                        // e.g. serviceHostnameAndKerbDomain := "myhost.foo.com at FOO.COM"
-                        final String serviceHostnameAndKerbDomain = servicePrincipalNameAndHostname.substring(indexOf+1,servicePrincipalNameAndHostname.length());
-
-                        indexOf = serviceHostnameAndKerbDomain.indexOf("@");
-                        // e.g. serviceHostname := "myhost.foo.com"
-                        final String serviceHostname = serviceHostnameAndKerbDomain.substring(0,indexOf);
-
-                        final String mech = "GSSAPI";   // TODO: should depend on zoo.cfg specified mechs, but if subject is non-null, it can be assumed to be GSSAPI.
-
-                        LOG.debug("serviceHostname is '"+ serviceHostname + "'");
-                        LOG.debug("servicePrincipalName is '"+ servicePrincipalName + "'");
-                        LOG.debug("SASL mechanism(mech) is '"+ mech +"'");
-
-                        boolean usingNativeJgss =
-                        		Boolean.getBoolean("sun.security.jgss.native");
-                        if (usingNativeJgss) {
-                        	// http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
-                        	// """
-                        	// In addition, when performing operations as a particular
-                        	// Subject, e.g. Subject.doAs(...) or
-                        	// Subject.doAsPrivileged(...), the to-be-used
-                        	// GSSCredential should be added to Subject's
-                        	// private credential set. Otherwise, the GSS operations
-                        	// will fail since no credential is found.
-                        	// """
-                        	try {
-                        		GSSManager manager = GSSManager.getInstance();
-                        		Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
-                        		GSSName gssName = manager.createName(
-                        				servicePrincipalName + "@" + serviceHostname,
-                        				GSSName.NT_HOSTBASED_SERVICE);
-                        		GSSCredential cred = manager.createCredential(gssName,
-                        				GSSContext.DEFAULT_LIFETIME,
-                        				krb5Mechanism,
-                        				GSSCredential.ACCEPT_ONLY);
-                        		subject.getPrivateCredentials().add(cred);
-                        		if (LOG.isDebugEnabled()) {
-                        			LOG.debug("Added private credential to subject: " + cred);
-                        		}
-                        	} catch (GSSException ex) {
-                        		LOG.warn("Cannot add private credential to subject; " +
-                        				"clients authentication may fail", ex);
-                        	}
-                        }
-                        try {
-                            return Subject.doAs(subject,new PrivilegedExceptionAction<SaslServer>() {
-                                public SaslServer run() {
-                                    try {
-                                        SaslServer saslServer;
-                                        saslServer = Sasl.createSaslServer(mech, servicePrincipalName, serviceHostname, null, login.callbackHandler);
-                                        return saslServer;
-                                    }
-                                    catch (SaslException e) {
-                                        LOG.error("Zookeeper Server failed to create a SaslServer to interact with a client during session initiation: " + e);
-                                        e.printStackTrace();
-                                        return null;
-                                    }
-                                }
-                            }
-                            );
-                        }
-                        catch (PrivilegedActionException e) {
-                            // TODO: exit server at this point(?)
-                            LOG.error("Zookeeper Quorum member experienced a PrivilegedActionException exception while creating a SaslServer using a JAAS principal context:" + e);
-                            e.printStackTrace();
-                        }
-                    }
-                    catch (IndexOutOfBoundsException e) {
-                        LOG.error("server principal name/hostname determination error: ", e);
-                    }
-                }
-                else {
-                    // JAAS non-GSSAPI authentication: assuming and supporting only DIGEST-MD5 mechanism for now.
-                    // TODO: use 'authMech=' value in zoo.cfg.
-                    try {
-                        SaslServer saslServer = Sasl.createSaslServer("DIGEST-MD5","zookeeper","zk-sasl-md5",null, login.callbackHandler);
-                        return saslServer;
-                    }
-                    catch (SaslException e) {
-                        LOG.error("Zookeeper Quorum member failed to create a SaslServer to interact with a client during session initiation", e);
-                    }
-                }
-            }
+            return SecurityUtils.createSaslServer(subject, "zookeeper",
+                    "zk-sasl-md5", login.callbackHandler, LOG);
         }
-        LOG.error("failed to create saslServer object.");
-        return null;
     }
 
     public byte[] evaluateResponse(byte[] response) throws SaslException {
diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java
index 62ac466..b0a8341 100644
--- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java
+++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java
@@ -489,7 +489,15 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider {
         return state == State.RUNNING;
     }
 
-    public synchronized void shutdown() {
+    public void shutdown() {
+        shutdown(false);
+    }
+
+    /**
+     * Shut down the server instance
+     * @param fullyShutDown true if another server using the same database will not replace this one in the same process
+     */
+    public synchronized void shutdown(boolean fullyShutDown) {
         if (!canShutdown()) {
             LOG.debug("ZooKeeper server is not running, so not proceeding to shutdown!");
             return;
@@ -507,9 +515,15 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider {
         if (firstProcessor != null) {
             firstProcessor.shutdown();
         }
-        if (zkDb != null) {
+
+        if (fullyShutDown && zkDb != null) {
             zkDb.clear();
         }
+        // else there is no need to clear the database
+        //  * When a new quorum is established we can still apply the diff
+        //    on top of the same zkDb data
+        //  * If we fetch a new snapshot from leader, the zkDb will be
+        //    cleared anyway before loading the snapshot
 
         unregisterJMX();
     }
diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java
index 612d227..1b0f59f 100644
--- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java
+++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java
@@ -124,7 +124,7 @@ public class ZooKeeperServerMain {
 
             cnxnFactory.join();
             if (zkServer.canShutdown()) {
-                zkServer.shutdown();
+                zkServer.shutdown(true);
             }
         } catch (InterruptedException e) {
             // warn, but generally this is ok
diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java
index 2830624..cf6cecf 100644
--- a/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java
+++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java
@@ -57,6 +57,6 @@ public class ZooKeeperThread extends Thread {
      *            - exception object
      */
     protected void handleException(String thName, Throwable e) {
-        LOG.warn("Exception occured from thread {}", thName, e);
+        LOG.warn("Exception occurred from thread {}", thName, e);
     }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/ZooTrace.java b/src/java/main/org/apache/zookeeper/server/ZooTrace.java
index ac14fe2..946a4bf 100644
--- a/src/java/main/org/apache/zookeeper/server/ZooTrace.java
+++ b/src/java/main/org/apache/zookeeper/server/ZooTrace.java
@@ -20,7 +20,7 @@ package org.apache.zookeeper.server;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
+import org.apache.zookeeper.server.quorum.LearnerHandler;
 import org.apache.zookeeper.server.quorum.QuorumPacket;
 
 /**
@@ -75,12 +75,10 @@ public class ZooTrace {
     static public void logQuorumPacket(Logger log, long mask,
             char direction, QuorumPacket qp)
     {
-        return;
-
-        // if (isTraceEnabled(log, mask)) {
-        // logTraceMessage(LOG, mask, direction + " "
-        // + FollowerHandler.packetToString(qp));
-        // }
+        if (isTraceEnabled(log, mask)) { 
+            logTraceMessage(log, mask, direction +
+                    " " + LearnerHandler.packetToString(qp));
+         }
     }
 
     static public void logRequest(Logger log, long mask,
diff --git a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java
index 2fbd6ed..9f53a4d 100644
--- a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java
+++ b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java
@@ -46,13 +46,15 @@ public class SaslServerCallbackHandler implements CallbackHandler {
     private String userName;
     private final Map<String,String> credentials = new HashMap<String,String>();
 
-    public SaslServerCallbackHandler(Configuration configuration) throws IOException {
-        String serverSection = System.getProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY,
-                                                  ZooKeeperSaslServer.DEFAULT_LOGIN_CONTEXT_NAME);
+    public SaslServerCallbackHandler(Configuration configuration)
+            throws IOException {
+        String serverSection = System.getProperty(
+                ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY,
+                ZooKeeperSaslServer.DEFAULT_LOGIN_CONTEXT_NAME);
         AppConfigurationEntry configurationEntries[] = configuration.getAppConfigurationEntry(serverSection);
 
         if (configurationEntries == null) {
-            String errorMessage = "Could not find a 'Server' entry in this configuration: Server cannot start.";
+            String errorMessage = "Could not find a '" + serverSection + "' entry in this configuration: Server cannot start.";
             LOG.error(errorMessage);
             throw new IOException(errorMessage);
         }
@@ -134,7 +136,7 @@ public class SaslServerCallbackHandler implements CallbackHandler {
             LOG.info("Setting authorizedID: " + userNameBuilder);
             ac.setAuthorizedID(userNameBuilder.toString());
         } catch (IOException e) {
-            LOG.error("Failed to set name based on Kerberos authentication rules.");
+            LOG.error("Failed to set name based on Kerberos authentication rules.", e);
         }
     }
 
diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileSnap.java b/src/java/main/org/apache/zookeeper/server/persistence/FileSnap.java
index 8f57338..cf464a1 100644
--- a/src/java/main/org/apache/zookeeper/server/persistence/FileSnap.java
+++ b/src/java/main/org/apache/zookeeper/server/persistence/FileSnap.java
@@ -177,19 +177,21 @@ public class FileSnap implements SnapShot {
     /**
      * find the last n snapshots. this does not have
      * any checks if the snapshot might be valid or not
-     * @param the number of most recent snapshots 
+     * @param the number of most recent snapshots
      * @return the last n snapshots
      * @throws IOException
      */
     public List<File> findNRecentSnapshots(int n) throws IOException {
         List<File> files = Util.sortDataDir(snapDir.listFiles(), "snapshot", false);
-        int i = 0;
+        int count = 0;
         List<File> list = new ArrayList<File>();
         for (File f: files) {
-            if (i==n)
+            if (count == n)
                 break;
-            i++;
-            list.add(f);
+            if (Util.getZxidFromName(f.getName(), "snapshot") != -1) {
+                count++;
+                list.add(f);
+            }
         }
         return list;
     }
diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java
index 6f0df51..24614c0 100644
--- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java
+++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java
@@ -86,12 +86,20 @@ public class FileTxnSnapLog {
                         + this.dataDir);
             }
         }
+        if (!this.dataDir.canWrite()) {
+            throw new IOException("Cannot write to data directory " + this.dataDir);
+        }
+
         if (!this.snapDir.exists()) {
             if (!this.snapDir.mkdirs()) {
                 throw new IOException("Unable to create snap directory "
                         + this.snapDir);
             }
         }
+        if (!this.snapDir.canWrite()) {
+            throw new IOException("Cannot write to snap directory " + this.snapDir);
+        }
+
         txnLog = new FileTxnLog(this.dataDir);
         snapLog = new FileSnap(this.snapDir);
     }
@@ -294,9 +302,11 @@ public class FileTxnSnapLog {
     }
 
     /**
-     * get the snapshot logs that are greater than
-     * the given zxid 
-     * @param zxid the zxid that contains logs greater than 
+     * get the snapshot logs which may contain transactions newer than the given zxid.
+     * This includes logs with starting zxid greater than given zxid, as well as the
+     * newest transaction log with starting zxid less than given zxid.  The latter log
+     * file may contain transactions beyond given zxid.
+     * @param zxid the zxid that contains logs greater than
      * zxid
      * @return
      */
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java
index 78f3aa6..2a3d4fd 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java
@@ -997,6 +997,8 @@ public class FastLeaderElection implements Election {
                 LOG.warn("Failed to unregister with JMX", e);
             }
             self.jmxLeaderElectionBean = null;
+            LOG.debug("Number of connection processing threads: {}",
+                    manager.getConnectionThreadCount());
         }
     }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java
index 043a522..a17af49 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.net.InetSocketAddress;
 
 import org.apache.jute.Record;
+import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
 import org.apache.zookeeper.server.util.SerializeUtils;
 import org.apache.zookeeper.server.util.ZxidUtils;
 import org.apache.zookeeper.txn.TxnHeader;
@@ -58,15 +59,16 @@ public class Follower extends Learner{
      */
     void followLeader() throws InterruptedException {
         self.end_fle = System.currentTimeMillis();
-        LOG.info("FOLLOWING - LEADER ELECTION TOOK - " +
-              (self.end_fle - self.start_fle));
+        long electionTimeTaken = self.end_fle - self.start_fle;
+        self.setElectionTimeTaken(electionTimeTaken);
+        LOG.info("FOLLOWING - LEADER ELECTION TOOK - {}", electionTimeTaken);
         self.start_fle = 0;
         self.end_fle = 0;
         fzk.registerJMX(new FollowerBean(this, zk), self.jmxLocalPeerBean);
         try {
-            InetSocketAddress addr = findLeader();            
+            QuorumServer leaderServer = findLeader();            
             try {
-                connectToLeader(addr);
+                connectToLeader(leaderServer.addr, leaderServer.hostname);
                 long newEpochZxid = registerWithLeader(Leader.FOLLOWERINFO);
 
                 //check to see if the leader zxid is lower than ours
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FollowerBean.java b/src/java/main/org/apache/zookeeper/server/quorum/FollowerBean.java
index fd31fa2..43d7cb7 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/FollowerBean.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/FollowerBean.java
@@ -22,7 +22,7 @@ import org.apache.zookeeper.server.ZooKeeperServer;
 import org.apache.zookeeper.server.ZooKeeperServerBean;
 
 /**
- * Follower MBean inteface implementation
+ * Follower MBean interface implementation.
  */
 public class FollowerBean extends ZooKeeperServerBean implements FollowerMXBean {
     private final Follower follower;
@@ -47,4 +47,9 @@ public class FollowerBean extends ZooKeeperServerBean implements FollowerMXBean
     public int getPendingRevalidationCount() {
         return follower.getPendingRevalidationsCount();
     }
+
+    @Override
+    public long getElectionTimeTaken() {
+        return follower.self.getElectionTimeTaken();
+    }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FollowerMXBean.java b/src/java/main/org/apache/zookeeper/server/quorum/FollowerMXBean.java
index ded0e1c..45c7fd8 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/FollowerMXBean.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/FollowerMXBean.java
@@ -38,4 +38,9 @@ public interface FollowerMXBean extends ZooKeeperServerMXBean {
      * @return count of pending revalidations
      */
     public int getPendingRevalidationCount();
+
+    /**
+     * @return time taken for leader election in milliseconds.
+     */
+    public long getElectionTimeTaken();
 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java
index c83d352..44e6b4f 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java
@@ -19,6 +19,7 @@
 package org.apache.zookeeper.server.quorum;
 
 import java.io.ByteArrayOutputStream;
+import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.net.BindException;
 import java.net.ServerSocket;
@@ -32,10 +33,12 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.security.sasl.SaslException;
 
 import org.apache.jute.BinaryOutputArchive;
 import org.apache.zookeeper.server.FinalRequestProcessor;
@@ -318,7 +321,10 @@ public class Leader {
                         // in LearnerHandler switch to the syncLimit
                         s.setSoTimeout(self.tickTime * self.initLimit);
                         s.setTcpNoDelay(nodelay);
-                        LearnerHandler fh = new LearnerHandler(s, Leader.this);
+
+                        BufferedInputStream is = new BufferedInputStream(
+                                s.getInputStream());
+                        LearnerHandler fh = new LearnerHandler(s, is, Leader.this);
                         fh.start();
                     } catch (SocketException e) {
                         if (stop) {
@@ -332,6 +338,8 @@ public class Leader {
                         } else {
                             throw e;
                         }
+                    } catch (SaslException e){
+                        LOG.error("Exception while connecting to quorum learner", e);
                     }
                 }
             } catch (Exception e) {
@@ -358,8 +366,9 @@ public class Leader {
      */
     void lead() throws IOException, InterruptedException {
         self.end_fle = System.currentTimeMillis();
-        LOG.info("LEADING - LEADER ELECTION TOOK - " +
-              (self.end_fle - self.start_fle));
+        long electionTimeTaken = self.end_fle - self.start_fle;
+        self.setElectionTimeTaken(electionTimeTaken);
+        LOG.info("LEADING - LEADER ELECTION TOOK - {}", electionTimeTaken);
         self.start_fle = 0;
         self.end_fle = 0;
 
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java b/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java
index b5a3a10..6ab2c30 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java
@@ -50,4 +50,8 @@ public class LeaderBean extends ZooKeeperServerBean implements LeaderMXBean {
         return sb.toString();
     }
 
+    @Override
+    public long getElectionTimeTaken() {
+        return leader.self.getElectionTimeTaken();
+    }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java b/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java
index bf08104..66428a4 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java
@@ -33,4 +33,9 @@ public interface LeaderMXBean extends ZooKeeperServerMXBean {
      * @return information on current followers
      */
     public String followerInfo();
+
+    /**
+     * @return time taken for leader election in milliseconds.
+     */
+    public long getElectionTimeTaken();
 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java
index 647b8a2..c54f6e6 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java
@@ -39,8 +39,6 @@ import org.apache.jute.BinaryOutputArchive;
 import org.apache.jute.InputArchive;
 import org.apache.jute.OutputArchive;
 import org.apache.jute.Record;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.apache.zookeeper.server.Request;
 import org.apache.zookeeper.server.ServerCnxn;
 import org.apache.zookeeper.server.ZooTrace;
@@ -48,6 +46,8 @@ import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
 import org.apache.zookeeper.server.util.SerializeUtils;
 import org.apache.zookeeper.server.util.ZxidUtils;
 import org.apache.zookeeper.txn.TxnHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * This class is the superclass of two of the three main actors in a ZK
@@ -191,8 +191,8 @@ public class Learner {
     /**
      * Returns the address of the node we think is the leader.
      */
-    protected InetSocketAddress findLeader() {
-        InetSocketAddress addr = null;
+    protected QuorumServer findLeader() {
+        QuorumServer leaderServer = null;
         // Find the leader by id
         Vote current = self.getCurrentVote();
         for (QuorumServer s : self.getView().values()) {
@@ -200,27 +200,28 @@ public class Learner {
                 // Ensure we have the leader's correct IP address before
                 // attempting to connect.
                 s.recreateSocketAddresses();
-                addr = s.addr;
+                leaderServer = s;
                 break;
             }
         }
-        if (addr == null) {
+        if (leaderServer == null) {
             LOG.warn("Couldn't find the leader with id = "
                     + current.getId());
         }
-        return addr;
+        return leaderServer;
     }
     
     /**
      * Establish a connection with the Leader found by findLeader. Retries
      * 5 times before giving up. 
      * @param addr - the address of the Leader to connect to.
-     * @throws IOException - if the socket connection fails on the 5th attempt
+     * @throws IOException <li>if the socket connection fails on the 5th attempt</li>
+     * <li>if there is an authentication failure while connecting to leader</li>
      * @throws ConnectException
      * @throws InterruptedException
      */
-    protected void connectToLeader(InetSocketAddress addr) 
-    throws IOException, ConnectException, InterruptedException {
+    protected void connectToLeader(InetSocketAddress addr, String hostname)
+            throws IOException, ConnectException, InterruptedException {
         sock = new Socket();        
         sock.setSoTimeout(self.tickTime * self.initLimit);
         for (int tries = 0; tries < 5; tries++) {
@@ -241,6 +242,9 @@ public class Learner {
             }
             Thread.sleep(1000);
         }
+
+        self.authLearner.authenticate(sock, hostname);
+
         leaderIs = BinaryInputArchive.getArchive(new BufferedInputStream(
                 sock.getInputStream()));
         bufferedOutput = new BufferedOutputStream(sock.getOutputStream());
@@ -317,13 +321,16 @@ public class Learner {
         QuorumPacket ack = new QuorumPacket(Leader.ACK, 0, null, null);
         QuorumPacket qp = new QuorumPacket();
         long newEpoch = ZxidUtils.getEpochFromZxid(newLeaderZxid);
-        
-        readPacket(qp);   
+        // In the DIFF case we don't need to do a snapshot because the transactions will sync on top of any existing snapshot
+        // For SNAP and TRUNC the snapshot is needed to save that history
+        boolean snapshotNeeded = true;
+        readPacket(qp);
         LinkedList<Long> packetsCommitted = new LinkedList<Long>();
         LinkedList<PacketInFlight> packetsNotCommitted = new LinkedList<PacketInFlight>();
         synchronized (zk) {
             if (qp.getType() == Leader.DIFF) {
-                LOG.info("Getting a diff from the leader 0x" + Long.toHexString(qp.getZxid()));                
+                LOG.info("Getting a diff from the leader 0x{}", Long.toHexString(qp.getZxid()));
+                snapshotNeeded = false;
             }
             else if (qp.getType() == Leader.SNAP) {
                 LOG.info("Getting a snapshot from leader");
@@ -360,10 +367,13 @@ public class Learner {
             
             long lastQueued = 0;
 
-            // in V1.0 we take a snapshot when we get the NEWLEADER message, but in pre V1.0
-            // we take the snapshot at the UPDATE, since V1.0 also gets the UPDATE (after the NEWLEADER)
+            // in Zab V1.0 (ZK 3.4+) we might take a snapshot when we get the NEWLEADER message, but in pre V1.0
+            // we take the snapshot on the UPDATE message, since Zab V1.0 also gets the UPDATE (after the NEWLEADER)
             // we need to make sure that we don't take the snapshot twice.
-            boolean snapshotTaken = false;
+            boolean isPreZAB1_0 = true;
+            //If we are not going to take the snapshot be sure the transactions are not applied in memory
+            // but written out to the transaction log
+            boolean writeToTxnLog = !snapshotNeeded;
             // we are now going to start getting transactions to apply followed by an UPTODATE
             outerLoop:
             while (self.isRunning()) {
@@ -383,7 +393,7 @@ public class Learner {
                     packetsNotCommitted.add(pif);
                     break;
                 case Leader.COMMIT:
-                    if (!snapshotTaken) {
+                    if (!writeToTxnLog) {
                         pif = packetsNotCommitted.peekFirst();
                         if (pif.hdr.getZxid() != qp.getZxid()) {
                             LOG.warn("Committing " + qp.getZxid() + ", but next proposal is " + pif.hdr.getZxid());
@@ -411,7 +421,7 @@ public class Learner {
                                 + Long.toHexString(lastQueued + 1));
                     }
                     lastQueued = packet.hdr.getZxid();
-                    if (!snapshotTaken) {
+                    if (!writeToTxnLog) {
                         // Apply to db directly if we haven't taken the snapshot
                         zk.processTxn(packet.hdr, packet.rec);
                     } else {
@@ -420,13 +430,14 @@ public class Learner {
                     }
                     break;
                 case Leader.UPTODATE:
-                    if (!snapshotTaken) { // true for the pre v1.0 case
+                    if (isPreZAB1_0) {
                         zk.takeSnapshot();
                         self.setCurrentEpoch(newEpoch);
                     }
                     self.cnxnFactory.setZooKeeperServer(zk);                
                     break outerLoop;
-                case Leader.NEWLEADER: // it will be NEWLEADER in v1.0
+                case Leader.NEWLEADER: // Getting NEWLEADER here instead of in discovery 
+                    // means this is Zab 1.0
                     // Create updatingEpoch file and remove it after current
                     // epoch is set. QuorumPeer.loadDataBase() uses this file to
                     // detect the case where the server was terminated after
@@ -437,13 +448,16 @@ public class Learner {
                         throw new IOException("Failed to create " +
                                               updating.toString());
                     }
-                    zk.takeSnapshot();
+                    if (snapshotNeeded) {
+                        zk.takeSnapshot();
+                    }
                     self.setCurrentEpoch(newEpoch);
                     if (!updating.delete()) {
                         throw new IOException("Failed to delete " +
                                               updating.toString());
                     }
-                    snapshotTaken = true;
+                    writeToTxnLog = true; //Anything after this needs to go to the transaction log, not applied directly in memory
+                    isPreZAB1_0 = false;
                     writePacket(new QuorumPacket(Leader.ACK, newLeaderZxid, null, null), true);
                     break;
                 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java
index 8a748c7..51ed7e7 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java
@@ -32,6 +32,8 @@ import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
 
+import javax.security.sasl.SaslException;
+
 import org.apache.jute.BinaryInputArchive;
 import org.apache.jute.BinaryOutputArchive;
 import org.apache.jute.Record;
@@ -153,15 +155,30 @@ public class LearnerHandler extends ZooKeeperThread {
 
     private BinaryOutputArchive oa;
 
+    private final BufferedInputStream bufferedInput;
     private BufferedOutputStream bufferedOutput;
 
-    LearnerHandler(Socket sock, Leader leader) throws IOException {
+    LearnerHandler(Socket sock, BufferedInputStream bufferedInput,
+                   Leader leader) throws IOException {
         super("LearnerHandler-" + sock.getRemoteSocketAddress());
         this.sock = sock;
         this.leader = leader;
-        leader.addLearnerHandler(this);
+        this.bufferedInput = bufferedInput;
+        try {
+            leader.self.authServer.authenticate(sock,
+                    new DataInputStream(bufferedInput));
+        } catch (IOException e) {
+            LOG.error("Server failed to authenticate quorum learner, addr: {}, closing connection",
+                    sock.getRemoteSocketAddress(), e);
+            try {
+                sock.close();
+            } catch (IOException ie) {
+                LOG.error("Exception while closing socket", ie);
+            }
+            throw new SaslException("Authentication failure: " + e.getMessage());
+        }
     }
-    
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -296,11 +313,11 @@ public class LearnerHandler extends ZooKeeperThread {
     @Override
     public void run() {
         try {
+            leader.addLearnerHandler(this);
             tickOfNextAckDeadline = leader.self.tick
                     + leader.self.initLimit + leader.self.syncLimit;
 
-            ia = BinaryInputArchive.getArchive(new BufferedInputStream(sock
-                    .getInputStream()));
+            ia = BinaryInputArchive.getArchive(bufferedInput);
             bufferedOutput = new BufferedOutputStream(sock.getOutputStream());
             oa = BinaryOutputArchive.getArchive(bufferedOutput);
 
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Observer.java b/src/java/main/org/apache/zookeeper/server/quorum/Observer.java
index e53f6f2..53f516f 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/Observer.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/Observer.java
@@ -19,11 +19,11 @@
 package org.apache.zookeeper.server.quorum;
 
 import java.io.IOException;
-import java.net.InetSocketAddress;
 
 import org.apache.jute.Record;
 import org.apache.zookeeper.server.ObserverBean;
 import org.apache.zookeeper.server.Request;
+import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
 import org.apache.zookeeper.server.util.SerializeUtils;
 import org.apache.zookeeper.txn.TxnHeader;
 
@@ -61,10 +61,10 @@ public class Observer extends Learner{
         zk.registerJMX(new ObserverBean(this, zk), self.jmxLocalPeerBean);
 
         try {
-            InetSocketAddress addr = findLeader();
-            LOG.info("Observing " + addr);
+            QuorumServer leaderServer = findLeader();
+            LOG.info("Observing " + leaderServer.addr);
             try {
-                connectToLeader(addr);
+                connectToLeader(leaderServer.addr, leaderServer.hostname);
                 long newLeaderZxid = registerWithLeader(Leader.OBSERVERINFO);
 
                 syncWithLeader(newLeaderZxid);
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java
index 20e5f16..6a5ebb4 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java
@@ -18,6 +18,7 @@
 
 package org.apache.zookeeper.server.quorum;
 
+import java.io.BufferedInputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -28,20 +29,28 @@ import java.net.SocketException;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.channels.UnresolvedAddressException;
+import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
-import java.util.NoSuchElementException;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.Date;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.zookeeper.server.ZooKeeperThread;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuthLearner;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.zookeeper.server.ZooKeeperServer;
-import org.apache.zookeeper.server.ZooKeeperThread;
-
 /**
  * This class implements a connection manager for leader election using TCP. It
  * maintains one connection for every pair of servers. The tricky part is to
@@ -89,7 +98,7 @@ public class QuorumCnxManager {
      * Negative counter for observer server ids.
      */
     
-    private long observerCounter = -1;
+    private AtomicLong observerCounter = new AtomicLong(-1);
     
     /*
      * Connection time out value in milliseconds 
@@ -100,7 +109,20 @@ public class QuorumCnxManager {
     /*
      * Local IP address
      */
-    final QuorumPeer self;
+    final long mySid;
+    final int socketTimeout;
+    final Map<Long, QuorumPeer.QuorumServer> view;
+    final boolean listenOnAllIPs;
+    private ThreadPoolExecutor connectionExecutor;
+    private final Set<Long> inprogressConnections = Collections
+            .synchronizedSet(new HashSet<Long>());
+    private QuorumAuthServer authServer;
+    private QuorumAuthLearner authLearner;
+    private boolean quorumSaslAuthEnabled;
+    /*
+     * Counter to count connection processing threads.
+     */
+    private AtomicInteger connectionThreadCnt = new AtomicInteger(0);
 
     /*
      * Mapping from Peer to Thread number
@@ -145,7 +167,14 @@ public class QuorumCnxManager {
         long sid;
     }
 
-    public QuorumCnxManager(QuorumPeer self) {
+    public QuorumCnxManager(final long mySid,
+                            Map<Long,QuorumPeer.QuorumServer> view,
+                            QuorumAuthServer authServer,
+                            QuorumAuthLearner authLearner,
+                            int socketTimeout,
+                            boolean listenOnAllIPs,
+                            int quorumCnxnThreadsSize,
+                            boolean quorumSaslAuthEnabled) {
         this.recvQueue = new ArrayBlockingQueue<Message>(RECV_CAPACITY);
         this.queueSendMap = new ConcurrentHashMap<Long, ArrayBlockingQueue<ByteBuffer>>();
         this.senderWorkerMap = new ConcurrentHashMap<Long, SendWorker>();
@@ -155,13 +184,53 @@ public class QuorumCnxManager {
         if(cnxToValue != null){
             this.cnxTO = new Integer(cnxToValue); 
         }
-        
-        this.self = self;
+
+        this.mySid = mySid;
+        this.socketTimeout = socketTimeout;
+        this.view = view;
+        this.listenOnAllIPs = listenOnAllIPs;
+
+        initializeAuth(mySid, authServer, authLearner, quorumCnxnThreadsSize,
+                quorumSaslAuthEnabled);
 
         // Starts listener thread that waits for connection requests 
         listener = new Listener();
     }
 
+    private void initializeAuth(final long mySid,
+            final QuorumAuthServer authServer,
+            final QuorumAuthLearner authLearner,
+            final int quorumCnxnThreadsSize,
+            final boolean quorumSaslAuthEnabled) {
+        this.authServer = authServer;
+        this.authLearner = authLearner;
+        this.quorumSaslAuthEnabled = quorumSaslAuthEnabled;
+        if (!this.quorumSaslAuthEnabled) {
+            LOG.debug("Not initializing connection executor as quorum sasl auth is disabled");
+            return;
+        }
+
+        // init connection executors
+        final AtomicInteger threadIndex = new AtomicInteger(1);
+        SecurityManager s = System.getSecurityManager();
+        final ThreadGroup group = (s != null) ? s.getThreadGroup()
+                : Thread.currentThread().getThreadGroup();
+        ThreadFactory daemonThFactory = new ThreadFactory() {
+
+            @Override
+            public Thread newThread(Runnable r) {
+                Thread t = new Thread(group, r, "QuorumConnectionThread-"
+                        + "[myid=" + mySid + "]-"
+                        + threadIndex.getAndIncrement());
+                return t;
+            }
+        };
+        this.connectionExecutor = new ThreadPoolExecutor(3,
+                quorumCnxnThreadsSize, 60, TimeUnit.SECONDS,
+                new SynchronousQueue<Runnable>(), daemonThFactory);
+        this.connectionExecutor.allowCoreThreadTimeOut(true);
+    }
+
     /**
      * Invokes initiateConnection for testing purposes
      * 
@@ -173,7 +242,8 @@ public class QuorumCnxManager {
         }
         Socket sock = new Socket();
         setSockOpts(sock);
-        sock.connect(self.getVotingView().get(sid).electionAddr, cnxTO);
+        sock.connect(QuorumPeer.viewToVotingView(view).get(sid).electionAddr,
+                     cnxTO);
         initiateConnection(sock, sid);
     }
     
@@ -181,28 +251,96 @@ public class QuorumCnxManager {
      * If this server has initiated the connection, then it gives up on the
      * connection if it loses challenge. Otherwise, it keeps the connection.
      */
-    public boolean initiateConnection(Socket sock, Long sid) {
+    public void initiateConnection(final Socket sock, final Long sid) {
+        try {
+            startConnection(sock, sid);
+        } catch (IOException e) {
+            LOG.error("Exception while connecting, id: {}, addr: {}, closing learner connection",
+                     new Object[] { sid, sock.getRemoteSocketAddress() }, e);
+            closeSocket(sock);
+            return;
+        }
+    }
+
+    /**
+     * Server will initiate the connection request to its peer server
+     * asynchronously via separate connection thread.
+     */
+    public void initiateConnectionAsync(final Socket sock, final Long sid) {
+        if(!inprogressConnections.add(sid)){
+            // simply return as there is a connection request to
+            // server 'sid' already in progress.
+            LOG.debug("Connection request to server id: {} is already in progress, so skipping this request",
+                    sid);
+            closeSocket(sock);
+            return;
+        }
+        try {
+            connectionExecutor.execute(
+                    new QuorumConnectionReqThread(sock, sid));
+            connectionThreadCnt.incrementAndGet();
+        } catch (Throwable e) {
+            // Imp: Safer side catching all type of exceptions and remove 'sid'
+            // from inprogress connections. This is to avoid blocking further
+            // connection requests from this 'sid' in case of errors.
+            inprogressConnections.remove(sid);
+            LOG.error("Exception while submitting quorum connection request", e);
+            closeSocket(sock);
+        }
+    }
+
+    /**
+     * Thread to send connection request to peer server.
+     */
+    private class QuorumConnectionReqThread extends ZooKeeperThread {
+        final Socket sock;
+        final Long sid;
+        QuorumConnectionReqThread(final Socket sock, final Long sid) {
+            super("QuorumConnectionReqThread-" + sid);
+            this.sock = sock;
+            this.sid = sid;
+        }
+
+        @Override
+        public void run() {
+            try{
+                initiateConnection(sock, sid);
+            } finally {
+                inprogressConnections.remove(sid);
+            }
+        }
+    }
+
+    private boolean startConnection(Socket sock, Long sid)
+            throws IOException {
         DataOutputStream dout = null;
+        DataInputStream din = null;
         try {
             // Sending id and challenge
             dout = new DataOutputStream(sock.getOutputStream());
-            dout.writeLong(self.getId());
+            dout.writeLong(this.mySid);
             dout.flush();
+
+            din = new DataInputStream(
+                    new BufferedInputStream(sock.getInputStream()));
         } catch (IOException e) {
             LOG.warn("Ignoring exception reading or writing challenge: ", e);
             closeSocket(sock);
             return false;
         }
-        
+
+        // authenticate learner
+        authLearner.authenticate(sock, view.get(sid).hostname);
+
         // If lost the challenge, then drop the new connection
-        if (sid > self.getId()) {
+        if (sid > this.mySid) {
             LOG.info("Have smaller server identifier, so dropping the " +
-                     "connection: (" + sid + ", " + self.getId() + ")");
+                     "connection: (" + sid + ", " + this.mySid + ")");
             closeSocket(sock);
             // Otherwise proceed with the connection
         } else {
             SendWorker sw = new SendWorker(sock, sid);
-            RecvWorker rw = new RecvWorker(sock, sid, sw);
+            RecvWorker rw = new RecvWorker(sock, din, sid, sw);
             sw.setRecv(rw);
 
             SendWorker vsw = senderWorkerMap.get(sid);
@@ -225,8 +363,6 @@ public class QuorumCnxManager {
         return false;
     }
 
-    
-    
     /**
      * If this server receives a connection request, then it gives up on the new
      * connection if it wins. Notice that it checks whether it has a connection
@@ -234,12 +370,57 @@ public class QuorumCnxManager {
      * possible long value to lose the challenge.
      * 
      */
-    public void receiveConnection(Socket sock) {
+    public void receiveConnection(final Socket sock) {
+        DataInputStream din = null;
+        try {
+            din = new DataInputStream(
+                    new BufferedInputStream(sock.getInputStream()));
+
+            handleConnection(sock, din);
+        } catch (IOException e) {
+            LOG.error("Exception handling connection, addr: {}, closing server connection",
+                     sock.getRemoteSocketAddress());
+            closeSocket(sock);
+        }
+    }
+
+    /**
+     * Server receives a connection request and handles it asynchronously via
+     * separate thread.
+     */
+    public void receiveConnectionAsync(final Socket sock) {
+        try {
+            connectionExecutor.execute(
+                    new QuorumConnectionReceiverThread(sock));
+            connectionThreadCnt.incrementAndGet();
+        } catch (Throwable e) {
+            LOG.error("Exception handling connection, addr: {}, closing server connection",
+                     sock.getRemoteSocketAddress());
+            closeSocket(sock);
+        }
+    }
+
+    /**
+     * Thread to receive connection request from peer server.
+     */
+    private class QuorumConnectionReceiverThread extends ZooKeeperThread {
+        private final Socket sock;
+        QuorumConnectionReceiverThread(final Socket sock) {
+            super("QuorumConnectionReceiverThread-" + sock.getRemoteSocketAddress());
+            this.sock = sock;
+        }
+
+        @Override
+        public void run() {
+            receiveConnection(sock);
+        }
+    }
+
+    private void handleConnection(Socket sock, DataInputStream din)
+            throws IOException {
         Long sid = null;
-        
         try {
             // Read server id
-            DataInputStream din = new DataInputStream(sock.getInputStream());
             sid = din.readLong();
             if (sid < 0) { // this is not a server id but a protocol version (see ZOOKEEPER-1633)
                 sid = din.readLong();
@@ -265,8 +446,7 @@ public class QuorumCnxManager {
                  * Choose identifier at random. We need a value to identify
                  * the connection.
                  */
-                
-                sid = observerCounter--;
+                sid = observerCounter.getAndDecrement();
                 LOG.info("Setting arbitrary identifier to observer: " + sid);
             }
         } catch (IOException e) {
@@ -274,9 +454,13 @@ public class QuorumCnxManager {
             LOG.warn("Exception reading or writing challenge: " + e.toString());
             return;
         }
-        
+
+        // do authenticating learner
+        LOG.debug("Authenticating learner server.id: {}", sid);
+        authServer.authenticate(sock, din);
+
         //If wins the challenge, then close the new connection.
-        if (sid < self.getId()) {
+        if (sid < this.mySid) {
             /*
              * This replica might still believe that the connection to sid is
              * up, so we have to shut down the workers before trying to open a
@@ -297,7 +481,7 @@ public class QuorumCnxManager {
             // Otherwise start worker threads to receive data.
         } else {
             SendWorker sw = new SendWorker(sock, sid);
-            RecvWorker rw = new RecvWorker(sock, sid, sw);
+            RecvWorker rw = new RecvWorker(sock, din, sid, sw);
             sw.setRecv(rw);
 
             SendWorker vsw = senderWorkerMap.get(sid);
@@ -327,7 +511,7 @@ public class QuorumCnxManager {
         /*
          * If sending message to myself, then simply enqueue it (loopback).
          */
-        if (self.getId() == sid) {
+        if (this.mySid == sid) {
              b.position(0);
              addToRecvQueue(new Message(b.duplicate(), sid));
             /*
@@ -361,28 +545,32 @@ public class QuorumCnxManager {
      * 
      *  @param sid  server id
      */
-    
-    synchronized void connectOne(long sid){
-        if (senderWorkerMap.get(sid) == null){
+    synchronized public void connectOne(long sid){
+        if (!connectedToPeer(sid)){
             InetSocketAddress electionAddr;
-            if (self.quorumPeers.containsKey(sid)) {
-                electionAddr = self.quorumPeers.get(sid).electionAddr;
+            if (view.containsKey(sid)) {
+                electionAddr = view.get(sid).electionAddr;
             } else {
                 LOG.warn("Invalid server id: " + sid);
                 return;
             }
             try {
 
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("Opening channel to server " + sid);
-                }
+                LOG.debug("Opening channel to server " + sid);
                 Socket sock = new Socket();
                 setSockOpts(sock);
-                sock.connect(self.getView().get(sid).electionAddr, cnxTO);
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("Connected to server " + sid);
+                sock.connect(view.get(sid).electionAddr, cnxTO);
+                LOG.debug("Connected to server " + sid);
+
+                // Sends connection request asynchronously if the quorum
+                // sasl authentication is enabled. This is required because
+                // sasl server authentication process may take few seconds to
+                // finish, this may delay next peer connection requests.
+                if (quorumSaslAuthEnabled) {
+                    initiateConnectionAsync(sock, sid);
+                } else {
+                    initiateConnection(sock, sid);
                 }
-                initiateConnection(sock, sid);
             } catch (UnresolvedAddressException e) {
                 // Sun doesn't include the address that causes this
                 // exception to be thrown, also UAE cannot be wrapped cleanly
@@ -392,8 +580,8 @@ public class QuorumCnxManager {
                         + " at election address " + electionAddr, e);
                 // Resolve hostname for this server in case the
                 // underlying ip address has changed.
-                if (self.getView().containsKey(sid)) {
-                    self.getView().get(sid).recreateSocketAddresses();
+                if (view.containsKey(sid)) {
+                    view.get(sid).recreateSocketAddresses();
                 }
                 throw e;
             } catch (IOException e) {
@@ -403,8 +591,8 @@ public class QuorumCnxManager {
                 // We can't really tell if the server is actually down or it failed
                 // to connect to the server because the underlying IP address
                 // changed. Resolve the hostname again just in case.
-                if (self.getView().containsKey(sid)) {
-                    self.getView().get(sid).recreateSocketAddresses();
+                if (view.containsKey(sid)) {
+                    view.get(sid).recreateSocketAddresses();
                 }
             }
         } else {
@@ -451,6 +639,13 @@ public class QuorumCnxManager {
         listener.halt();
         
         softHalt();
+
+        // clear data structures used for auth
+        if (connectionExecutor != null) {
+            connectionExecutor.shutdown();
+        }
+        inprogressConnections.clear();
+        resetConnectionThreadCount();
     }
    
     /**
@@ -471,7 +666,7 @@ public class QuorumCnxManager {
      */
     private void setSockOpts(Socket sock) throws SocketException {
         sock.setTcpNoDelay(true);
-        sock.setSoTimeout(self.tickTime * self.syncLimit);
+        sock.setSoTimeout(socketTimeout);
     }
 
     /**
@@ -494,11 +689,19 @@ public class QuorumCnxManager {
     public long getThreadCount() {
         return threadCnt.get();
     }
+
+    /**
+     * Return number of connection processing threads.
+     */
+    public long getConnectionThreadCount() {
+        return connectionThreadCnt.get();
+    }
+
     /**
-     * Return reference to QuorumPeer
+     * Reset the value of connection processing threads count to zero.
      */
-    public QuorumPeer getQuorumPeer() {
-        return self;
+    private void resetConnectionThreadCount() {
+        connectionThreadCnt.set(0);
     }
 
     /**
@@ -525,22 +728,35 @@ public class QuorumCnxManager {
                 try {
                     ss = new ServerSocket();
                     ss.setReuseAddress(true);
-                    if (self.getQuorumListenOnAllIPs()) {
-                        int port = self.quorumPeers.get(self.getId()).electionAddr.getPort();
+                    if (listenOnAllIPs) {
+                        int port = view.get(QuorumCnxManager.this.mySid)
+                            .electionAddr.getPort();
                         addr = new InetSocketAddress(port);
                     } else {
-                        addr = self.quorumPeers.get(self.getId()).electionAddr;
+                        addr = view.get(QuorumCnxManager.this.mySid)
+                            .electionAddr;
                     }
                     LOG.info("My election bind port: " + addr.toString());
-                    setName(self.quorumPeers.get(self.getId()).electionAddr
-                            .toString());
+                    setName(view.get(QuorumCnxManager.this.mySid)
+                            .electionAddr.toString());
                     ss.bind(addr);
                     while (!shutdown) {
                         Socket client = ss.accept();
                         setSockOpts(client);
                         LOG.info("Received connection request "
                                 + client.getRemoteSocketAddress());
-                        receiveConnection(client);
+
+                        // Receive and handle the connection request
+                        // asynchronously if the quorum sasl authentication is
+                        // enabled. This is required because sasl server
+                        // authentication process may take few seconds to finish,
+                        // this may delay next peer connection requests.
+                        if (quorumSaslAuthEnabled) {
+                            receiveConnectionAsync(client);
+                        } else {
+                            receiveConnection(client);
+                        }
+
                         numRetries = 0;
                     }
                 } catch (IOException e) {
@@ -562,7 +778,7 @@ public class QuorumCnxManager {
                 LOG.error("As I'm leaving the listener thread, "
                         + "I won't be able to participate in leader "
                         + "election any longer: "
-                        + self.quorumPeers.get(self.getId()).electionAddr);
+                        + view.get(QuorumCnxManager.this.mySid).electionAddr);
             }
         }
         
@@ -573,7 +789,8 @@ public class QuorumCnxManager {
             try{
                 LOG.debug("Trying to close listener: " + ss);
                 if(ss != null) {
-                    LOG.debug("Closing listener: " + self.getId());
+                    LOG.debug("Closing listener: "
+                              + QuorumCnxManager.this.mySid);
                     ss.close();
                 }
             } catch (IOException e){
@@ -729,8 +946,9 @@ public class QuorumCnxManager {
                     }
                 }
             } catch (Exception e) {
-                LOG.warn("Exception when using channel: for id " + sid + " my id = " + 
-                        self.getId() + " error = " + e);
+                LOG.warn("Exception when using channel: for id " + sid
+                         + " my id = " + QuorumCnxManager.this.mySid
+                         + " error = " + e);
             }
             this.finish();
             LOG.warn("Send worker leaving thread");
@@ -745,16 +963,16 @@ public class QuorumCnxManager {
         Long sid;
         Socket sock;
         volatile boolean running = true;
-        DataInputStream din;
+        final DataInputStream din;
         final SendWorker sw;
 
-        RecvWorker(Socket sock, Long sid, SendWorker sw) {
+        RecvWorker(Socket sock, DataInputStream din, Long sid, SendWorker sw) {
             super("RecvWorker:" + sid);
             this.sid = sid;
             this.sock = sock;
             this.sw = sw;
+            this.din = din;
             try {
-                din = new DataInputStream(sock.getInputStream());
                 // OK to wait until socket disconnects while reading.
                 sock.setSoTimeout(0);
             } catch (IOException e) {
@@ -807,8 +1025,8 @@ public class QuorumCnxManager {
                     addToRecvQueue(new Message(message.duplicate(), sid));
                 }
             } catch (Exception e) {
-                LOG.warn("Connection broken for id " + sid + ", my id = " + 
-                        self.getId() + ", error = " , e);
+                LOG.warn("Connection broken for id " + sid + ", my id = "
+                         + QuorumCnxManager.this.mySid + ", error = " , e);
             } finally {
                 LOG.warn("Interrupting SendWorker");
                 sw.finish();
@@ -930,4 +1148,8 @@ public class QuorumCnxManager {
        throws InterruptedException {
        return recvQueue.poll(timeout, unit);
     }
+
+    public boolean connectedToPeer(long peerSid) {
+        return senderWorkerMap.get(peerSid) != null;
+    }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
index 2f0f21b..9eeeb5d 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
@@ -34,8 +34,12 @@ import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+
+import javax.security.sasl.SaslException;
 
 import org.apache.zookeeper.common.AtomicFileOutputStream;
 import org.apache.zookeeper.jmx.MBeanRegistry;
@@ -45,6 +49,13 @@ import org.apache.zookeeper.server.ZKDatabase;
 import org.apache.zookeeper.server.ZooKeeperServer;
 import org.apache.zookeeper.server.ZooKeeperThread;
 import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuth;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuthLearner;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer;
+import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthLearner;
+import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthServer;
+import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthLearner;
+import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthServer;
 import org.apache.zookeeper.server.quorum.flexible.QuorumMaj;
 import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
 import org.apache.zookeeper.server.util.ZxidUtils;
@@ -85,6 +96,12 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider
     LocalPeerBean jmxLocalPeerBean;
     LeaderElectionBean jmxLeaderElectionBean;
     QuorumCnxManager qcm;
+    QuorumAuthServer authServer;
+    QuorumAuthLearner authLearner;
+    // VisibleForTesting. This flag is used to know whether qLearner's and
+    // qServer's login context has been initialized as ApacheDS has concurrency
+    // issues. Refer https://issues.apache.org/jira/browse/ZOOKEEPER-2712
+    private boolean authInitialized = false;
 
     /* ZKDatabase is a top level member of quorumpeer 
      * which will be used in all the zookeeperservers
@@ -102,7 +119,8 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider
             this.electionAddr = electionAddr;
         }
 
-        private QuorumServer(long id, InetSocketAddress addr) {
+        // VisibleForTesting
+        public QuorumServer(long id, InetSocketAddress addr) {
             this.id = id;
             this.addr = addr;
             this.electionAddr = null;
@@ -338,6 +356,56 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider
     protected boolean quorumListenOnAllIPs = false;
 
     /**
+     * Enable/Disables quorum authentication using sasl. Defaulting to false.
+     */
+    protected boolean quorumSaslEnableAuth;
+
+    /**
+     * If this is false, quorum peer server will accept another quorum peer client
+     * connection even if the authentication did not succeed. This can be used while
+     * upgrading ZooKeeper server. Defaulting to false (required).
+     */
+    protected boolean quorumServerSaslAuthRequired;
+
+    /**
+     * If this is false, quorum peer learner will talk to quorum peer server
+     * without authentication. This can be used while upgrading ZooKeeper
+     * server. Defaulting to false (required).
+     */
+    protected boolean quorumLearnerSaslAuthRequired;
+
+    /**
+     * Kerberos quorum service principal. Defaulting to 'zkquorum/localhost'.
+     */
+    protected String quorumServicePrincipal;
+
+    /**
+     * Quorum learner login context name in jaas-conf file to read the kerberos
+     * security details. Defaulting to 'QuorumLearner'.
+     */
+    protected String quorumLearnerLoginContext;
+
+    /**
+     * Quorum server login context name in jaas-conf file to read the kerberos
+     * security details. Defaulting to 'QuorumServer'.
+     */
+    protected String quorumServerLoginContext;
+
+    // TODO: need to tune the default value of thread size
+    private static final int QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE = 20;
+    /**
+     * The maximum number of threads to allow in the connectionExecutors thread
+     * pool which will be used to initiate quorum server connections.
+     */
+    protected int quorumCnxnThreadsSize = QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE;
+
+    /**
+     * Keeps time taken for leader election in milliseconds. Sets the value to
+     * this variable only after the completion of leader election.
+     */
+    private long electionTimeTaken = -1;
+
+    /**
      * @deprecated As of release 3.4.0, this class has been deprecated, since
      * it is used with one of the udp-based versions of leader election, which
      * we are also deprecating. 
@@ -449,10 +517,15 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider
     private FileTxnSnapLog logFactory = null;
 
     private final QuorumStats quorumStats;
-    
-    public QuorumPeer() {
+
+    public static QuorumPeer testingQuorumPeer() throws SaslException {
+        return new QuorumPeer();
+    }
+
+    private QuorumPeer() throws SaslException {
         super("QuorumPeer");
         quorumStats = new QuorumStats(this);
+        initialize();
     }
     
    
@@ -490,7 +563,25 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider
             this.quorumConfig = new QuorumMaj(countParticipants(quorumPeers));
         else this.quorumConfig = quorumConfig;
     }
-    
+
+    public void initialize() throws SaslException {
+        // init quorum auth server & learner
+        if (isQuorumSaslAuthEnabled()) {
+            Set<String> authzHosts = new HashSet<String>();
+            for (QuorumServer qs : getView().values()) {
+                authzHosts.add(qs.hostname);
+            }
+            authServer = new SaslQuorumAuthServer(isQuorumServerSaslAuthRequired(),
+                    quorumServerLoginContext, authzHosts);
+            authLearner = new SaslQuorumAuthLearner(isQuorumLearnerSaslAuthRequired(),
+                    quorumServicePrincipal, quorumLearnerLoginContext);
+            authInitialized = true;
+        } else {
+            authServer = new NullQuorumAuthServer();
+            authLearner = new NullQuorumAuthLearner();
+        }
+    }
+
     QuorumStats quorumStats() {
         return quorumStats;
     }
@@ -686,7 +777,7 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider
             le = new AuthFastLeaderElection(this, true);
             break;
         case 3:
-            qcm = new QuorumCnxManager(this);
+            qcm = createCnxnManager();
             QuorumCnxManager.Listener listener = qcm.listener;
             if(listener != null){
                 listener.start();
@@ -903,33 +994,37 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider
             zkDb.close();
         } catch (IOException ie) {
             LOG.warn("Error closing logs ", ie);
-        }     
+        }
     }
 
     /**
      * A 'view' is a node's current opinion of the membership of the entire
-     * ensemble.    
+     * ensemble.
      */
     public Map<Long,QuorumPeer.QuorumServer> getView() {
         return Collections.unmodifiableMap(this.quorumPeers);
     }
-    
+
     /**
      * Observers are not contained in this view, only nodes with 
-     * PeerType=PARTICIPANT.     
+     * PeerType=PARTICIPANT.
      */
     public Map<Long,QuorumPeer.QuorumServer> getVotingView() {
-        Map<Long,QuorumPeer.QuorumServer> ret = 
+        return QuorumPeer.viewToVotingView(getView());
+    }
+
+    static Map<Long,QuorumPeer.QuorumServer> viewToVotingView(
+            Map<Long,QuorumPeer.QuorumServer> view) {
+        Map<Long,QuorumPeer.QuorumServer> ret =
             new HashMap<Long, QuorumPeer.QuorumServer>();
-        Map<Long,QuorumPeer.QuorumServer> view = getView();
-        for (QuorumServer server : view.values()) {            
+        for (QuorumServer server : view.values()) {
             if (server.type == LearnerType.PARTICIPANT) {
                 ret.put(server.id, server);
             }
-        }        
+        }
         return ret;
     }
-    
+
     /**
      * Returns only observers, no followers.
      */
@@ -1306,4 +1401,96 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider
         }
     }
 
+    void setQuorumServerSaslRequired(boolean serverSaslRequired) {
+        quorumServerSaslAuthRequired = serverSaslRequired;
+        LOG.info("{} set to {}", QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED,
+                serverSaslRequired);
+    }
+
+    void setQuorumLearnerSaslRequired(boolean learnerSaslRequired) {
+        quorumLearnerSaslAuthRequired = learnerSaslRequired;
+        LOG.info("{} set to {}", QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED,
+                learnerSaslRequired);
+    }
+
+    void setQuorumSaslEnabled(boolean enableAuth) {
+        quorumSaslEnableAuth = enableAuth;
+        if (!quorumSaslEnableAuth) {
+            LOG.info("QuorumPeer communication is not secured!");
+        } else {
+            LOG.info("{} set to {}",
+                    QuorumAuth.QUORUM_SASL_AUTH_ENABLED, enableAuth);
+        }
+    }
+
+    void setQuorumServicePrincipal(String servicePrincipal) {
+        quorumServicePrincipal = servicePrincipal;
+        LOG.info("{} set to {}",QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL,
+                quorumServicePrincipal);
+    }
+
+    void setQuorumLearnerLoginContext(String learnerContext) {
+        quorumLearnerLoginContext = learnerContext;
+        LOG.info("{} set to {}", QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT,
+                quorumLearnerLoginContext);
+    }
+
+    void setQuorumServerLoginContext(String serverContext) {
+        quorumServerLoginContext = serverContext;
+        LOG.info("{} set to {}", QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT,
+                quorumServerLoginContext);
+    }
+
+    void setQuorumCnxnThreadsSize(int qCnxnThreadsSize) {
+        if (qCnxnThreadsSize > QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE) {
+            quorumCnxnThreadsSize = qCnxnThreadsSize;
+        }
+        LOG.info("quorum.cnxn.threads.size set to {}", quorumCnxnThreadsSize);
+    }
+
+    boolean isQuorumSaslAuthEnabled() {
+        return quorumSaslEnableAuth;
+    }
+
+    private boolean isQuorumServerSaslAuthRequired() {
+        return quorumServerSaslAuthRequired;
+    }
+
+    private boolean isQuorumLearnerSaslAuthRequired() {
+        return quorumLearnerSaslAuthRequired;
+    }
+
+    // VisibleForTesting. Returns true if both the quorumlearner and
+    // quorumserver login has been finished. Otherwse, false.
+    public boolean hasAuthInitialized(){
+        return authInitialized;
+    }
+
+    public QuorumCnxManager createCnxnManager() {
+        return new QuorumCnxManager(this.getId(),
+                                    this.getView(),
+                                    this.authServer,
+                                    this.authLearner,
+                                    this.tickTime * this.syncLimit,
+                                    this.getQuorumListenOnAllIPs(),
+                                    this.quorumCnxnThreadsSize,
+                                    this.isQuorumSaslAuthEnabled());
+    }
+
+    /**
+     * Sets the time taken for leader election in milliseconds.
+     *
+     * @param electionTimeTaken
+     *            time taken for leader election
+     */
+    void setElectionTimeTaken(long electionTimeTaken) {
+        this.electionTimeTaken = electionTimeTaken;
+    }
+
+    /**
+     * @return the time taken for leader election in milliseconds.
+     */
+    long getElectionTimeTaken() {
+        return electionTimeTaken;
+    }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java
index 0924ef6..621f830 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java
@@ -38,6 +38,7 @@ import org.slf4j.MDC;
 import org.apache.zookeeper.server.ZooKeeperServer;
 import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType;
 import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuth;
 import org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical;
 import org.apache.zookeeper.server.quorum.flexible.QuorumMaj;
 import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
@@ -75,7 +76,16 @@ public class QuorumPeerConfig {
     protected boolean syncEnabled = true;
 
     protected LearnerType peerType = LearnerType.PARTICIPANT;
-    
+
+    /** Configurations for the quorumpeer-to-quorumpeer sasl authentication */
+    protected boolean quorumServerRequireSasl = false;
+    protected boolean quorumLearnerRequireSasl = false;
+    protected boolean quorumEnableSasl = false;
+    protected String quorumServicePrincipal = QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE;
+    protected String quorumLearnerLoginContext = QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT_DFAULT_VALUE;
+    protected String quorumServerLoginContext = QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT_DFAULT_VALUE;
+    protected int quorumCnxnThreadsSize;
+
     /**
      * Minimum snapshot retain count.
      * @see org.apache.zookeeper.server.PurgeTxnLog#purge(File, File, int)
@@ -246,11 +256,45 @@ public class QuorumPeerConfig {
                 int dot = key.indexOf('.');
                 long sid = Long.parseLong(key.substring(dot + 1));
                 serverWeight.put(sid, Long.parseLong(value));
+            } else if (key.equals(QuorumAuth.QUORUM_SASL_AUTH_ENABLED)) {
+                quorumEnableSasl = Boolean.parseBoolean(value);
+            } else if (key.equals(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED)) {
+                quorumServerRequireSasl = Boolean.parseBoolean(value);
+            } else if (key.equals(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED)) {
+                quorumLearnerRequireSasl = Boolean.parseBoolean(value);
+            } else if (key.equals(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT)) {
+                quorumLearnerLoginContext = value;
+            } else if (key.equals(QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT)) {
+                quorumServerLoginContext = value;
+            } else if (key.equals(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL)) {
+                quorumServicePrincipal = value;
+            } else if (key.equals("quorum.cnxn.threads.size")) {
+                quorumCnxnThreadsSize = Integer.parseInt(value);
             } else {
                 System.setProperty("zookeeper." + key, value);
             }
         }
-        
+        if (!quorumEnableSasl && quorumServerRequireSasl) {
+            throw new IllegalArgumentException(
+                    QuorumAuth.QUORUM_SASL_AUTH_ENABLED
+                    + " is disabled, so cannot enable "
+                    + QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED);
+        }
+        if (!quorumEnableSasl && quorumLearnerRequireSasl) {
+            throw new IllegalArgumentException(
+                    QuorumAuth.QUORUM_SASL_AUTH_ENABLED
+                    + " is disabled, so cannot enable "
+                    + QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED);
+        }
+        // If quorumpeer learner is not auth enabled then self won't be able to
+        // join quorum. So this condition is ensuring that the quorumpeer learner
+        // is also auth enabled while enabling quorum server require sasl.
+        if (!quorumLearnerRequireSasl && quorumServerRequireSasl) {
+            throw new IllegalArgumentException(
+                    QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED
+                    + " is disabled, so cannot enable "
+                    + QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED);
+        }
         // Reset to MIN_SNAP_RETAIN_COUNT if invalid (less than 3)
         // PurgeTxnLog.purge(File, File, int) will not allow to purge less
         // than 3.
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java
index e9c8007..4ea7e54 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java
@@ -130,26 +130,37 @@ public class QuorumPeerMain {
           cnxnFactory.configure(config.getClientPortAddress(),
                                 config.getMaxClientCnxns());
   
-          quorumPeer = new QuorumPeer();
+          quorumPeer = new QuorumPeer(config.getServers(),
+                                      new File(config.getDataDir()),
+                                      new File(config.getDataLogDir()),
+                                      config.getElectionAlg(),
+                                      config.getServerId(),
+                                      config.getTickTime(),
+                                      config.getInitLimit(),
+                                      config.getSyncLimit(),
+                                      config.getQuorumListenOnAllIPs(),
+                                      cnxnFactory,
+                                      config.getQuorumVerifier());
           quorumPeer.setClientPortAddress(config.getClientPortAddress());
-          quorumPeer.setTxnFactory(new FileTxnSnapLog(
-                      new File(config.getDataLogDir()),
-                      new File(config.getDataDir())));
-          quorumPeer.setQuorumPeers(config.getServers());
-          quorumPeer.setElectionType(config.getElectionAlg());
-          quorumPeer.setMyid(config.getServerId());
-          quorumPeer.setTickTime(config.getTickTime());
           quorumPeer.setMinSessionTimeout(config.getMinSessionTimeout());
           quorumPeer.setMaxSessionTimeout(config.getMaxSessionTimeout());
-          quorumPeer.setInitLimit(config.getInitLimit());
-          quorumPeer.setSyncLimit(config.getSyncLimit());
-          quorumPeer.setQuorumVerifier(config.getQuorumVerifier());
-          quorumPeer.setCnxnFactory(cnxnFactory);
           quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory()));
           quorumPeer.setLearnerType(config.getPeerType());
           quorumPeer.setSyncEnabled(config.getSyncEnabled());
-          quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs());
-  
+
+          // sets quorum sasl authentication configurations
+          quorumPeer.setQuorumSaslEnabled(config.quorumEnableSasl);
+          if(quorumPeer.isQuorumSaslAuthEnabled()){
+              quorumPeer.setQuorumServerSaslRequired(config.quorumServerRequireSasl);
+              quorumPeer.setQuorumLearnerSaslRequired(config.quorumLearnerRequireSasl);
+              quorumPeer.setQuorumServicePrincipal(config.quorumServicePrincipal);
+              quorumPeer.setQuorumServerLoginContext(config.quorumServerLoginContext);
+              quorumPeer.setQuorumLearnerLoginContext(config.quorumLearnerLoginContext);
+          }
+
+          quorumPeer.setQuorumCnxnThreadsSize(config.quorumCnxnThreadsSize);
+          quorumPeer.initialize();
+
           quorumPeer.start();
           quorumPeer.join();
       } catch (InterruptedException e) {
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthLearner.java
similarity index 68%
copy from src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java
copy to src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthLearner.java
index bf08104..0af891c 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthLearner.java
@@ -16,21 +16,18 @@
  * limitations under the License.
  */
 
-package org.apache.zookeeper.server.quorum;
+package org.apache.zookeeper.server.quorum.auth;
 
-import org.apache.zookeeper.server.ZooKeeperServerMXBean;
+import java.net.Socket;
 
 /**
- * Leader MBean.
+ * This class represents no authentication learner, it just return
+ * without performing any authentication.
  */
-public interface LeaderMXBean extends ZooKeeperServerMXBean {
-    /**
-     * Current zxid of cluster.
-     */
-    public String getCurrentZxid();
+public class NullQuorumAuthLearner implements QuorumAuthLearner {
 
-    /**
-     * @return information on current followers
-     */
-    public String followerInfo();
+    @Override
+    public void authenticate(Socket sock, String hostname) {
+        return; // simply return don't require auth
+    }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthServer.java
similarity index 65%
copy from src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java
copy to src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthServer.java
index bf08104..b26a54a 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthServer.java
@@ -16,21 +16,19 @@
  * limitations under the License.
  */
 
-package org.apache.zookeeper.server.quorum;
+package org.apache.zookeeper.server.quorum.auth;
 
-import org.apache.zookeeper.server.ZooKeeperServerMXBean;
+import java.io.DataInputStream;
+import java.net.Socket;
 
 /**
- * Leader MBean.
+ * This class represents no authentication server, it just return
+ * without performing any authentication.
  */
-public interface LeaderMXBean extends ZooKeeperServerMXBean {
-    /**
-     * Current zxid of cluster.
-     */
-    public String getCurrentZxid();
+public class NullQuorumAuthServer implements QuorumAuthServer {
 
-    /**
-     * @return information on current followers
-     */
-    public String followerInfo();
+    @Override
+    public void authenticate(final Socket sock, final DataInputStream din) {
+        return; // simply return don't require auth
+    }
 }
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuth.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuth.java
new file mode 100644
index 0000000..8bfa394
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuth.java
@@ -0,0 +1,96 @@
+/**
+ * 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.quorum.auth;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import org.apache.jute.BinaryInputArchive;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.zookeeper.server.quorum.QuorumAuthPacket;
+
+public class QuorumAuth {
+    private static final Logger LOG = LoggerFactory.getLogger(QuorumAuth.class);
+
+    public static final String QUORUM_SASL_AUTH_ENABLED = "quorum.auth.enableSasl";
+    public static final String QUORUM_SERVER_SASL_AUTH_REQUIRED = "quorum.auth.serverRequireSasl";
+    public static final String QUORUM_LEARNER_SASL_AUTH_REQUIRED = "quorum.auth.learnerRequireSasl";
+
+    public static final String QUORUM_KERBEROS_SERVICE_PRINCIPAL = "quorum.auth.kerberos.servicePrincipal";
+    public static final String QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE = "zkquorum/localhost";
+
+    public static final String QUORUM_LEARNER_SASL_LOGIN_CONTEXT = "quorum.auth.learner.saslLoginContext";
+    public static final String QUORUM_LEARNER_SASL_LOGIN_CONTEXT_DFAULT_VALUE = "QuorumLearner";
+
+    public static final String QUORUM_SERVER_SASL_LOGIN_CONTEXT = "quorum.auth.server.saslLoginContext";
+    public static final String QUORUM_SERVER_SASL_LOGIN_CONTEXT_DFAULT_VALUE = "QuorumServer";
+
+    static final String QUORUM_SERVER_PROTOCOL_NAME = "zookeeper-quorum";
+    static final String QUORUM_SERVER_SASL_DIGEST = "zk-quorum-sasl-md5";
+    static final String QUORUM_AUTH_MESSAGE_TAG = "qpconnect";
+
+    // this is negative, so that if a learner that does auth, connects to a
+    // server, it'll think the received packet is an authentication packet
+    public static final long QUORUM_AUTH_MAGIC_NUMBER = -0xa0dbcafecafe1234L;
+
+    public enum Status {
+         IN_PROGRESS(0), SUCCESS(1), ERROR(-1);
+        private int status;
+
+        Status(int status) {
+            this.status = status;
+        }
+
+        static Status getStatus(int status) {
+            switch (status) {
+            case 0:
+                return IN_PROGRESS;
+            case 1:
+                return SUCCESS;
+            case -1:
+                return ERROR;
+            default:
+                LOG.error("Unknown status:{}!", status);
+                assert false : "Unknown status!";
+                return ERROR;
+            }
+        }
+
+        int status() {
+            return status;
+        }
+    }
+
+    public static QuorumAuthPacket createPacket(Status status, byte[] response) {
+        return new QuorumAuthPacket(QUORUM_AUTH_MAGIC_NUMBER,
+                                    status.status(), response);
+    }
+
+    public static boolean nextPacketIsAuth(DataInputStream din)
+            throws IOException {
+        din.mark(32);
+        BinaryInputArchive bia = new BinaryInputArchive(din);
+        boolean firstIsAuth = (bia.readLong("NO_TAG")
+                               == QuorumAuth.QUORUM_AUTH_MAGIC_NUMBER);
+        din.reset();
+        return firstIsAuth;
+    }
+}
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthLearner.java
similarity index 58%
copy from src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java
copy to src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthLearner.java
index bf08104..af71257 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthLearner.java
@@ -16,21 +16,25 @@
  * limitations under the License.
  */
 
-package org.apache.zookeeper.server.quorum;
+package org.apache.zookeeper.server.quorum.auth;
 
-import org.apache.zookeeper.server.ZooKeeperServerMXBean;
+import java.io.IOException;
+import java.net.Socket;
 
 /**
- * Leader MBean.
+ * Interface for quorum learner authentication mechanisms.
  */
-public interface LeaderMXBean extends ZooKeeperServerMXBean {
-    /**
-     * Current zxid of cluster.
-     */
-    public String getCurrentZxid();
+public interface QuorumAuthLearner {
 
     /**
-     * @return information on current followers
+     * Performs an authentication step for the given socket connection.
+     *
+     * @param sock
+     *            socket connection to other quorum peer server
+     * @param hostname
+     *            host name of other quorum peer server
+     * @throws IOException
+     *             if there is an authentication failure
      */
-    public String followerInfo();
+    public void authenticate(Socket sock, String hostname) throws IOException;
 }
diff --git a/src/c/include/zookeeper_version.h b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthServer.java
similarity index 56%
copy from src/c/include/zookeeper_version.h
copy to src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthServer.java
index 57790c4..e9de8f0 100644
--- a/src/c/include/zookeeper_version.h
+++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthServer.java
@@ -15,19 +15,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef ZOOKEEPER_VERSION_H_
-#define ZOOKEEPER_VERSION_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+package org.apache.zookeeper.server.quorum.auth;
 
-#define ZOO_MAJOR_VERSION 3
-#define ZOO_MINOR_VERSION 4
-#define ZOO_PATCH_VERSION 9
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.net.Socket;
 
-#ifdef __cplusplus
-}
-#endif
+/**
+ * Interface for quorum server authentication mechanisms.
+ */
+public interface QuorumAuthServer {
 
-#endif /* ZOOKEEPER_VERSION_H_ */
+    /**
+     * Performs an authentication step for the given socket connection.
+     *
+     * @param sock
+     *            socket connection to other quorum peer
+     * @param din
+     *            stream used to read auth data send by the quorum learner
+     * @throws IOException if the server fails to authenticate connecting quorum learner
+     */
+    public void authenticate(Socket sock, DataInputStream din)
+            throws IOException;
+}
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java
new file mode 100644
index 0000000..2643808
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java
@@ -0,0 +1,233 @@
+/**
+ * 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.quorum.auth;
+
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginException;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+import org.apache.jute.BinaryInputArchive;
+import org.apache.jute.BinaryOutputArchive;
+import org.apache.zookeeper.Login;
+import org.apache.zookeeper.SaslClientCallbackHandler;
+import org.apache.zookeeper.server.quorum.QuorumAuthPacket;
+import org.apache.zookeeper.util.SecurityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SaslQuorumAuthLearner implements QuorumAuthLearner {
+    private static final Logger LOG = LoggerFactory
+            .getLogger(SaslQuorumAuthLearner.class);
+
+    private final Login learnerLogin;
+    private final boolean quorumRequireSasl;
+    private final String quorumServicePrincipal;
+
+    public SaslQuorumAuthLearner(boolean quorumRequireSasl,
+            String quorumServicePrincipal, String loginContext)
+                    throws SaslException {
+        this.quorumRequireSasl = quorumRequireSasl;
+        this.quorumServicePrincipal = quorumServicePrincipal;
+        try {
+            AppConfigurationEntry entries[] = Configuration
+                .getConfiguration()
+                .getAppConfigurationEntry(loginContext);
+            if (entries == null || entries.length == 0) {
+                throw new LoginException("SASL-authentication failed because"
+                                         + " the specified JAAS configuration "
+                                         + "section '" + loginContext
+                                         + "' could not be found.");
+            }
+            this.learnerLogin = new Login(loginContext,
+                                    new SaslClientCallbackHandler(null, "QuorumLearner"));
+            this.learnerLogin.startThreadIfNeeded();
+        } catch (LoginException e) {
+            throw new SaslException("Failed to initialize authentication mechanism using SASL", e);
+        }
+    }
+
+    @Override
+    public void authenticate(Socket sock, String hostName) throws IOException {
+        if (!quorumRequireSasl) { // let it through, we don't require auth
+            LOG.info("Skipping SASL authentication as {}={}",
+                    QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED,
+                    quorumRequireSasl);
+            return;
+        }
+        SaslClient sc = null;
+        String principalConfig = SecurityUtils
+                .getServerPrincipal(quorumServicePrincipal, hostName);
+        try {
+            DataOutputStream dout = new DataOutputStream(
+                    sock.getOutputStream());
+            DataInputStream din = new DataInputStream(sock.getInputStream());
+            byte[] responseToken = new byte[0];
+            sc = SecurityUtils.createSaslClient(learnerLogin.getSubject(),
+                    principalConfig,
+                    QuorumAuth.QUORUM_SERVER_PROTOCOL_NAME,
+                    QuorumAuth.QUORUM_SERVER_SASL_DIGEST, LOG, "QuorumLearner");
+
+            if (sc.hasInitialResponse()) {
+                responseToken = createSaslToken(new byte[0], sc, learnerLogin);
+            }
+            send(dout, responseToken);
+            QuorumAuthPacket authPacket = receive(din);
+            QuorumAuth.Status qpStatus = QuorumAuth.Status
+                    .getStatus(authPacket.getStatus());
+            while (!sc.isComplete()) {
+                switch (qpStatus) {
+                case SUCCESS:
+                    responseToken = createSaslToken(authPacket.getToken(), sc,
+                            learnerLogin);
+                    // we're done; don't expect to send another BIND
+                    if (responseToken != null) {
+                        throw new SaslException(
+                                "Protocol error: attempting to send response after completion"
+                                        + ". Server addr: "
+                                        + sock.getRemoteSocketAddress());
+                    }
+                    break;
+                case IN_PROGRESS:
+                    responseToken = createSaslToken(authPacket.getToken(), sc,
+                            learnerLogin);
+                    send(dout, responseToken);
+                    authPacket = receive(din);
+                    qpStatus = QuorumAuth.Status
+                            .getStatus(authPacket.getStatus());
+                    break;
+                case ERROR:
+                    throw new SaslException(
+                            "Authentication failed against server addr: "
+                                    + sock.getRemoteSocketAddress());
+                default:
+                    LOG.warn("Unknown status:{}!", qpStatus);
+                    throw new SaslException(
+                            "Authentication failed against server addr: "
+                                    + sock.getRemoteSocketAddress());
+                }
+            }
+
+            // Validate status code at the end of authentication exchange.
+            checkAuthStatus(sock, qpStatus);
+        } finally {
+            if (sc != null) {
+                try {
+                    sc.dispose();
+                } catch (SaslException e) {
+                    LOG.error("SaslClient dispose() failed", e);
+                }
+            }
+        }
+        return;
+    }
+
+    private void checkAuthStatus(Socket sock, QuorumAuth.Status qpStatus)
+            throws SaslException {
+        if (qpStatus == QuorumAuth.Status.SUCCESS) {
+            LOG.info("Successfully completed the authentication using SASL. server addr: {}, status: {}",
+                    sock.getRemoteSocketAddress(), qpStatus);
+        } else {
+            throw new SaslException("Authentication failed against server addr: "
+                            + sock.getRemoteSocketAddress() + ", qpStatus: "
+                            + qpStatus);
+        }
+    }
+
+    private QuorumAuthPacket receive(DataInputStream din) throws IOException {
+        QuorumAuthPacket authPacket = new QuorumAuthPacket();
+        BinaryInputArchive bia = BinaryInputArchive.getArchive(din);
+        authPacket.deserialize(bia, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG);
+        return authPacket;
+    }
+
+    private void send(DataOutputStream dout, byte[] response)
+            throws IOException {
+        QuorumAuthPacket authPacket;
+        BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout);
+        BinaryOutputArchive boa = BinaryOutputArchive
+                .getArchive(bufferedOutput);
+        if (response != null && response.length < 0) {
+            throw new IOException("Response length < 0");
+        } else if (response == null) {
+            authPacket = QuorumAuth.createPacket(
+                    QuorumAuth.Status.IN_PROGRESS, response);
+        } else {
+            authPacket = QuorumAuth.createPacket(
+                    QuorumAuth.Status.IN_PROGRESS, response);
+        }
+
+        boa.writeRecord(authPacket, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG);
+        bufferedOutput.flush();
+    }
+
+    // TODO: need to consolidate the #createSaslToken() implementation between ZooKeeperSaslClient#createSaslToken().
+    private byte[] createSaslToken(final byte[] saslToken,
+            final SaslClient saslClient, final Login login)
+                    throws SaslException {
+        if (saslToken == null) {
+            throw new SaslException(
+                    "Error in authenticating with a Zookeeper Quorum member: the quorum member's saslToken is null.");
+        }
+        if (login.getSubject() != null) {
+            synchronized (login) {
+                try {
+                    final byte[] retval = Subject.doAs(login.getSubject(),
+                            new PrivilegedExceptionAction<byte[]>() {
+                                public byte[] run() throws SaslException {
+                                    LOG.debug("saslClient.evaluateChallenge(len="
+                                                    + saslToken.length + ")");
+                                    return saslClient.evaluateChallenge(saslToken);
+                                }
+                            });
+                    return retval;
+                } catch (PrivilegedActionException e) {
+                    String error = "An error: (" + e
+                            + ") occurred when evaluating Zookeeper Quorum Member's "
+                            + " received SASL token.";
+                    // Try to provide hints to use about what went wrong so they
+                    // can fix their configuration.
+                    // TODO: introspect about e: look for GSS information.
+                    final String UNKNOWN_SERVER_ERROR_TEXT = "(Mechanism level: Server not found in Kerberos database (7) - UNKNOWN_SERVER)";
+                    if (e.toString().indexOf(UNKNOWN_SERVER_ERROR_TEXT) > -1) {
+                        error += " This may be caused by Java's being unable to resolve the Zookeeper Quorum Member's"
+                                + " hostname correctly. You may want to try to adding"
+                                + " '-Dsun.net.spi.nameservice.provider.1=dns,sun' to your server's JVMFLAGS environment.";
+                    }
+                    LOG.error(error);
+                    throw new SaslException(error);
+                }
+            }
+        } else {
+            throw new SaslException(
+                    "Cannot make SASL token without subject defined. "
+                            + "For diagnosis, please look for WARNs and ERRORs in your log related to the Login class.");
+        }
+    }
+}
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java
new file mode 100644
index 0000000..0a67b79
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java
@@ -0,0 +1,182 @@
+/**
+ * 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.quorum.auth;
+
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+import java.util.Set;
+
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginException;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.jute.BinaryInputArchive;
+import org.apache.jute.BinaryOutputArchive;
+import org.apache.zookeeper.Login;
+import org.apache.zookeeper.server.quorum.QuorumAuthPacket;
+import org.apache.zookeeper.util.SecurityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SaslQuorumAuthServer implements QuorumAuthServer {
+
+    private static final Logger LOG = LoggerFactory
+            .getLogger(SaslQuorumAuthServer.class);
+
+    private final static int MAX_RETRIES = 5;
+    private final Login serverLogin;
+    private final boolean quorumRequireSasl;
+
+    public SaslQuorumAuthServer(boolean quorumRequireSasl, String loginContext, Set<String> authzHosts)
+            throws SaslException {
+        this.quorumRequireSasl = quorumRequireSasl;
+        try {
+            AppConfigurationEntry entries[] = Configuration.getConfiguration()
+                    .getAppConfigurationEntry(loginContext);
+            if (entries == null || entries.length == 0) {
+                throw new LoginException("SASL-authentication failed"
+                        + " because the specified JAAS configuration "
+                        + "section '" + loginContext + "' could not be found.");
+            }
+            SaslQuorumServerCallbackHandler saslServerCallbackHandler = new SaslQuorumServerCallbackHandler(
+                    Configuration.getConfiguration(), loginContext, authzHosts);
+            serverLogin = new Login(loginContext, saslServerCallbackHandler);
+            serverLogin.startThreadIfNeeded();
+        } catch (Throwable e) {
+            throw new SaslException(
+                    "Failed to initialize authentication mechanism using SASL",
+                    e);
+        }
+    }
+
+    @Override
+    public void authenticate(Socket sock, DataInputStream din)
+            throws SaslException {
+        DataOutputStream dout = null;
+        SaslServer ss = null;
+        try {
+            if (!QuorumAuth.nextPacketIsAuth(din)) {
+                if (quorumRequireSasl) {
+                    throw new SaslException(
+                            "Learner " + sock.getRemoteSocketAddress()
+                                    + " not trying to authenticate"
+                                    + " and authentication is required");
+                } else {
+                    // let it through, we don't require auth
+                    return;
+                }
+            }
+
+            byte[] token = receive(din);
+            int tries = 0;
+            dout = new DataOutputStream(sock.getOutputStream());
+            byte[] challenge = null;
+            ss = SecurityUtils.createSaslServer(serverLogin.getSubject(),
+                    QuorumAuth.QUORUM_SERVER_PROTOCOL_NAME,
+                    QuorumAuth.QUORUM_SERVER_SASL_DIGEST, serverLogin.callbackHandler,
+                    LOG);
+            while (!ss.isComplete()) {
+                challenge = ss.evaluateResponse(token);
+                if (!ss.isComplete()) {
+                    // limited number of retries.
+                    if (++tries > MAX_RETRIES) {
+                        send(dout, challenge, QuorumAuth.Status.ERROR);
+                        LOG.warn("Failed to authenticate using SASL, server addr: {}, retries={} exceeded.",
+                                sock.getRemoteSocketAddress(), tries);
+                        break;
+                    }
+                    send(dout, challenge, QuorumAuth.Status.IN_PROGRESS);
+                    token = receive(din);
+                }
+            }
+            // Authentication exchange has completed
+            if (ss.isComplete()) {
+                send(dout, challenge, QuorumAuth.Status.SUCCESS);
+                LOG.info("Successfully completed the authentication using SASL. learner addr: {}",
+                        sock.getRemoteSocketAddress());
+            }
+        } catch (Exception e) {
+            try {
+                if (dout != null) {
+                    // send error message to the learner
+                    send(dout, new byte[0], QuorumAuth.Status.ERROR);
+                }
+            } catch (IOException ioe) {
+                LOG.warn("Exception while sending failed status", ioe);
+            }
+            // If sasl is not required, when a server initializes a
+            // connection it will try to log in, but it will also
+            // accept connections that do not start with a sasl
+            // handshake.
+            if (quorumRequireSasl) {
+                LOG.error("Failed to authenticate using SASL", e);
+                throw new SaslException(
+                        "Failed to authenticate using SASL: " + e.getMessage());
+            } else {
+                LOG.warn("Failed to authenticate using SASL", e);
+                LOG.warn("Maintaining learner connection despite SASL authentication failure."
+                                + " server addr: {}, {}: {}",
+                        new Object[] { sock.getRemoteSocketAddress(),
+                                QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED,
+                                quorumRequireSasl });
+                return; // let it through, we don't require auth
+            }
+        } finally {
+            if (ss != null) {
+                try {
+                    ss.dispose();
+                } catch (SaslException e) {
+                    LOG.error("SaslServer dispose() failed", e);
+                }
+            }
+        }
+        return;
+    }
+
+    private byte[] receive(DataInputStream din) throws IOException {
+        QuorumAuthPacket authPacket = new QuorumAuthPacket();
+        BinaryInputArchive bia = BinaryInputArchive.getArchive(din);
+        authPacket.deserialize(bia, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG);
+        return authPacket.getToken();
+    }
+
+    private void send(DataOutputStream dout, byte[] challenge,
+            QuorumAuth.Status s) throws IOException {
+        BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout);
+        BinaryOutputArchive boa = BinaryOutputArchive
+                .getArchive(bufferedOutput);
+        QuorumAuthPacket authPacket;
+        if (challenge != null && challenge.length < 0) {
+            throw new IOException("Response length < 0");
+        } else if (challenge == null && s != QuorumAuth.Status.SUCCESS) {
+            authPacket = QuorumAuth.createPacket(
+                    QuorumAuth.Status.IN_PROGRESS, challenge);
+        } else {
+            authPacket = QuorumAuth.createPacket(s, challenge);
+        }
+
+        boa.writeRecord(authPacket, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG);
+        bufferedOutput.flush();
+    }
+}
diff --git a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumServerCallbackHandler.java
similarity index 56%
copy from src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java
copy to src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumServerCallbackHandler.java
index 2fbd6ed..3e71bb1 100644
--- a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumServerCallbackHandler.java
@@ -15,15 +15,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.zookeeper.server.auth;
+package org.apache.zookeeper.server.quorum.auth;
 
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.callback.NameCallback;
@@ -34,32 +32,36 @@ import javax.security.auth.login.Configuration;
 import javax.security.sasl.AuthorizeCallback;
 import javax.security.sasl.RealmCallback;
 
-import org.apache.zookeeper.server.ZooKeeperSaslServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public class SaslServerCallbackHandler implements CallbackHandler {
+/**
+ * This is used by the SASL mechanisms to get further information to complete
+ * the authentication. For example, a SASL mechanism might use this callback
+ * handler to do verification operation. This is used by the QuorumServer to
+ * perform the mutual quorum peer authentication.
+ */
+public class SaslQuorumServerCallbackHandler implements CallbackHandler {
     private static final String USER_PREFIX = "user_";
-    private static final Logger LOG = LoggerFactory.getLogger(SaslServerCallbackHandler.class);
-    private static final String SYSPROP_SUPER_PASSWORD = "zookeeper.SASLAuthenticationProvider.superPassword";
-    private static final String SYSPROP_REMOVE_HOST = "zookeeper.kerberos.removeHostFromPrincipal";
-    private static final String SYSPROP_REMOVE_REALM = "zookeeper.kerberos.removeRealmFromPrincipal";
+    private static final Logger LOG = LoggerFactory.getLogger(SaslQuorumServerCallbackHandler.class);
 
     private String userName;
     private final Map<String,String> credentials = new HashMap<String,String>();
+    private final Set<String> authzHosts;
 
-    public SaslServerCallbackHandler(Configuration configuration) throws IOException {
-        String serverSection = System.getProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY,
-                                                  ZooKeeperSaslServer.DEFAULT_LOGIN_CONTEXT_NAME);
+    public SaslQuorumServerCallbackHandler(Configuration configuration,
+            String serverSection, Set<String> authzHosts) throws IOException {
         AppConfigurationEntry configurationEntries[] = configuration.getAppConfigurationEntry(serverSection);
 
         if (configurationEntries == null) {
-            String errorMessage = "Could not find a 'Server' entry in this configuration: Server cannot start.";
+            String errorMessage = "Could not find a '" + serverSection + "' entry in this configuration: Server cannot start.";
             LOG.error(errorMessage);
             throw new IOException(errorMessage);
         }
         credentials.clear();
         for(AppConfigurationEntry entry: configurationEntries) {
             Map<String,?> options = entry.getOptions();
-            // Populate DIGEST-MD5 user -> password map with JAAS configuration entries from the "Server" section.
+            // Populate DIGEST-MD5 user -> password map with JAAS configuration entries from the "QuorumServer" section.
             // Usernames are distinguished from other options by prefixing the username with a "user_" prefix.
             for(Map.Entry<String, ?> pair : options.entrySet()) {
                 String key = pair.getKey();
@@ -69,6 +71,9 @@ public class SaslServerCallbackHandler implements CallbackHandler {
                 }
             }
         }
+
+        // authorized host lists
+        this.authzHosts = authzHosts;
     }
 
     public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
@@ -88,7 +93,8 @@ public class SaslServerCallbackHandler implements CallbackHandler {
     private void handleNameCallback(NameCallback nc) {
         // check to see if this user is in the user password database.
         if (credentials.get(nc.getDefaultName()) == null) {
-            LOG.warn("User '" + nc.getDefaultName() + "' not found in list of DIGEST-MD5 authenticateable users.");
+            LOG.warn("User '{}' not found in list of DIGEST-MD5 authenticateable users.",
+                    nc.getDefaultName());
             return;
         }
         nc.setName(nc.getDefaultName());
@@ -96,18 +102,15 @@ public class SaslServerCallbackHandler implements CallbackHandler {
     }
 
     private void handlePasswordCallback(PasswordCallback pc) {
-        if ("super".equals(this.userName) && System.getProperty(SYSPROP_SUPER_PASSWORD) != null) {
-            // superuser: use Java system property for password, if available.
-            pc.setPassword(System.getProperty(SYSPROP_SUPER_PASSWORD).toCharArray());
-        } else if (credentials.containsKey(userName) ) {
+        if (credentials.containsKey(userName) ) {
             pc.setPassword(credentials.get(userName).toCharArray());
         } else {
-            LOG.warn("No password found for user: " + userName);
+            LOG.warn("No password found for user: {}", userName);
         }
     }
 
     private void handleRealmCallback(RealmCallback rc) {
-        LOG.debug("client supplied realm: " + rc.getDefaultText());
+        LOG.debug("QuorumLearner supplied realm: {}", rc.getDefaultText());
         rc.setText(rc.getDefaultText());
     }
 
@@ -115,38 +118,31 @@ public class SaslServerCallbackHandler implements CallbackHandler {
         String authenticationID = ac.getAuthenticationID();
         String authorizationID = ac.getAuthorizationID();
 
-        LOG.info("Successfully authenticated client: authenticationID=" + authenticationID
-                + ";  authorizationID=" + authorizationID + ".");
-        ac.setAuthorized(true);
-
-        // canonicalize authorization id according to system properties:
-        // zookeeper.kerberos.removeRealmFromPrincipal(={true,false})
-        // zookeeper.kerberos.removeHostFromPrincipal(={true,false})
-        KerberosName kerberosName = new KerberosName(authenticationID);
-        try {
-            StringBuilder userNameBuilder = new StringBuilder(kerberosName.getShortName());
-            if (shouldAppendHost(kerberosName)) {
-                userNameBuilder.append("/").append(kerberosName.getHostName());
+        boolean authzFlag = false;
+        // 1. Matches authenticationID and authorizationID
+        authzFlag = authenticationID.equals(authorizationID);
+
+        // 2. Verify whether the connecting host is present in authorized hosts.
+        // If not exists, then connecting peer is not authorized to join the
+        // ensemble and will reject it.
+        if (authzFlag) {
+            String[] components = authorizationID.split("[/@]");
+            if (components.length == 3) {
+                authzFlag = authzHosts.contains(components[1]);
             }
-            if (shouldAppendRealm(kerberosName)) {
-                userNameBuilder.append("@").append(kerberosName.getRealm());
+            if (!authzFlag) {
+                LOG.error("SASL authorization completed, {} is not authorized to connect",
+                        components[1]);
             }
-            LOG.info("Setting authorizedID: " + userNameBuilder);
-            ac.setAuthorizedID(userNameBuilder.toString());
-        } catch (IOException e) {
-            LOG.error("Failed to set name based on Kerberos authentication rules.");
         }
-    }
-
-    private boolean shouldAppendRealm(KerberosName kerberosName) {
-        return !isSystemPropertyTrue(SYSPROP_REMOVE_REALM) && kerberosName.getRealm() != null;
-    }
 
-    private boolean shouldAppendHost(KerberosName kerberosName) {
-        return !isSystemPropertyTrue(SYSPROP_REMOVE_HOST) && kerberosName.getHostName() != null;
-    }
-
-    private boolean isSystemPropertyTrue(String propertyName) {
-        return "true".equals(System.getProperty(propertyName));
+        // Sets authorization flag
+        ac.setAuthorized(authzFlag);
+        if (ac.isAuthorized()) {
+            ac.setAuthorizedID(authorizationID);
+            LOG.info("Successfully authenticated learner: authenticationID={};  authorizationID={}.",
+                    authenticationID, authorizationID);
+        }
+        LOG.debug("SASL authorization completed, authorized flag set to {}", ac.isAuthorized());
     }
 }
diff --git a/src/java/main/org/apache/zookeeper/util/SecurityUtils.java b/src/java/main/org/apache/zookeeper/util/SecurityUtils.java
new file mode 100644
index 0000000..67484e4
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/util/SecurityUtils.java
@@ -0,0 +1,298 @@
+/**
+ * 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.util;
+
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.zookeeper.SaslClientCallbackHandler;
+import org.apache.zookeeper.server.auth.KerberosName;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.slf4j.Logger;
+
+public final class SecurityUtils {
+
+    public static final String QUORUM_HOSTNAME_PATTERN = "_HOST";
+
+    /**
+     * Create an instance of a SaslClient. It will return null if there is an exception.
+     *
+     * @param subject subject
+     * @param servicePrincipal principal
+     * @param protocol name of the protocol for which the authentication is being performed
+     * @param serverName name of the server to authenticate to
+     * @param LOG logger
+     * @param entity can be either zookeeper client or quorum learner
+     *
+     * @return saslclient object
+     * @throws SaslException
+     */
+    public static SaslClient createSaslClient(final Subject subject,
+            final String servicePrincipal, final String protocol,
+            final String serverName, final Logger LOG, final String entity) throws SaslException {
+        SaslClient saslClient;
+        // Use subject.getPrincipals().isEmpty() as an indication of which SASL
+        // mechanism to use: if empty, use DIGEST-MD5; otherwise, use GSSAPI.
+        if (subject.getPrincipals().isEmpty()) {
+            // no principals: must not be GSSAPI: use DIGEST-MD5 mechanism
+            // instead.
+            LOG.info("{} will use DIGEST-MD5 as SASL mechanism.", entity);
+            String[] mechs = { "DIGEST-MD5" };
+            String username = (String) (subject.getPublicCredentials()
+                    .toArray()[0]);
+            String password = (String) (subject.getPrivateCredentials()
+                    .toArray()[0]);
+            // 'domain' parameter is hard-wired between the server and client
+            saslClient = Sasl.createSaslClient(mechs, username, protocol,
+                    serverName, null, new SaslClientCallbackHandler(password, entity));
+            return saslClient;
+        } else { // GSSAPI.
+            final Object[] principals = subject.getPrincipals().toArray();
+            // determine client principal from subject.
+            final Principal clientPrincipal = (Principal) principals[0];
+            boolean usingNativeJgss = Boolean
+                    .getBoolean("sun.security.jgss.native");
+            if (usingNativeJgss) {
+                // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
+                // """
+                // In addition, when performing operations as a particular
+                // Subject, e.g. Subject.doAs(...) or
+                // Subject.doAsPrivileged(...),
+                // the to-be-used GSSCredential should be added to Subject's
+                // private credential set. Otherwise, the GSS operations will
+                // fail since no credential is found.
+                // """
+                try {
+                    GSSManager manager = GSSManager.getInstance();
+                    Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
+                    GSSCredential cred = manager.createCredential(null,
+                            GSSContext.DEFAULT_LIFETIME, krb5Mechanism,
+                            GSSCredential.INITIATE_ONLY);
+                    subject.getPrivateCredentials().add(cred);
+                    LOG.debug("Added private credential to {} principal name: '{}'",
+                            entity, clientPrincipal);
+                } catch (GSSException ex) {
+                    LOG.warn("Cannot add private credential to subject; "
+                                    + "authentication at the server may fail", ex);
+                }
+            }
+            final KerberosName clientKerberosName = new KerberosName(
+                    clientPrincipal.getName());
+            // assume that server and client are in the same realm (by default;
+            // unless the system property
+            // "zookeeper.server.realm" is set).
+            String serverRealm = System.getProperty("zookeeper.server.realm",
+                    clientKerberosName.getRealm());
+            KerberosName serviceKerberosName = new KerberosName(
+                    servicePrincipal + "@" + serverRealm);
+            final String serviceName = serviceKerberosName.getServiceName();
+            final String serviceHostname = serviceKerberosName.getHostName();
+            final String clientPrincipalName = clientKerberosName.toString();
+            try {
+                saslClient = Subject.doAs(subject,
+                        new PrivilegedExceptionAction<SaslClient>() {
+                            public SaslClient run() throws SaslException {
+                                LOG.info("{} will use GSSAPI as SASL mechanism.", entity);
+                                String[] mechs = { "GSSAPI" };
+                                LOG.debug("creating sasl client: {}={};service={};serviceHostname={}",
+                                        new Object[] { entity, clientPrincipalName, serviceName, serviceHostname });
+                                SaslClient saslClient = Sasl.createSaslClient(
+                                        mechs, clientPrincipalName, serviceName,
+                                        serviceHostname, null,
+                                        new SaslClientCallbackHandler(null, entity));
+                                return saslClient;
+                            }
+                        });
+                return saslClient;
+            } catch (Exception e) {
+                LOG.error("Exception while trying to create SASL client", e);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Create an instance of a SaslServer. It will return null if there is an exception.
+     *
+     * @param subject subject
+     * @param protocol protocol
+     * @param serverName server name
+     * @param callbackHandler login callback handler
+     * @param LOG logger
+     * @return sasl server object
+     */
+    public static SaslServer createSaslServer(final Subject subject,
+            final String protocol, final String serverName,
+            final CallbackHandler callbackHandler, final Logger LOG) {
+        if (subject != null) {
+            // server is using a JAAS-authenticated subject: determine service
+            // principal name and hostname from zk server's subject.
+            if (subject.getPrincipals().size() > 0) {
+                try {
+                    final Object[] principals = subject.getPrincipals()
+                            .toArray();
+                    final Principal servicePrincipal = (Principal) principals[0];
+
+                    // e.g. servicePrincipalNameAndHostname :=
+                    // "zookeeper/myhost.foo.com at FOO.COM"
+                    final String servicePrincipalNameAndHostname = servicePrincipal
+                            .getName();
+
+                    int indexOf = servicePrincipalNameAndHostname.indexOf("/");
+
+                    // e.g. servicePrincipalName := "zookeeper"
+                    final String servicePrincipalName = servicePrincipalNameAndHostname
+                            .substring(0, indexOf);
+
+                    // e.g. serviceHostnameAndKerbDomain :=
+                    // "myhost.foo.com at FOO.COM"
+                    final String serviceHostnameAndKerbDomain = servicePrincipalNameAndHostname
+                            .substring(indexOf + 1,
+                                    servicePrincipalNameAndHostname.length());
+
+                    indexOf = serviceHostnameAndKerbDomain.indexOf("@");
+                    // e.g. serviceHostname := "myhost.foo.com"
+                    final String serviceHostname = serviceHostnameAndKerbDomain
+                            .substring(0, indexOf);
+
+                    // TODO: should depend on zoo.cfg specified mechs, but if
+                    // subject is non-null, it can be assumed to be GSSAPI.
+                    final String mech = "GSSAPI";
+
+                    LOG.debug("serviceHostname is '" + serviceHostname + "'");
+                    LOG.debug("servicePrincipalName is '" + servicePrincipalName
+                            + "'");
+                    LOG.debug("SASL mechanism(mech) is '" + mech + "'");
+
+                    boolean usingNativeJgss = Boolean
+                            .getBoolean("sun.security.jgss.native");
+                    if (usingNativeJgss) {
+                        // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
+                        // """
+                        // In addition, when performing operations as a
+                        // particular
+                        // Subject, e.g. Subject.doAs(...) or
+                        // Subject.doAsPrivileged(...), the to-be-used
+                        // GSSCredential should be added to Subject's
+                        // private credential set. Otherwise, the GSS operations
+                        // will fail since no credential is found.
+                        // """
+                        try {
+                            GSSManager manager = GSSManager.getInstance();
+                            Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
+                            GSSName gssName = manager.createName(
+                                    servicePrincipalName + "@"
+                                            + serviceHostname,
+                                    GSSName.NT_HOSTBASED_SERVICE);
+                            GSSCredential cred = manager.createCredential(
+                                    gssName, GSSContext.DEFAULT_LIFETIME,
+                                    krb5Mechanism, GSSCredential.ACCEPT_ONLY);
+                            subject.getPrivateCredentials().add(cred);
+                            LOG.debug("Added private credential to service principal name: '{}',"
+                                            + " GSSCredential name: {}", servicePrincipalName, cred.getName());
+                        } catch (GSSException ex) {
+                            LOG.warn("Cannot add private credential to subject; "
+                                            + "clients authentication may fail", ex);
+                        }
+                    }
+                    try {
+                        return Subject.doAs(subject,
+                                new PrivilegedExceptionAction<SaslServer>() {
+                                    public SaslServer run() {
+                                        try {
+                                            SaslServer saslServer;
+                                            saslServer = Sasl.createSaslServer(
+                                                    mech, servicePrincipalName,
+                                                    serviceHostname, null,
+                                                    callbackHandler);
+                                            return saslServer;
+                                        } catch (SaslException e) {
+                                            LOG.error("Zookeeper Server failed to create a SaslServer to interact with a client during session initiation: ", e);
+                                            return null;
+                                        }
+                                    }
+                                });
+                    } catch (PrivilegedActionException e) {
+                        // TODO: exit server at this point(?)
+                        LOG.error("Zookeeper Quorum member experienced a PrivilegedActionException exception while creating a SaslServer using a JAAS principal context:", e);
+                    }
+                } catch (IndexOutOfBoundsException e) {
+                    LOG.error("server principal name/hostname determination error: ", e);
+                }
+            } else {
+                // JAAS non-GSSAPI authentication: assuming and supporting only
+                // DIGEST-MD5 mechanism for now.
+                // TODO: use 'authMech=' value in zoo.cfg.
+                try {
+                    SaslServer saslServer = Sasl.createSaslServer("DIGEST-MD5",
+                            protocol, serverName, null, callbackHandler);
+                    return saslServer;
+                } catch (SaslException e) {
+                    LOG.error("Zookeeper Quorum member failed to create a SaslServer to interact with a client during session initiation", e);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Convert Kerberos principal name pattern to valid Kerberos principal name.
+     * If the principal name contains hostname pattern "_HOST" then it replaces
+     * with the given hostname, which should be fully-qualified domain name.
+     *
+     * @param principalConfig
+     *            the Kerberos principal name conf value to convert
+     * @param hostname
+     *            the fully-qualified domain name used for substitution
+     * @return converted Kerberos principal name
+     */
+    public static String getServerPrincipal(String principalConfig,
+            String hostname) {
+        String[] components = getComponents(principalConfig);
+        if (components == null || components.length != 2
+                || !components[1].equals(QUORUM_HOSTNAME_PATTERN)) {
+            return principalConfig;
+        } else {
+            return replacePattern(components, hostname);
+        }
+    }
+
+    private static String[] getComponents(String principalConfig) {
+        if (principalConfig == null)
+            return null;
+        return principalConfig.split("[/]");
+    }
+
+    private static String replacePattern(String[] components, String hostname) {
+        return components[0] + "/" + hostname.toLowerCase();
+    }
+}
diff --git a/src/java/main/org/apache/zookeeper/version/util/VerGen.java b/src/java/main/org/apache/zookeeper/version/util/VerGen.java
index d4cbeb6..93a0710 100644
--- a/src/java/main/org/apache/zookeeper/version/util/VerGen.java
+++ b/src/java/main/org/apache/zookeeper/version/util/VerGen.java
@@ -34,7 +34,7 @@ public class VerGen {
         System.exit(1);
     }
 
-    public static void generateFile(File outputDir, Version version, int rev, String buildDate)
+    public static void generateFile(File outputDir, Version version, String rev, String buildDate)
     {
         String path = PACKAGE_NAME.replaceAll("\\.", "/");
         File pkgdir = new File(outputDir, path);
@@ -76,18 +76,19 @@ public class VerGen {
             w.write("\n");
             w.write("package " + PACKAGE_NAME + ";\n\n");
             w.write("public interface " + TYPE_NAME + " {\n");
-            w.write("    public static final int MAJOR=" + version.maj + ";\n");
-            w.write("    public static final int MINOR=" + version.min + ";\n");
-            w.write("    public static final int MICRO=" + version.micro + ";\n");
-            w.write("    public static final String QUALIFIER="
+            w.write("    int MAJOR=" + version.maj + ";\n");
+            w.write("    int MINOR=" + version.min + ";\n");
+            w.write("    int MICRO=" + version.micro + ";\n");
+            w.write("    String QUALIFIER="
                     + (version.qualifier == null ? null :
                         "\"" + version.qualifier + "\"")
                     + ";\n");
-            if (rev < 0) {
+            if (rev.equals("-1")) {
                 System.out.println("Unknown REVISION number, using " + rev);
             }
-            w.write("    public static final int REVISION=" + rev + ";\n");
-            w.write("    public static final String BUILD_DATE=\"" + buildDate
+            w.write("    int REVISION=-1; //TODO: remove as related to SVN VCS\n");
+            w.write("    String REVISION_HASH=\"" + rev + "\";\n");
+            w.write("    String BUILD_DATE=\"" + buildDate
                     + "\";\n");
             w.write("}\n");
         } catch (IOException e) {
@@ -146,7 +147,7 @@ public class VerGen {
      *            <li>min - minor version number
      *            <li>micro - minor minor version number
      *            <li>qualifier - optional qualifier (dash followed by qualifier text)
-     *            <li>rev - current SVN revision number
+     *            <li>rev - current Git revision number
      *            <li>buildDate - date the build
      *            </ul>
      */
@@ -160,11 +161,11 @@ public class VerGen {
                         "Invalid version number format, must be \"x.y.z(-.*)?\"");
                 System.exit(1);
             }
-            int rev;
-            try {
-                rev = Integer.parseInt(args[1]);
-            } catch (NumberFormatException e) {
-                rev = -1;
+            String rev = args[1];
+            if (rev == null || rev.trim().isEmpty()) {
+                rev = "-1";
+            } else {
+                rev = rev.trim();
             }
             generateFile(new File("."), version, rev, args[2]);
         } catch (NumberFormatException e) {
diff --git a/src/java/test/bin/test-github-pr.sh b/src/java/test/bin/test-github-pr.sh
new file mode 100755
index 0000000..e155769
--- /dev/null
+++ b/src/java/test/bin/test-github-pr.sh
@@ -0,0 +1,616 @@
+#!/usr/bin/env bash
+#   Licensed 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.
+
+
+#set -x
+
+### Setup some variables.
+### GIT_COMMIT and BUILD_URL are set by Hudson if it is run by patch process
+### Read variables from properties file
+. `dirname $0`/test-patch.properties
+
+###############################################################################
+parseArgs() {
+  case "$1" in
+    QABUILD)
+      ### Set QABUILD to true to indicate that this script is being run by Hudson
+      QABUILD=true
+      if [[ $# != 14 ]] ; then
+        echo "ERROR: usage $0 QABUILD <PATCH_DIR> <PS_CMD> <WGET_CMD> <JIRACLI> <GIT_CMD> <GREP_CMD> <PATCH_CMD> <FINDBUGS_HOME> <FORREST_HOME> <WORKSPACE_BASEDIR> <JIRA_PASSWD> <JAVA5_HOME> <CURL_CMD>"
+        cleanupAndExit 0
+      fi
+      PATCH_DIR=$2
+      PS=$3
+      WGET=$4
+      JIRACLI=$5
+      GIT=$6
+      GREP=$7
+      PATCH=$8
+      FINDBUGS_HOME=$9
+      FORREST_HOME=${10}
+      BASEDIR=${11}
+      JIRA_PASSWD=${12}
+      JAVA5_HOME=${13}
+      CURL=${14}
+      if [ ! -e "$PATCH_DIR" ] ; then
+        mkdir -p $PATCH_DIR
+      fi
+
+      ## Obtain PR number and title
+      PULLREQUEST_ID=${GIT_PR_NUMBER}
+      PULLREQUEST_TITLE="${GIT_PR_TITLE}"
+
+      ## Extract jira number from PR title
+      local prefix=${PULLREQUEST_TITLE%ZOOKEEPER\-[0-9]*}
+      local noprefix=${PULLREQUEST_TITLE#$prefix}
+      local regex='\(ZOOKEEPER-.[0-9]*\)'
+      defect=$(expr "$noprefix" : ${regex})
+
+      echo "Pull request id: ${PULLREQUEST_ID}"
+      echo "Pull request title: ${PULLREQUEST_TITLE}"
+      echo "Defect number: ${defect}"
+
+      JIRA_COMMENT="GitHub Pull Request ${PULLREQUEST_NUMBER} Build
+      "
+      ;;
+    DEVELOPER)
+      ### Set QABUILD to false to indicate that this script is being run by a developer
+      QABUILD=false
+      if [[ $# != 10 ]] ; then
+        echo "ERROR: usage $0 DEVELOPER <GIT_PR_URL> <SCRATCH_DIR> <GIT_CMD> <GREP_CMD> <PATCH_CMD> <FINDBUGS_HOME> <FORREST_HOME> <WORKSPACE_BASEDIR> <JAVA5_HOME>"
+        cleanupAndExit 0
+      fi
+      PATCH_DIR=$3
+      PATCH_FILE=${PATCH_DIR}/patch
+      curl -L $2.diff > ${PATCH_FILE}
+      ### PATCH_FILE contains the location of the patchfile
+      if [[ ! -e "$PATCH_FILE" ]] ; then
+        echo "Unable to locate the patch file $PATCH_FILE"
+        cleanupAndExit 0
+      fi
+      ### Check if $PATCH_DIR exists. If it does not exist, create a new directory
+      if [[ ! -e "$PATCH_DIR" ]] ; then
+	mkdir "$PATCH_DIR"
+	if [[ $? == 0 ]] ; then
+	  echo "$PATCH_DIR has been created"
+	else
+	  echo "Unable to create $PATCH_DIR"
+	  cleanupAndExit 0
+	fi
+      fi
+      GIT=$4
+      GREP=$5
+      PATCH=$6
+      FINDBUGS_HOME=$7
+      FORREST_HOME=$8
+      BASEDIR=$9
+      JAVA5_HOME=${10}
+      ### Obtain the patch filename to append it to the version number
+      local subject=`grep "Subject:" ${PATCH_FILE}`
+      local length=`expr match ${subject} ZOOKEEPER-[0-9]*`
+      local position=`expr index ${subject} ZOOKEEPER-`
+      defect=${${subject:$position:$length}#ZOOKEEPER-}
+      ;;
+    *)
+      echo "ERROR: usage $0 QABUILD [args] | DEVELOPER [args]"
+      cleanupAndExit 0
+      ;;
+  esac
+}
+
+###############################################################################
+checkout () {
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Testing patch for pull request ${PULLREQUEST_ID}."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  ### When run by a developer, if the workspace contains modifications, do not continue
+  # Ref http://stackoverflow.com/a/2659808 for details on checking dirty status
+  ${GIT} diff-index --quiet HEAD
+  if [[ $? -ne 0 ]] ; then
+    uncommitted=`${GIT} diff --name-only HEAD`
+    uncommitted="You have the following files with uncommitted changes:${NEWLINE}${uncommitted}"
+  fi
+  untracked="$(${GIT} ls-files --exclude-standard --others)" && test -z "${untracked}"
+  if [[ $? -ne 0 ]] ; then
+    untracked="You have untracked and unignored files:${NEWLINE}${untracked}"
+  fi
+
+  if [[ $QABUILD == "false" ]] ; then
+    if [[ $uncommitted || $untracked ]] ; then
+      echo "ERROR: can't run in a workspace that contains the following modifications"
+      echo ""
+      echo "${uncommitted}"
+      echo ""
+      echo "${untracked}"
+      cleanupAndExit 1
+    fi
+  else
+    # I don't believe we need to do anything here - the jenkins job will
+    # cleanup the environment for us ("cleanup before checkout" action)
+    # on the precommit jenkins job
+    echo
+  fi
+  return $?
+}
+
+###############################################################################
+setup () {
+  ### exit if warnings are NOT defined in the properties file
+  if [ -z "$OK_FINDBUGS_WARNINGS" ] || [[ -z "$OK_JAVADOC_WARNINGS" ]] || [[ -z $OK_RELEASEAUDIT_WARNINGS ]]; then
+    echo "Please define the following properties in test-patch.properties file"
+	 echo  "OK_FINDBUGS_WARNINGS"
+	 echo  "OK_RELEASEAUDIT_WARNINGS"
+	 echo  "OK_JAVADOC_WARNINGS"
+    cleanupAndExit 1
+  fi
+  ### get pull request diff
+  ${CURL} -L ${GIT_PR_URL}.diff > $PATCH_DIR/patch
+
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo " Pre-build trunk to verify trunk stability and javac warnings"
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  echo "$ANT_HOME/bin/ant  -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/trunkJavacWarnings.txt 2>&1"
+ $ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/trunkJavacWarnings.txt 2>&1
+  if [[ $? != 0 ]] ; then
+    echo "Trunk compilation is broken?"
+    cleanupAndExit 1
+  fi
+}
+
+###############################################################################
+### Check for @author tags in the patch
+checkAuthor () {
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Checking there are no @author tags in the patch."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  authorTags=`$GREP -c -i '@author' $PATCH_DIR/patch`
+  echo "There appear to be $authorTags @author tags in the patch."
+  if [[ $authorTags != 0 ]] ; then
+    JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 @author.  The patch appears to contain $authorTags @author tags which the Zookeeper community has agreed to not allow in code contributions."
+    return 1
+  fi
+  JIRA_COMMENT="$JIRA_COMMENT
+
+    +1 @author.  The patch does not contain any @author tags."
+  return 0
+}
+
+###############################################################################
+### Check for tests in the patch
+checkTests () {
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Checking there are new or changed tests in the patch."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  testReferences=`$GREP -c -i '/test' $PATCH_DIR/patch`
+  echo "There appear to be $testReferences test files referenced in the patch."
+  if [[ $testReferences == 0 ]] ; then
+    if [[ $QABUILD == "true" ]] ; then
+      patchIsDoc=`$GREP -c -i 'title="documentation' $PATCH_DIR/jira`
+      if [[ $patchIsDoc != 0 ]] ; then
+        echo "The patch appears to be a documentation patch that doesn't require tests."
+        JIRA_COMMENT="$JIRA_COMMENT
+
+    +0 tests included.  The patch appears to be a documentation patch that doesn't require tests."
+        return 0
+      fi
+    fi
+    JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 tests included.  The patch doesn't appear to include any new or modified tests.
+                        Please justify why no new tests are needed for this patch.
+                        Also please list what manual steps were performed to verify this patch."
+    return 1
+  fi
+  JIRA_COMMENT="$JIRA_COMMENT
+
+    +1 tests included.  The patch appears to include $testReferences new or modified tests."
+  return 0
+}
+
+###############################################################################
+### Check there are no javadoc warnings
+checkJavadocWarnings () {
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Determining number of patched javadoc warnings."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= clean javadoc | tee $PATCH_DIR/patchJavadocWarnings.txt"
+  $ANT_HOME/bin/ant -DZookeeperPatchProcess= clean javadoc | tee $PATCH_DIR/patchJavadocWarnings.txt
+  javadocWarnings=`$GREP -o '\[javadoc\] [0-9]* warning' $PATCH_DIR/patchJavadocWarnings.txt | awk '{total += $2} END {print total}'`
+  echo ""
+  echo ""
+  echo "There appear to be $javadocWarnings javadoc warnings generated by the patched build."
+
+  ### if current warnings greater than OK_JAVADOC_WARNINGS
+  if [[ $javadocWarnings > $OK_JAVADOC_WARNINGS ]] ; then
+    JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 javadoc.  The javadoc tool appears to have generated `expr $(($javadocWarnings-$OK_JAVADOC_WARNINGS))` warning messages."
+    return 1
+  fi
+  JIRA_COMMENT="$JIRA_COMMENT
+
+    +1 javadoc.  The javadoc tool did not generate any warning messages."
+  return 0
+}
+
+###############################################################################
+### Check there are no changes in the number of Javac warnings
+checkJavacWarnings () {
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Determining number of patched javac warnings."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  echo "$ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/patchJavacWarnings.txt 2>&1"
+  $ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/patchJavacWarnings.txt 2>&1
+  if [[ $? != 0 ]] ; then
+    JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 javac.  The patch appears to cause tar ant target to fail."
+    return 1
+  fi
+  ### Compare trunk and patch javac warning numbers
+  if [[ -f $PATCH_DIR/patchJavacWarnings.txt ]] ; then
+    trunkJavacWarnings=`$GREP -o '\[javac\] [0-9]* warning' $PATCH_DIR/trunkJavacWarnings.txt | awk '{total += $2} END {print total}'`
+    patchJavacWarnings=`$GREP -o '\[javac\] [0-9]* warning' $PATCH_DIR/patchJavacWarnings.txt | awk '{total += $2} END {print total}'`
+    echo "There appear to be $trunkJavacWarnings javac compiler warnings before the patch and $patchJavacWarnings javac compiler warnings after applying the patch."
+    if [[ $patchJavacWarnings != "" && $trunkJavacWarnings != "" ]] ; then
+      if [[ $patchJavacWarnings -gt $trunkJavacWarnings ]] ; then
+        JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 javac.  The applied patch generated $patchJavacWarnings javac compiler warnings (more than the trunk's current $trunkJavacWarnings warnings)."
+        return 1
+      fi
+    fi
+  fi
+  JIRA_COMMENT="$JIRA_COMMENT
+
+    +1 javac.  The applied patch does not increase the total number of javac compiler warnings."
+  return 0
+}
+
+###############################################################################
+### Check there are no changes in the number of release audit (RAT) warnings
+checkReleaseAuditWarnings () {
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Determining number of patched release audit warnings."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  echo "$ANT_HOME/bin/ant -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= releaseaudit > $PATCH_DIR/patchReleaseAuditWarnings.txt 2>&1"
+  $ANT_HOME/bin/ant -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= releaseaudit > $PATCH_DIR/patchReleaseAuditWarnings.txt 2>&1
+
+  ### Compare trunk and patch release audit warning numbers
+  if [[ -f $PATCH_DIR/patchReleaseAuditWarnings.txt ]] ; then
+    patchReleaseAuditWarnings=`$GREP -c '\!?????' $PATCH_DIR/patchReleaseAuditWarnings.txt`
+    echo ""
+    echo ""
+    echo "There appear to be $OK_RELEASEAUDIT_WARNINGS release audit warnings before the patch and $patchReleaseAuditWarnings release audit warnings after applying the patch."
+    if [[ $patchReleaseAuditWarnings != "" && $OK_RELEASEAUDIT_WARNINGS != "" ]] ; then
+      if [[ $patchReleaseAuditWarnings -gt $OK_RELEASEAUDIT_WARNINGS ]] ; then
+        JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 release audit.  The applied patch generated $patchReleaseAuditWarnings release audit warnings (more than the trunk's current $OK_RELEASEAUDIT_WARNINGS warnings)."
+        $GREP '\!?????' $PATCH_DIR/patchReleaseAuditWarnings.txt > $PATCH_DIR/patchReleaseAuditProblems.txt
+        echo "Lines that start with ????? in the release audit report indicate files that do not have an Apache license header." >> $PATCH_DIR/patchReleaseAuditProblems.txt
+        JIRA_COMMENT_FOOTER="Release audit warnings: $BUILD_URL/artifact/trunk/patchprocess/patchReleaseAuditProblems.txt
+$JIRA_COMMENT_FOOTER"
+        return 1
+      fi
+    fi
+  fi
+  JIRA_COMMENT="$JIRA_COMMENT
+
+    +1 release audit.  The applied patch does not increase the total number of release audit warnings."
+  return 0
+}
+
+###############################################################################
+### Check there are no changes in the number of Checkstyle warnings
+checkStyle () {
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Determining number of patched checkstyle warnings."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  echo "THIS IS NOT IMPLEMENTED YET"
+  echo ""
+  echo ""
+  echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= checkstyle"
+  $ANT_HOME/bin/ant -DZookeeperPatchProcess= checkstyle
+  JIRA_COMMENT_FOOTER="Checkstyle results: $BUILD_URL/artifact/trunk/build/test/checkstyle-errors.html
+$JIRA_COMMENT_FOOTER"
+  ### TODO: calculate actual patchStyleErrors
+#  patchStyleErrors=0
+#  if [[ $patchStyleErrors != 0 ]] ; then
+#    JIRA_COMMENT="$JIRA_COMMENT
+#
+#    -1 checkstyle.  The patch generated $patchStyleErrors code style errors."
+#    return 1
+#  fi
+#  JIRA_COMMENT="$JIRA_COMMENT
+#
+#    +1 checkstyle.  The patch generated 0 code style errors."
+  return 0
+}
+
+###############################################################################
+### Check there are no changes in the number of Findbugs warnings
+checkFindbugsWarnings () {
+  findbugs_version=`${FINDBUGS_HOME}/bin/findbugs -version`
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Determining number of patched Findbugs warnings."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  echo "$ANT_HOME/bin/ant -Dfindbugs.home=$FINDBUGS_HOME -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= findbugs"
+  $ANT_HOME/bin/ant -Dfindbugs.home=$FINDBUGS_HOME -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= findbugs
+  if [ $? != 0 ] ; then
+    JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 findbugs.  The patch appears to cause Findbugs (version ${findbugs_version}) to fail."
+    return 1
+  fi
+JIRA_COMMENT_FOOTER="Findbugs warnings: $BUILD_URL/artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html
+$JIRA_COMMENT_FOOTER"
+  cp $BASEDIR/build/test/findbugs/*.xml $PATCH_DIR/patchFindbugsWarnings.xml
+  $FINDBUGS_HOME/bin/setBugDatabaseInfo -timestamp "01/01/2000" \
+    $PATCH_DIR/patchFindbugsWarnings.xml \
+    $PATCH_DIR/patchFindbugsWarnings.xml
+  findbugsWarnings=`$FINDBUGS_HOME/bin/filterBugs -first "01/01/2000" $PATCH_DIR/patchFindbugsWarnings.xml \
+    $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml | /usr/bin/awk '{print $1}'`
+  $FINDBUGS_HOME/bin/convertXmlToText -html \
+    $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml \
+    $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.html
+  cp $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.html $PATCH_DIR/newPatchFindbugsWarnings.html
+  cp $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml $PATCH_DIR/newPatchFindbugsWarnings.xml
+
+  ### if current warnings greater than OK_FINDBUGS_WARNINGS
+  if [[ $findbugsWarnings > $OK_FINDBUGS_WARNINGS ]] ; then
+    JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 findbugs.  The patch appears to introduce `expr $(($findbugsWarnings-$OK_FINDBUGS_WARNINGS))` new Findbugs (version ${findbugs_version}) warnings."
+    return 1
+  fi
+  JIRA_COMMENT="$JIRA_COMMENT
+
+    +1 findbugs.  The patch does not introduce any new Findbugs (version ${findbugs_version}) warnings."
+  return 0
+}
+
+###############################################################################
+### Run the test-core target
+runCoreTests () {
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Running core tests."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+
+  ### Kill any rogue build processes from the last attempt
+  $PS auxwww | $GREP ZookeeperPatchProcess | /usr/bin/nawk '{print $2}' | /usr/bin/xargs -t -I {} /bin/kill -9 {} > /dev/null
+
+  echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes -Dtest.junit.threads=8 -Dcompile.c++=yes -Dforrest.home=$FORREST_HOME -Djava5.home=$JAVA5_HOME test-core"
+  $ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes -Dtest.junit.threads=8 -Dcompile.c++=yes -Dforrest.home=$FORREST_HOME -Djava5.home=$JAVA5_HOME test-core
+  if [[ $? != 0 ]] ; then
+    JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 core tests.  The patch failed core unit tests."
+    return 1
+  fi
+  JIRA_COMMENT="$JIRA_COMMENT
+
+    +1 core tests.  The patch passed core unit tests."
+  return 0
+}
+
+###############################################################################
+### Run the test-contrib target
+runContribTests () {
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Running contrib tests."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+
+  ### Kill any rogue build processes from the last attempt
+  $PS auxwww | $GREP ZookeeperPatchProcess | /usr/bin/nawk '{print $2}' | /usr/bin/xargs -t -I {} /bin/kill -9 {} > /dev/null
+
+  echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes test-contrib"
+  $ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes test-contrib
+  if [[ $? != 0 ]] ; then
+    JIRA_COMMENT="$JIRA_COMMENT
+
+    -1 contrib tests.  The patch failed contrib unit tests."
+    return 1
+  fi
+  JIRA_COMMENT="$JIRA_COMMENT
+
+    +1 contrib tests.  The patch passed contrib unit tests."
+  return 0
+}
+
+###############################################################################
+### Submit a comment to the defect's Jira
+submitJiraComment () {
+  local result=$1
+  ### Do not output the value of JIRA_COMMENT_FOOTER when run by a developer
+  if [[  $QABUILD == "false" ]] ; then
+    JIRA_COMMENT_FOOTER=""
+  fi
+  if [[ $result == 0 ]] ; then
+    comment="+1 overall.  $JIRA_COMMENT
+
+$JIRA_COMMENT_FOOTER"
+  else
+    comment="-1 overall.  $JIRA_COMMENT
+
+$JIRA_COMMENT_FOOTER"
+  fi
+  ### Output the test result to the console
+  echo "
+
+
+
+$comment"
+
+  if [[ $QABUILD == "true" ]] ; then
+    echo ""
+    echo ""
+    echo "======================================================================"
+    echo "======================================================================"
+    echo "    Adding comment to Jira."
+    echo "======================================================================"
+    echo "======================================================================"
+    echo ""
+    echo ""
+    ### Update Jira with a comment
+    export USER=jenkins
+    $JIRACLI -s https://issues.apache.org/jira -a addcomment -u hadoopqa -p $JIRA_PASSWD --comment "$comment" --issue $defect
+    $JIRACLI -s https://issues.apache.org/jira -a logout -u hadoopqa -p $JIRA_PASSWD
+  fi
+}
+
+###############################################################################
+### Cleanup files
+cleanupAndExit () {
+  local result=$1
+  if [[ $QABUILD == "true" ]] ; then
+    if [ -e "$PATCH_DIR" ] ; then
+      mv $PATCH_DIR $BASEDIR
+    fi
+  fi
+  echo ""
+  echo ""
+  echo "======================================================================"
+  echo "======================================================================"
+  echo "    Finished build."
+  echo "======================================================================"
+  echo "======================================================================"
+  echo ""
+  echo ""
+  exit $result
+}
+
+###############################################################################
+###############################################################################
+###############################################################################
+
+JIRA_COMMENT=""
+JIRA_COMMENT_FOOTER="Console output: $BUILD_URL/console
+
+This message is automatically generated."
+
+### Check if arguments to the script have been specified properly or not
+echo "----- Going to parser args -----"
+parseArgs $@
+cd $BASEDIR
+
+echo "----- Parsed args, going to checkout -----"
+checkout
+RESULT=$?
+if [[ $QABUILD == "true" ]] ; then
+  if [[ $RESULT != 0 ]] ; then
+    exit 100
+  fi
+fi
+setup
+checkAuthor
+(( RESULT = RESULT + $? ))
+
+checkTests
+checkTestsResult=$?
+(( RESULT = RESULT + $checkTestsResult ))
+if [[ $checkTestsResult != 0 ]] ; then
+  submitJiraComment 1
+  cleanupAndExit 1
+fi
+checkJavadocWarnings
+(( RESULT = RESULT + $? ))
+checkJavacWarnings
+(( RESULT = RESULT + $? ))
+### Checkstyle not implemented yet
+#checkStyle
+#(( RESULT = RESULT + $? ))
+checkFindbugsWarnings
+(( RESULT = RESULT + $? ))
+checkReleaseAuditWarnings
+(( RESULT = RESULT + $? ))
+### Do not call these when run by a developer
+if [[ $QABUILD == "true" ]] ; then
+  runCoreTests
+  (( RESULT = RESULT + $? ))
+  runContribTests
+  (( RESULT = RESULT + $? ))
+fi
+JIRA_COMMENT_FOOTER="Test results: $BUILD_URL/testReport/
+$JIRA_COMMENT_FOOTER"
+
+submitJiraComment $RESULT
+cleanupAndExit $RESULT
diff --git a/src/java/test/checkstyle.xml b/src/java/test/checkstyle.xml
index 5415731..a5d5182 100644
--- a/src/java/test/checkstyle.xml
+++ b/src/java/test/checkstyle.xml
@@ -4,6 +4,23 @@
     "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
 
 <!--
+   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.
+-->
+<!--
+  [Forked from Hadoop at 2d7363b27360e36fdd62546c0f9d0b1d78133f29]
 
   Checkstyle configuration for Zookeeper that is based on the sun_checks.xml file
   that is bundled with Checkstyle and includes checks for:
@@ -35,7 +52,7 @@
 
     <!-- Checks that a package.html file exists for each package.     -->
     <!-- See http://checkstyle.sf.net/config_javadoc.html#PackageHtml -->
-    <module name="PackageHtml"/>
+    <module name="JavadocPackage"/>
 
     <!-- Checks whether files end with a new line.                        -->
     <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
@@ -45,6 +62,8 @@
     <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
     <module name="Translation"/>
 
+    <module name="FileLength"/>
+    <module name="FileTabCharacter"/>
 
     <module name="TreeWalker">
 
@@ -96,8 +115,9 @@
 
         <!-- Checks for Size Violations.                    -->
         <!-- See http://checkstyle.sf.net/config_sizes.html -->
-        <module name="FileLength"/>
-        <module name="LineLength"/>
+        <module name="LineLength">
+          <property name="ignorePattern" value="^import"/>
+        </module>
         <module name="MethodLength"/>
         <module name="ParameterNumber"/>
 
@@ -110,7 +130,6 @@
         <module name="NoWhitespaceBefore"/>
         <module name="ParenPad"/>
         <module name="TypecastParenPad"/>
-        <module name="TabCharacter"/>
         <module name="WhitespaceAfter">
 	    	<property name="tokens" value="COMMA, SEMI"/>
 		</module>
@@ -134,7 +153,6 @@
         <!-- Checks for common coding problems               -->
         <!-- See http://checkstyle.sf.net/config_coding.html -->
         <!-- module name="AvoidInlineConditionals"/-->
-        <module name="DoubleCheckedLocking"/>
         <module name="EmptyStatement"/>
         <module name="EqualsHashCode"/>
         <module name="HiddenField">
@@ -143,7 +161,6 @@
         <module name="IllegalInstantiation"/>
         <module name="InnerAssignment"/>
         <module name="MissingSwitchDefault"/>
-        <module name="RedundantThrows"/>
         <module name="SimplifyBooleanExpression"/>
         <module name="SimplifyBooleanReturn"/>
 
diff --git a/src/lastRevision.sh b/src/java/test/data/kerberos/minikdc-krb5.conf
old mode 100755
new mode 100644
similarity index 71%
copy from src/lastRevision.sh
copy to src/java/test/data/kerberos/minikdc-krb5.conf
index a462990..43ec7c4
--- a/src/lastRevision.sh
+++ b/src/java/test/data/kerberos/minikdc-krb5.conf
@@ -1,3 +1,4 @@
+#
 # 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
@@ -13,9 +14,17 @@
 # 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.
+#
+# This resource is originally from HDFS, see the similarly named files there
+# in case of bug fixing, history, etc.
+# Branch : trunk
+# Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33
+#
+[libdefaults]
+    default_realm = {0}
+    udp_preference_limit = 1
 
-# Find the current revision, store it in a file
-FILE=$1
-LASTREV=`svn info | grep '^Revision' | sed -e 's/Revision: *//'`
-
-echo "lastRevision=${LASTREV}" > $FILE
+[realms]
+    {0} = '{'
+        kdc = {1}:{2}
+    '}'
\ No newline at end of file
diff --git a/src/java/test/data/kerberos/minikdc.ldiff b/src/java/test/data/kerberos/minikdc.ldiff
new file mode 100644
index 0000000..20c8d77
--- /dev/null
+++ b/src/java/test/data/kerberos/minikdc.ldiff
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+# This resource is originally from HDFS, see the similarly named files there
+# in case of bug fixing, history, etc.
+# Branch : trunk
+# Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33
+#
+dn: ou=users,dc=${0},dc=${1}
+objectClass: organizationalUnit
+objectClass: top
+ou: users
+
+dn: uid=krbtgt,ou=users,dc=${0},dc=${1}
+objectClass: top
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: krb5principal
+objectClass: krb5kdcentry
+cn: KDC Service
+sn: Service
+uid: krbtgt
+userPassword: secret
+krb5PrincipalName: krbtgt/${2}.${3}@${2}.${3}
+krb5KeyVersionNumber: 0
+
+dn: uid=ldap,ou=users,dc=${0},dc=${1}
+objectClass: top
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: krb5principal
+objectClass: krb5kdcentry
+cn: LDAP
+sn: Service
+uid: ldap
+userPassword: secret
+krb5PrincipalName: ldap/${4}@${2}.${3}
+krb5KeyVersionNumber: 0
\ No newline at end of file
diff --git a/src/java/test/org/apache/zookeeper/ServerConfigTest.java b/src/java/test/org/apache/zookeeper/ServerConfigTest.java
new file mode 100644
index 0000000..27faa74
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/ServerConfigTest.java
@@ -0,0 +1,74 @@
+/**
+ * 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.server.ServerConfig;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+public class ServerConfigTest {
+
+    private ServerConfig serverConfig;
+
+    @Before
+    public void setUp() {
+        serverConfig = new ServerConfig();
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testFewArguments() {
+        String[] args = {"2181"};
+        serverConfig.parse(args);
+    }
+
+    @Test
+    public void testValidArguments() {
+        String[] args = {"2181", "/data/dir", "60000", "10000"};
+        serverConfig.parse(args);
+
+        assertEquals(2181, serverConfig.getClientPortAddress().getPort());
+        assertTrue(checkEquality("/data/dir", serverConfig.getDataDir()));
+        assertEquals(60000, serverConfig.getTickTime());
+        assertEquals(10000, serverConfig.getMaxClientCnxns());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testTooManyArguments() {
+        String[] args = {"2181", "/data/dir", "60000", "10000", "9999"};
+        serverConfig.parse(args);
+    }
+
+    boolean checkEquality(String a, String b) {
+        assertNotNull(a);
+        assertNotNull(b);
+        return a.equals(b);
+    }
+
+    boolean checkEquality(String a, File b) {
+        assertNotNull(a);
+        assertNotNull(b);
+        return new File(a).equals(b);
+    }
+}
\ No newline at end of file
diff --git a/src/java/test/org/apache/zookeeper/VerGenTest.java b/src/java/test/org/apache/zookeeper/VerGenTest.java
index 13d3abd..607edbb 100644
--- a/src/java/test/org/apache/zookeeper/VerGenTest.java
+++ b/src/java/test/org/apache/zookeeper/VerGenTest.java
@@ -72,7 +72,7 @@ public class VerGenTest extends ZKTestCase {
     public void testGenFile() throws Exception {
         VerGen.Version v = VerGen.parseVersionString(input);
         File outputDir = ClientBase.createTmpDir();
-        VerGen.generateFile(outputDir, v, 1, "Nov1");
+        VerGen.generateFile(outputDir, v, "1", "Nov1");
         ClientBase.recursiveDelete(outputDir);
     }
 }
diff --git a/src/java/test/org/apache/zookeeper/ZKTestCase.java b/src/java/test/org/apache/zookeeper/ZKTestCase.java
index 97e2db6..9098fc4 100644
--- a/src/java/test/org/apache/zookeeper/ZKTestCase.java
+++ b/src/java/test/org/apache/zookeeper/ZKTestCase.java
@@ -52,6 +52,10 @@ public class ZKTestCase {
         @Override
         public void starting(FrameworkMethod method) {
             testName = method.getName();
+            // ZOOKEEPER-2693 disables all 4lw by default.
+            // Here we enable the 4lw which ZooKeeper tests depends.
+            System.setProperty("zookeeper.4lw.commands.whitelist", "*");
+
             LOG.info("STARTING " + testName);
         }
 
diff --git a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java
index d75a817..2cdc4cb 100644
--- a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java
+++ b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java
@@ -255,4 +255,27 @@ public class ZooKeeperTest extends ClientBase {
         Assert.assertEquals("empty string is not taken as third argument", zkMain.cl.getCmdArgument(2), "");
         Assert.assertEquals("empty string is not taken as fourth argument", zkMain.cl.getCmdArgument(3), "");
     }
+
+    // ZOOKEEPER-2467 : Testing negative number for redo command
+    @Test
+    public void testRedoWithNegativeCmdNumber() throws Exception {
+        final ZooKeeper zk = createClient();
+        ZooKeeperMain zkMain = new ZooKeeperMain(zk);
+        String cmd1 = "redo -1";
+
+        // setup redirect out/err streams to get System.in/err, use this
+        // judiciously!
+        final PrintStream systemOut = System.out; // get current out
+        final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(outContent));
+        try {
+            zkMain.executeLine(cmd1);
+            Assert.assertEquals("Command index out of range", outContent
+                    .toString().trim());
+        } finally {
+            // revert redirect of out/err streams - important step!
+            System.setOut(systemOut);
+        }
+    }
+
 }
diff --git a/src/java/test/org/apache/zookeeper/server/DataNodeTest.java b/src/java/test/org/apache/zookeeper/server/DataNodeTest.java
new file mode 100644
index 0000000..6289766
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/DataNodeTest.java
@@ -0,0 +1,65 @@
+/**
+ * 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 static org.junit.Assert.*;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+public class DataNodeTest {
+
+    @Test
+    public void testGetChildrenShouldReturnEmptySetWhenThereAreNoChidren() {
+        // create DataNode and call getChildren
+        DataNode dataNode = new DataNode();
+        Set<String> children = dataNode.getChildren();
+        assertNotNull(children);
+        assertEquals(0, children.size());
+
+        // add child,remove child and then call getChildren
+        String child = "child";
+        dataNode.addChild(child);
+        dataNode.removeChild(child);
+        children = dataNode.getChildren();
+        assertNotNull(children);
+        assertEquals(0, children.size());
+
+        // Returned empty set must not be modifiable
+        children = dataNode.getChildren();
+        try {
+            children.add("new child");
+            fail("UnsupportedOperationException is expected");
+        } catch (UnsupportedOperationException e) {
+            // do nothing
+        }
+    }
+
+    @Test
+    public void testGetChildrenReturnsImmutableEmptySet() {
+        DataNode dataNode = new DataNode();
+        Set<String> children = dataNode.getChildren();
+        try {
+            children.add("new child");
+            fail("UnsupportedOperationException is expected");
+        } catch (UnsupportedOperationException e) {
+            // do nothing
+        }
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java b/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java
index 5c94ed7..bdee20f 100644
--- a/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java
+++ b/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java
@@ -18,14 +18,15 @@
 package org.apache.zookeeper.server;
 
 import java.io.IOException;
-
-import junit.framework.Assert;
+import java.nio.ByteBuffer;
+import java.nio.channels.CancelledKeyException;
 
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.ZooDefs.Ids;
 import org.apache.zookeeper.ZooKeeper;
 import org.apache.zookeeper.test.ClientBase;
+import org.junit.Assert;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -68,4 +69,38 @@ public class NIOServerCnxnTest extends ClientBase {
             zk.close();
         }
     }
+
+    /**
+     * Mock extension of NIOServerCnxn to test for
+     * CancelledKeyException (ZOOKEEPER-2044).
+     */
+    private static class MockNIOServerCnxn extends NIOServerCnxn {
+        public MockNIOServerCnxn(NIOServerCnxn cnxn)
+                throws IOException {
+            super(cnxn.zkServer, cnxn.sock, cnxn.sk, cnxn.factory);
+        }
+
+        public void mockSendBuffer(ByteBuffer bb) throws Exception {
+            super.internalSendBuffer(bb);
+        }
+    }
+
+    @Test(timeout = 30000)
+    public void testValidSelectionKey() throws Exception {
+        final ZooKeeper zk = createZKClient(hostPort, 3000);
+        try {
+            Iterable<ServerCnxn> connections = serverFactory.getConnections();
+            for (ServerCnxn serverCnxn : connections) {
+                MockNIOServerCnxn mock = new MockNIOServerCnxn((NIOServerCnxn) serverCnxn);
+                // Cancel key
+                ((NIOServerCnxn) serverCnxn).sock.keyFor(((NIOServerCnxnFactory) serverFactory).selector).cancel();;
+                mock.mockSendBuffer(ByteBuffer.allocate(8));
+            }
+        } catch (CancelledKeyException e) {
+            LOG.error("Exception while sending bytes!", e);
+            Assert.fail(e.toString());
+        } finally {
+            zk.close();
+        }
+    }
 }
diff --git a/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java
index 3e00e00..1685a5b 100644
--- a/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java
+++ b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java
@@ -29,6 +29,7 @@ import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.zookeeper.data.Stat;
 
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.PortAssignment;
@@ -42,6 +43,7 @@ import org.apache.zookeeper.server.ServerCnxnFactory;
 import org.apache.zookeeper.server.SyncRequestProcessor;
 import org.apache.zookeeper.server.ZooKeeperServer;
 import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
+import org.apache.zookeeper.server.persistence.Util;
 import org.apache.zookeeper.test.ClientBase;
 import org.junit.After;
 import org.junit.Assert;
@@ -174,10 +176,17 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
         int nRecentSnap = 4; // n recent snap shots
         int nRecentCount = 30;
         int offset = 0;
+
         tmpDir = ClientBase.createTmpDir();
         File version2 = new File(tmpDir.toString(), "version-2");
         Assert.assertTrue("Failed to create version_2 dir:" + version2.toString(),
                 version2.mkdir());
+
+        // Test that with no snaps, findNRecentSnapshots returns empty list
+        FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir);
+        List<File> foundSnaps = txnLog.findNRecentSnapshots(1);
+        assertEquals(0, foundSnaps.size());
+
         List<File> expectedNRecentSnapFiles = new ArrayList<File>();
         int counter = offset + (2 * nRecentCount);
         for (int i = 0; i < nRecentCount; i++) {
@@ -196,14 +205,25 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
             }
         }
 
-        FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir);
+        // Test that when we ask for recent snaps we get the number we asked for and
+        // the files we expected
         List<File> nRecentSnapFiles = txnLog.findNRecentSnapshots(nRecentSnap);
-        txnLog.close();
         Assert.assertEquals("exactly 4 snapshots ", 4,
                 nRecentSnapFiles.size());
         expectedNRecentSnapFiles.removeAll(nRecentSnapFiles);
         Assert.assertEquals("Didn't get the recent snap files", 0,
                 expectedNRecentSnapFiles.size());
+
+        // Test that when asking for more snaps than we created, we still only get snaps
+        // not logs or anything else (per ZOOKEEPER-2420)
+        nRecentSnapFiles = txnLog.findNRecentSnapshots(nRecentCount + 5);
+        assertEquals(nRecentCount, nRecentSnapFiles.size());
+        for (File f: nRecentSnapFiles) {
+            Assert.assertTrue("findNRecentSnapshots() returned a non-snapshot: " + f.getPath(),
+                   (Util.getZxidFromName(f.getName(), "snapshot") != -1));
+        }
+
+        txnLog.close();
     }
 
     /**
@@ -227,14 +247,21 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
         List<File> logs = new ArrayList<File>();
         List<File> snapsAboveRecentFiles = new ArrayList<File>();
         List<File> logsAboveRecentFiles = new ArrayList<File>();
-        createDataDirFiles(offset, fileToPurgeCount, version2, snapsToPurge,
+        createDataDirFiles(offset, fileToPurgeCount, false, version2, snapsToPurge,
                 logsToPurge);
-        createDataDirFiles(offset, nRecentCount, version2, snaps, logs);
-        createDataDirFiles(offset, fileAboveRecentCount, version2,
+        createDataDirFiles(offset, nRecentCount, false, version2, snaps, logs);
+        logs.add(logsToPurge.remove(0)); // log that precedes first retained snapshot is also retained
+        createDataDirFiles(offset, fileAboveRecentCount, false, version2,
                 snapsAboveRecentFiles, logsAboveRecentFiles);
 
+        /**
+         * The newest log file preceding the oldest retained snapshot is not removed as it may
+         * contain transactions newer than the oldest snapshot.
+         */
+        logsToPurge.remove(0);
+
         FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir);
-        PurgeTxnLog.retainNRecentSnapshots(txnLog, snaps);
+        PurgeTxnLog.purgeOlderSnapshots(txnLog, snaps.get(snaps.size() - 1));
         txnLog.close();
         verifyFilesAfterPurge(snapsToPurge, false);
         verifyFilesAfterPurge(logsToPurge, false);
@@ -245,11 +272,24 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
     }
 
     /**
-     * Tests purge where the data directory contains snap files equals to the
+     * Tests purge where the data directory contains snap files and log files equals to the
      * number of files to be retained
      */
     @Test
     public void testSnapFilesEqualsToRetain() throws Exception {
+        internalTestSnapFilesEqualsToRetain(false);
+    }
+
+    /**
+     * Tests purge where the data directory contains snap files equals to the
+     * number of files to be retained, and a log file that precedes the earliest snapshot
+     */
+    @Test
+    public void testSnapFilesEqualsToRetainWithPrecedingLog() throws Exception {
+        internalTestSnapFilesEqualsToRetain(true);
+    }
+
+    public void internalTestSnapFilesEqualsToRetain(boolean testWithPrecedingLogFile) throws Exception {
         int nRecentCount = 3;
         AtomicInteger offset = new AtomicInteger(0);
         tmpDir = ClientBase.createTmpDir();
@@ -258,10 +298,10 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
                 version2.mkdir());
         List<File> snaps = new ArrayList<File>();
         List<File> logs = new ArrayList<File>();
-        createDataDirFiles(offset, nRecentCount, version2, snaps, logs);
+        createDataDirFiles(offset, nRecentCount, testWithPrecedingLogFile, version2, snaps, logs);
 
         FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir);
-        PurgeTxnLog.retainNRecentSnapshots(txnLog, snaps);
+        PurgeTxnLog.purgeOlderSnapshots(txnLog, snaps.get(snaps.size() - 1));
         txnLog.close();
         verifyFilesAfterPurge(snaps, true);
         verifyFilesAfterPurge(logs, true);
@@ -284,12 +324,19 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
         List<File> logsToPurge = new ArrayList<File>();
         List<File> snaps = new ArrayList<File>();
         List<File> logs = new ArrayList<File>();
-        createDataDirFiles(offset, fileToPurgeCount, version2, snapsToPurge,
+        createDataDirFiles(offset, fileToPurgeCount, false, version2, snapsToPurge,
                 logsToPurge);
-        createDataDirFiles(offset, nRecentCount, version2, snaps, logs);
+        createDataDirFiles(offset, nRecentCount, false, version2, snaps, logs);
+        logs.add(logsToPurge.remove(0)); // log that precedes first retained snapshot is also retained
+
+        /**
+         * The newest log file preceding the oldest retained snapshot is not removed as it may
+         * contain transactions newer than the oldest snapshot.
+         */
+        logsToPurge.remove(0);
 
         FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir);
-        PurgeTxnLog.retainNRecentSnapshots(txnLog, snaps);
+        PurgeTxnLog.purgeOlderSnapshots(txnLog, snaps.get(snaps.size() - 1));
         txnLog.close();
         verifyFilesAfterPurge(snapsToPurge, false);
         verifyFilesAfterPurge(logsToPurge, false);
@@ -329,15 +376,16 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
             snapFile.createNewFile();
         }
 
-        int numberOfFilesToKeep = 10;
+        int numberOfSnapFilesToKeep = 10;
         // scenario where four parameter are passed
         String[] args = new String[] { dataLogDir.getAbsolutePath(),
                 dataDir.getAbsolutePath(), "-n",
-                Integer.toString(numberOfFilesToKeep) };
+                Integer.toString(numberOfSnapFilesToKeep) };
         PurgeTxnLog.main(args);
 
-        assertEquals(numberOfFilesToKeep, dataDirVersion2.listFiles().length);
-        assertEquals(numberOfFilesToKeep, dataLogDirVersion2.listFiles().length);
+        assertEquals(numberOfSnapFilesToKeep, dataDirVersion2.listFiles().length);
+        // Since for each snapshot we have a log file with same zxid, expect same # logs as snaps to be kept
+        assertEquals(numberOfSnapFilesToKeep, dataLogDirVersion2.listFiles().length);
         ClientBase.recursiveDelete(tmpDir);
 
     }
@@ -373,28 +421,121 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
             snapFile.createNewFile();
         }
 
-        int numberOfFilesToKeep = 10;
+        int numberOfSnapFilesToKeep = 10;
         // scenario where only three parameter are passed
         String[] args = new String[] { dataLogDir.getAbsolutePath(), "-n",
-                Integer.toString(numberOfFilesToKeep) };
+                Integer.toString(numberOfSnapFilesToKeep) };
         PurgeTxnLog.main(args);
-        assertEquals(numberOfFilesToKeep + numberOfFilesToKeep,
+        assertEquals(numberOfSnapFilesToKeep * 2, // Since for each snapshot we have a log file with same zxid, expect same # logs as snaps to be kept
                 dataLogDirVersion2.listFiles().length);
         ClientBase.recursiveDelete(tmpDir);
 
     }
 
-    private void createDataDirFiles(AtomicInteger offset, int limit,
+    /**
+     * Verifies that purge does not delete any log files which started before the oldest retained
+     * snapshot but which might extend beyond it.
+     * @throws Exception an exception might be thrown here
+     */
+    @Test
+    public void testPurgeDoesNotDeleteOverlappingLogFile() throws Exception {
+        // Setting used for snapRetainCount in this test.
+        final int SNAP_RETAIN_COUNT = 3;
+        // Number of znodes this test creates in each snapshot.
+        final int NUM_ZNODES_PER_SNAPSHOT = 100;
+        /**
+         * Set a sufficiently high snapCount to ensure that we don't rollover the log.  Normally,
+         * the default value (100K at time of this writing) would ensure this, but we make that
+         * dependence explicit here to make the test future-proof.  Not rolling over the log is
+         * important for this test since we are testing retention of the one and only log file which
+         * predates each retained snapshot.
+         */
+        SyncRequestProcessor.setSnapCount(SNAP_RETAIN_COUNT * NUM_ZNODES_PER_SNAPSHOT * 10);
+
+        // Create Zookeeper and connect to it.
+        tmpDir = ClientBase.createTmpDir();
+        ClientBase.setupTestEnv();
+        ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000);
+        final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]);
+        ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1);
+        f.startup(zks);
+        Assert.assertTrue("waiting for server being up ",
+                ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT));
+        ZooKeeper zk = ClientBase.createZKClient(HOSTPORT);
+
+        // Unique identifier for each znode that we create.
+        int unique = 0;
+        try {
+            /**
+             * Create some znodes and take a snapshot.  Repeat this until we have SNAP_RETAIN_COUNT
+             * snapshots.  Do not rollover the log.
+             */
+            for (int snapshotCount = 0; snapshotCount < SNAP_RETAIN_COUNT; snapshotCount++) {
+                for (int i = 0; i< 100; i++, unique++) {
+                    zk.create("/snap-" + unique, new byte[0], Ids.OPEN_ACL_UNSAFE,
+                            CreateMode.PERSISTENT);
+                }
+                zks.takeSnapshot();
+            }
+            // Create some additional znodes without taking a snapshot afterwards.
+            for (int i = 0; i< 100; i++, unique++) {
+                zk.create("/snap-" + unique, new byte[0], Ids.OPEN_ACL_UNSAFE,
+                        CreateMode.PERSISTENT);
+            }
+        } finally {
+            zk.close();
+        }
+
+        // Shutdown Zookeeper.
+        f.shutdown();
+        zks.getTxnLogFactory().close();
+        zks.shutdown();
+        Assert.assertTrue("waiting for server to shutdown",
+                ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT));
+
+        // Purge snapshot and log files.
+        PurgeTxnLog.purge(tmpDir, tmpDir, SNAP_RETAIN_COUNT);
+
+        // Initialize Zookeeper again from the same dataDir.
+        zks = new ZooKeeperServer(tmpDir, tmpDir, 3000);
+        f = ServerCnxnFactory.createFactory(PORT, -1);
+        f.startup(zks);
+        zk = ClientBase.createZKClient(HOSTPORT);
+
+        /**
+         * Verify that the last znode that was created above exists.  This znode's creation was
+         * captured by the transaction log which was created before any of the above
+         * SNAP_RETAIN_COUNT snapshots were created, but it's not captured in any of these
+         * snapshots.  So for it it exist, the (only) existing log file should not have been purged.
+         */
+        final String lastZnode = "/snap-" + (unique - 1);
+        final Stat stat = zk.exists(lastZnode, false);
+        Assert.assertNotNull("Last znode does not exist: " + lastZnode, stat);
+
+        // Shutdown for the last time.
+        f.shutdown();
+        zks.getTxnLogFactory().close();
+        zks.shutdown();
+    }
+
+    private File createDataDirLogFile(File version_2, int Zxid) throws IOException {
+        File logFile = new File(version_2 + "/log." + Long.toHexString(Zxid));
+        Assert.assertTrue("Failed to create log File:" + logFile.toString(),
+                logFile.createNewFile());
+        return logFile;
+    }
+
+    private void createDataDirFiles(AtomicInteger offset, int limit, boolean createPrecedingLogFile,
             File version_2, List<File> snaps, List<File> logs)
             throws IOException {
         int counter = offset.get() + (2 * limit);
+        if (createPrecedingLogFile) {
+            counter++;
+        }
         offset.set(counter);
         for (int i = 0; i < limit; i++) {
             // simulate log file
-            File logFile = new File(version_2 + "/log." + Long.toHexString(--counter));
-            Assert.assertTrue("Failed to create log File:" + logFile.toString(),
-                    logFile.createNewFile());
-            logs.add(logFile);
+            logs.add(createDataDirLogFile(version_2, --counter));
             // simulate snapshot file
             File snapFile = new File(version_2 + "/snapshot."
                     + Long.toHexString(--counter));
@@ -402,6 +543,9 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
                     snapFile.createNewFile());
             snaps.add(snapFile);
         }
+        if (createPrecedingLogFile) {
+            logs.add(createDataDirLogFile(version_2, --counter));
+        }
     }
 
     private void verifyFilesAfterPurge(List<File> logs, boolean exists) {
@@ -428,7 +572,7 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
                             zk.create(mynode, new byte[0], Ids.OPEN_ACL_UNSAFE,
                                     CreateMode.PERSISTENT);
                         } catch (Exception e) {
-                            LOG.error("Unexpected exception occured!", e);
+                            LOG.error("Unexpected exception occurred!", e);
                         }
                         if (i == 200) {
                             doPurge.countDown();
@@ -447,8 +591,8 @@ public class PurgeTxnTest extends ZKTestCase implements  Watcher {
             Assert.assertTrue("ZkClient ops is not finished!",
                     finished.await(OP_TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS));
         } catch (InterruptedException ie) {
-            LOG.error("Unexpected exception occured!", ie);
-            Assert.fail("Unexpected exception occured!");
+            LOG.error("Unexpected exception occurred!", ie);
+            Assert.fail("Unexpected exception occurred!");
         }
         return znodes;
     }
diff --git a/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java b/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java
index 61072e6..f427102 100644
--- a/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java
+++ b/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java
@@ -143,8 +143,8 @@ public class SessionTrackerTest extends ZKTestCase {
         public void processRequest(Request request) {
             // check session close request
             if (request.type == OpCode.closeSession) {
-                latch.countDown();
                 countOfCloseSessionReq++;
+                latch.countDown();
             }
         }
 
diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java
index fcb7889..2ffb91e 100644
--- a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java
+++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java
@@ -62,35 +62,45 @@ public class ZooKeeperServerMainTest extends ZKTestCase implements Watcher {
         final File confFile;
         final TestZKSMain main;
         final File tmpDir;
+        final File dataDir;
+        final File logDir;
 
         public MainThread(int clientPort, boolean preCreateDirs) throws IOException {
+            this(clientPort, preCreateDirs, ClientBase.createTmpDir());
+        }
+
+        public MainThread(int clientPort, boolean preCreateDirs, File tmpDir) throws IOException {
             super("Standalone server with clientPort:" + clientPort);
-            tmpDir = ClientBase.createTmpDir();
-            confFile = new File(tmpDir, "zoo.cfg");
+            this.tmpDir = tmpDir;
+            confFile = new File(this.tmpDir, "zoo.cfg");
 
             FileWriter fwriter = new FileWriter(confFile);
             fwriter.write("tickTime=2000\n");
             fwriter.write("initLimit=10\n");
             fwriter.write("syncLimit=5\n");
 
-            File dataDir = new File(tmpDir, "data");
-            String dir = dataDir.toString();
-            String dirLog = dataDir.toString() + "_txnlog";
+            dataDir = new File(this.tmpDir, "data");
+            logDir = new File(dataDir.toString() + "_txnlog");
             if (preCreateDirs) {
                 if (!dataDir.mkdir()) {
                     throw new IOException("unable to mkdir " + dataDir);
                 }
-                dirLog = dataDir.toString();
+                if (!logDir.mkdir()) {
+                    throw new IOException("unable to mkdir " + logDir);
+                }
             }
-            
+
+            String dataDirPath = dataDir.toString();
+            String logDirPath = logDir.toString();
+
             // Convert windows path to UNIX to avoid problems with "\"
             String osname = java.lang.System.getProperty("os.name");
             if (osname.toLowerCase().contains("windows")) {
-                dir = dir.replace('\\', '/');
-                dirLog = dirLog.replace('\\', '/');
+                dataDirPath = dataDirPath.replace('\\', '/');
+                logDirPath = logDirPath.replace('\\', '/');
             }
-            fwriter.write("dataDir=" + dir + "\n");
-            fwriter.write("dataLogDir=" + dirLog + "\n");
+            fwriter.write("dataDir=" + dataDirPath + "\n");
+            fwriter.write("dataLogDir=" + logDirPath + "\n");
             fwriter.write("clientPort=" + clientPort + "\n");
             fwriter.flush();
             fwriter.close();
@@ -198,6 +208,86 @@ public class ZooKeeperServerMainTest extends ZKTestCase implements Watcher {
     }
 
     /**
+     * Tests that the ZooKeeper server will fail to start if the
+     * snapshot directory is read only.
+     *
+     * This test will fail if it is executed as root user.
+     */
+    @Test(timeout = 30000)
+    public void testReadOnlySnapshotDir() throws Exception {
+        ClientBase.setupTestEnv();
+        final int CLIENT_PORT = PortAssignment.unique();
+
+        // Start up the ZK server to automatically create the necessary directories
+        // and capture the directory where data is stored
+        MainThread main = new MainThread(CLIENT_PORT, true);
+        File tmpDir = main.tmpDir;
+        main.start();
+        Assert.assertTrue("waiting for server being up", ClientBase
+                .waitForServerUp("127.0.0.1:" + CLIENT_PORT,
+                        CONNECTION_TIMEOUT / 2));
+        main.shutdown();
+
+        // Make the snapshot directory read only
+        File snapDir = new File(main.dataDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION);
+        snapDir.setWritable(false);
+
+        // Restart ZK and observe a failure
+        main = new MainThread(CLIENT_PORT, false, tmpDir);
+        main.start();
+
+        Assert.assertFalse("waiting for server being up", ClientBase
+                .waitForServerUp("127.0.0.1:" + CLIENT_PORT,
+                        CONNECTION_TIMEOUT / 2));
+
+        main.shutdown();
+
+        snapDir.setWritable(true);
+
+        main.deleteDirs();
+    }
+
+    /**
+     * Tests that the ZooKeeper server will fail to start if the
+     * transaction log directory is read only.
+     *
+     * This test will fail if it is executed as root user.
+     */
+    @Test(timeout = 30000)
+    public void testReadOnlyTxnLogDir() throws Exception {
+        ClientBase.setupTestEnv();
+        final int CLIENT_PORT = PortAssignment.unique();
+
+        // Start up the ZK server to automatically create the necessary directories
+        // and capture the directory where data is stored
+        MainThread main = new MainThread(CLIENT_PORT, true);
+        File tmpDir = main.tmpDir;
+        main.start();
+        Assert.assertTrue("waiting for server being up", ClientBase
+                .waitForServerUp("127.0.0.1:" + CLIENT_PORT,
+                        CONNECTION_TIMEOUT / 2));
+        main.shutdown();
+
+        // Make the transaction log directory read only
+        File logDir = new File(main.logDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION);
+        logDir.setWritable(false);
+
+        // Restart ZK and observe a failure
+        main = new MainThread(CLIENT_PORT, false, tmpDir);
+        main.start();
+
+        Assert.assertFalse("waiting for server being up", ClientBase
+                .waitForServerUp("127.0.0.1:" + CLIENT_PORT,
+                        CONNECTION_TIMEOUT / 2));
+
+        main.shutdown();
+
+        logDir.setWritable(true);
+
+        main.deleteDirs();
+    }
+
+    /**
      * Verify the ability to start a standalone server instance.
      */
     @Test
diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerStartupTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerStartupTest.java
new file mode 100644
index 0000000..9435711
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerStartupTest.java
@@ -0,0 +1,302 @@
+/**
+ * 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 static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.zookeeper.PortAssignment;
+import org.apache.zookeeper.ZKTestCase;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.test.ClientBase;
+import org.apache.zookeeper.test.ClientBase.CountdownWatcher;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class tests the startup behavior of ZooKeeper server.
+ */
+public class ZooKeeperServerStartupTest extends ZKTestCase {
+    private static final Logger LOG = LoggerFactory
+            .getLogger(ZooKeeperServerStartupTest.class);
+    private static int PORT = PortAssignment.unique();
+    private static String HOST = "127.0.0.1";
+    private static String HOSTPORT = HOST + ":" + PORT;
+    private static final String ZK_NOT_SERVING = "This ZooKeeper instance is not currently serving requests";
+
+    private ServerCnxnFactory servcnxnf;
+    private ZooKeeperServer zks;
+    private File tmpDir;
+    private CountDownLatch startupDelayLatch = new CountDownLatch(1);
+
+    @After
+    public void teardown() throws Exception {
+        // count down to avoid infinite blocking call due to this latch, if
+        // any.
+        startupDelayLatch.countDown();
+
+        if (servcnxnf != null) {
+            servcnxnf.shutdown();
+        }
+        if (zks != null) {
+            zks.shutdown();
+        }
+        if (zks.getZKDatabase() != null) {
+            zks.getZKDatabase().close();
+        }
+        if (tmpDir != null) {
+            ClientBase.recursiveDelete(tmpDir);
+        }
+    }
+
+    /**
+     * Test case for
+     * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}.
+     */
+    @Test(timeout = 30000)
+    public void testClientConnectionRequestDuringStartupWithNIOServerCnxn()
+            throws Exception {
+        tmpDir = ClientBase.createTmpDir();
+        ClientBase.setupTestEnv();
+
+        startSimpleZKServer(startupDelayLatch);
+        SimpleZooKeeperServer simplezks = (SimpleZooKeeperServer) zks;
+        Assert.assertTrue(
+                "Failed to invoke zks#startup() method during server startup",
+                simplezks.waitForStartupInvocation(10));
+
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zkClient = new ZooKeeper(HOSTPORT,
+                ClientBase.CONNECTION_TIMEOUT, watcher);
+
+        Assert.assertFalse(
+                "Since server is not fully started, zks#createSession() shouldn't be invoked",
+                simplezks.waitForSessionCreation(5));
+
+        LOG.info(
+                "Decrements the count of the latch, so that server will proceed with startup");
+        startupDelayLatch.countDown();
+
+        Assert.assertTrue("waiting for server being up ", ClientBase
+                .waitForServerUp(HOSTPORT, ClientBase.CONNECTION_TIMEOUT));
+
+        Assert.assertTrue(
+                "Failed to invoke zks#createSession() method during client session creation",
+                simplezks.waitForSessionCreation(5));
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zkClient.close();
+    }
+
+    /**
+     * Test case for
+     * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}.
+     */
+    @Test(timeout = 30000)
+    public void testClientConnectionRequestDuringStartupWithNettyServerCnxn()
+            throws Exception {
+        tmpDir = ClientBase.createTmpDir();
+        ClientBase.setupTestEnv();
+
+        String originalServerCnxnFactory = System
+                .getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY);
+        try {
+            System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY,
+                    NettyServerCnxnFactory.class.getName());
+            startSimpleZKServer(startupDelayLatch);
+            SimpleZooKeeperServer simplezks = (SimpleZooKeeperServer) zks;
+            Assert.assertTrue(
+                    "Failed to invoke zks#startup() method during server startup",
+                    simplezks.waitForStartupInvocation(10));
+
+            CountdownWatcher watcher = new CountdownWatcher();
+            ZooKeeper zkClient = new ZooKeeper(HOSTPORT,
+                    ClientBase.CONNECTION_TIMEOUT, watcher);
+
+            Assert.assertFalse(
+                    "Since server is not fully started, zks#createSession() shouldn't be invoked",
+                    simplezks.waitForSessionCreation(5));
+
+            LOG.info(
+                    "Decrements the count of the latch, so that server will proceed with startup");
+            startupDelayLatch.countDown();
+
+            Assert.assertTrue("waiting for server being up ", ClientBase
+                    .waitForServerUp(HOSTPORT, ClientBase.CONNECTION_TIMEOUT));
+
+            Assert.assertTrue(
+                    "Failed to invoke zks#createSession() method during client session creation",
+                    simplezks.waitForSessionCreation(5));
+            watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+            zkClient.close();
+        } finally {
+            // reset cnxn factory
+            if (originalServerCnxnFactory == null) {
+                System.clearProperty(
+                        ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY);
+            } else {
+                System.setProperty(
+                        ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY,
+                        originalServerCnxnFactory);
+            }
+        }
+    }
+
+    /**
+     * Test case for
+     * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}.
+     */
+    @Test(timeout = 30000)
+    public void testFourLetterWordsWithNIOServerCnxn() throws Exception {
+        startSimpleZKServer(startupDelayLatch);
+        verify("conf", ZK_NOT_SERVING);
+        verify("crst", ZK_NOT_SERVING);
+        verify("cons", ZK_NOT_SERVING);
+        verify("dump", ZK_NOT_SERVING);
+        verify("mntr", ZK_NOT_SERVING);
+        verify("stat", ZK_NOT_SERVING);
+        verify("srst", ZK_NOT_SERVING);
+        verify("wchs", ZK_NOT_SERVING);
+        verify("isro", "null");
+    }
+
+    /**
+     * Test case for
+     * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}.
+     */
+    @Test(timeout = 30000)
+    public void testFourLetterWordsWithNettyServerCnxn() throws Exception {
+        String originalServerCnxnFactory = System
+                .getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY);
+        try {
+            startSimpleZKServer(startupDelayLatch);
+            verify("conf", ZK_NOT_SERVING);
+            verify("crst", ZK_NOT_SERVING);
+            verify("cons", ZK_NOT_SERVING);
+            verify("dump", ZK_NOT_SERVING);
+            verify("mntr", ZK_NOT_SERVING);
+            verify("stat", ZK_NOT_SERVING);
+            verify("srst", ZK_NOT_SERVING);
+            verify("wchs", ZK_NOT_SERVING);
+            verify("isro", "null");
+        } finally {
+            // reset cnxn factory
+            if (originalServerCnxnFactory == null) {
+                System.clearProperty(
+                        ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY);
+            } else {
+                System.setProperty(
+                        ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY,
+                        originalServerCnxnFactory);
+            }
+        }
+    }
+
+    private void verify(String cmd, String expected)
+            throws IOException {
+        String resp = sendRequest(cmd);
+        LOG.info("cmd " + cmd + " expected " + expected + " got " + resp);
+        Assert.assertTrue("Unexpected response: " + resp,
+                resp.contains(expected));
+    }
+
+    private String sendRequest(String cmd)
+            throws IOException {
+        return send4LetterWord(HOST, PORT, cmd);
+    }
+
+    private void startSimpleZKServer(CountDownLatch startupDelayLatch)
+            throws IOException {
+        zks = new SimpleZooKeeperServer(tmpDir, tmpDir, 3000,
+                startupDelayLatch);
+        SyncRequestProcessor.setSnapCount(100);
+        final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]);
+
+        servcnxnf = ServerCnxnFactory.createFactory(PORT, -1);
+        Thread startupThread = new Thread() {
+            public void run() {
+                try {
+                    servcnxnf.startup(zks);
+                } catch (IOException e) {
+                    LOG.error("Unexcepted exception during server startup", e);
+                    // Ignoring exception. If there is an ioexception
+                    // then one of the following assertion will fail
+                } catch (InterruptedException e) {
+                    LOG.error("Unexcepted exception during server startup", e);
+                    // Ignoring exception. If there is an interrupted exception
+                    // then one of the following assertion will fail
+                }
+            };
+        };
+        LOG.info("Starting zk server {}", HOSTPORT);
+        startupThread.start();
+    }
+
+    private static class SimpleZooKeeperServer extends ZooKeeperServer {
+        private CountDownLatch startupDelayLatch;
+        private CountDownLatch startupInvokedLatch = new CountDownLatch(1);
+        private CountDownLatch createSessionInvokedLatch = new CountDownLatch(
+                1);
+
+        public SimpleZooKeeperServer(File snapDir, File logDir, int tickTime,
+                CountDownLatch startupDelayLatch) throws IOException {
+            super(snapDir, logDir, tickTime);
+            this.startupDelayLatch = startupDelayLatch;
+        }
+
+        @Override
+        public synchronized void startup() {
+            try {
+                startupInvokedLatch.countDown();
+                // Delaying the zk server startup so that
+                // ZooKeeperServer#sessionTracker reference won't be
+                // initialized. In the defect scenario, while processing the
+                // connection request zkServer needs sessionTracker reference,
+                // but this is not yet initialized and the server is still in
+                // the startup phase, resulting in NPE.
+                startupDelayLatch.await();
+            } catch (InterruptedException e) {
+                Assert.fail(
+                        "Unexpected InterruptedException while startinng up!");
+            }
+            super.startup();
+        }
+
+        @Override
+        long createSession(ServerCnxn cnxn, byte[] passwd, int timeout) {
+            createSessionInvokedLatch.countDown();
+            return super.createSession(cnxn, passwd, timeout);
+        }
+
+        boolean waitForStartupInvocation(long timeout)
+                throws InterruptedException {
+            return startupInvokedLatch.await(timeout, TimeUnit.SECONDS);
+        }
+
+        boolean waitForSessionCreation(long timeout)
+                throws InterruptedException {
+            return createSessionInvokedLatch.await(timeout, TimeUnit.SECONDS);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java
index 831d3ed..a82a728 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java
@@ -87,7 +87,7 @@ public class CnxManagerTest extends ZKTestCase {
         public void run(){
             try {
                 QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[0], peerTmpdir[0], peerClientPort[0], 3, 0, 1000, 2, 2);
-                QuorumCnxManager cnxManager = new QuorumCnxManager(peer);
+                QuorumCnxManager cnxManager = peer.createCnxnManager();
                 QuorumCnxManager.Listener listener = cnxManager.listener;
                 if(listener != null){
                     listener.start();
@@ -131,7 +131,7 @@ public class CnxManagerTest extends ZKTestCase {
         thread.start();
         
         QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2);
-        QuorumCnxManager cnxManager = new QuorumCnxManager(peer);
+        QuorumCnxManager cnxManager = peer.createCnxnManager();
         QuorumCnxManager.Listener listener = cnxManager.listener;
         if(listener != null){
             listener.start();
@@ -175,7 +175,7 @@ public class CnxManagerTest extends ZKTestCase {
         peerTmpdir[2] = ClientBase.createTmpDir();
     
         QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2);
-        QuorumCnxManager cnxManager = new QuorumCnxManager(peer);
+        QuorumCnxManager cnxManager = peer.createCnxnManager();
         QuorumCnxManager.Listener listener = cnxManager.listener;
         if(listener != null){
             listener.start();
@@ -202,7 +202,7 @@ public class CnxManagerTest extends ZKTestCase {
     @Test
     public void testCnxManagerSpinLock() throws Exception {               
         QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2);
-        QuorumCnxManager cnxManager = new QuorumCnxManager(peer);
+        QuorumCnxManager cnxManager = peer.createCnxnManager();
         QuorumCnxManager.Listener listener = cnxManager.listener;
         if(listener != null){
             listener.start();
@@ -258,7 +258,10 @@ public class CnxManagerTest extends ZKTestCase {
     class TestCnxManager extends QuorumCnxManager {
 
         TestCnxManager(QuorumPeer self) {
-            super(self);
+            super(self.getId(), self.getView(), self.authServer,
+                    self.authLearner, self.tickTime * self.syncLimit,
+                    self.getQuorumListenOnAllIPs(),
+                    self.quorumCnxnThreadsSize, false);
         }
         
         boolean senderWorkerMapContains(Long l){
@@ -288,7 +291,7 @@ public class CnxManagerTest extends ZKTestCase {
      */
     @Test
     public void testCnxFromFutureVersion() throws Exception {               
-        QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2);
+        QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 20);
         TestCnxManager cnxManager = new TestCnxManager(peer);
         QuorumCnxManager.Listener listener = cnxManager.listener;
         if(listener != null){
@@ -359,7 +362,7 @@ public class CnxManagerTest extends ZKTestCase {
     @Test
     public void testSocketTimeout() throws Exception {
         QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 2000, 2, 2);
-        QuorumCnxManager cnxManager = new QuorumCnxManager(peer);
+        QuorumCnxManager cnxManager = peer.createCnxnManager();
         QuorumCnxManager.Listener listener = cnxManager.listener;
         if(listener != null){
             listener.start();
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java
index c1259d1..c0ab3ea 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java
@@ -113,7 +113,7 @@ public class FLEBackwardElectionRoundTest extends ZKTestCase {
          * Start mock server 1
          */
         QuorumPeer mockPeer = new QuorumPeer(peers, tmpdir[1], tmpdir[1], port[1], 3, 1, 1000, 2, 2);
-        cnxManagers[0] = new QuorumCnxManager(mockPeer);
+        cnxManagers[0] = mockPeer.createCnxnManager();
         QuorumCnxManager.Listener listener = cnxManagers[0].listener;
         listener.start();
 
@@ -124,7 +124,7 @@ public class FLEBackwardElectionRoundTest extends ZKTestCase {
          * Start mock server 2
          */
         mockPeer = new QuorumPeer(peers, tmpdir[2], tmpdir[2], port[2], 3, 2, 1000, 2, 2);
-        cnxManagers[1] = new QuorumCnxManager(mockPeer);
+        cnxManagers[1] = mockPeer.createCnxnManager();
         listener = cnxManagers[1].listener;
         listener.start();
 
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java
index 72e4fc9..f1c04ca 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java
@@ -288,7 +288,7 @@ public class FLECompatibilityTest extends ZKTestCase {
         
         QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2);
         peer.setPeerState(ServerState.LOOKING);
-        QuorumCnxManager mng = new QuorumCnxManager(peer);
+        QuorumCnxManager mng = peer.createCnxnManager();
         
         /*
          * Check that it generates an internal notification correctly
@@ -325,7 +325,7 @@ public class FLECompatibilityTest extends ZKTestCase {
 
         QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2);
         peer.setPeerState(ServerState.LOOKING);
-        QuorumCnxManager mng = new QuorumCnxManager(peer);
+        QuorumCnxManager mng = peer.createCnxnManager();
         
         /*
          * Check that it generates an internal notification correctly
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java
index a4c0cb0..3d4a02c 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java
@@ -90,7 +90,7 @@ public class FLEDontCareTest {
 
     @Test
     public void testDontCare() {
-        MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer));
+        MockFLE fle = new MockFLE(peer, peer.createCnxnManager());
 
         HashMap<Long, Vote> votes = new HashMap<Long, Vote>();
         votes.put(0L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 2, ServerState.FOLLOWING));
@@ -104,7 +104,7 @@ public class FLEDontCareTest {
 
     @Test
     public void testDontCareVersion() {
-        MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer));
+        MockFLE fle = new MockFLE(peer, peer.createCnxnManager());
 
         HashMap<Long, Vote> votes = new HashMap<Long, Vote>();
         votes.put(0L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.FOLLOWING));
@@ -118,7 +118,7 @@ public class FLEDontCareTest {
 
     @Test
     public void testLookingNormal() {
-        MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer));
+        MockFLE fle = new MockFLE(peer, peer.createCnxnManager());
 
         HashMap<Long, Vote> votes = new HashMap<Long, Vote>();
         votes.put(0L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING));
@@ -132,7 +132,7 @@ public class FLEDontCareTest {
 
     @Test
     public void testLookingDiffRounds() {
-        MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer));
+        MockFLE fle = new MockFLE(peer, peer.createCnxnManager());
 
         HashMap<Long, Vote> votes = new HashMap<Long, Vote>();
         votes.put(0L, new Vote(4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.LOOKING));
@@ -188,7 +188,7 @@ public class FLEDontCareTest {
 
     @Test
     public void testOutofElection() {
-        MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer));
+        MockFLE fle = new MockFLE(peer, peer.createCnxnManager());
         HashMap<Long,Vote> outofelection = new HashMap<Long,Vote>();
 
         /*
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java
index 39a53ca..190785c 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java
@@ -101,7 +101,7 @@ public class FLELostMessageTest extends ZKTestCase {
          * Create an instance of the connection manager
          */
         QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2);
-        cnxManager = new QuorumCnxManager(peer);
+        cnxManager = peer.createCnxnManager();
         QuorumCnxManager.Listener listener = cnxManager.listener;
         listener.start();
 
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java
index 2ae57ce..fd08d21 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java
@@ -69,8 +69,8 @@ public class LearnerTest extends ZKTestCase {
 	}
 	class SimpleLearner extends Learner {
 		SimpleLearner(FileTxnSnapLog ftsl) throws IOException {
-			self = new QuorumPeer();
-			zk = new SimpleLearnerZooKeeperServer(ftsl, self);
+            self = QuorumPeer.testingQuorumPeer();
+            zk = new SimpleLearnerZooKeeperServer(ftsl, self);
 			((SimpleLearnerZooKeeperServer)zk).learner = this;
 		}
 	}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java
new file mode 100644
index 0000000..6b6c0b4
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java
@@ -0,0 +1,925 @@
+/**
+ * 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.quorum;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.security.sasl.SaslException;
+
+import org.apache.jute.BinaryOutputArchive;
+import org.apache.zookeeper.PortAssignment;
+import org.apache.zookeeper.ZKTestCase;
+import org.apache.zookeeper.server.ServerCnxn;
+import org.apache.zookeeper.server.ServerCnxnFactory;
+import org.apache.zookeeper.server.ZKDatabase;
+import org.apache.zookeeper.server.ZooKeeperServer;
+import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
+import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
+import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthLearner;
+import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthServer;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuth;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuthLearner;
+import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer;
+import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthLearner;
+import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthServer;
+import org.apache.zookeeper.server.quorum.flexible.QuorumMaj;
+import org.apache.zookeeper.test.ClientBase;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class QuorumCnxManagerTest extends ZKTestCase {
+    private static final Logger LOG = LoggerFactory.getLogger(QuorumCnxManagerTest.class);
+    private int count;
+    private HashMap<Long,QuorumServer> peers;
+    private int peerQuorumPort[];
+    private int peerClientPort[];
+    private ThreadPoolExecutor executor;
+    /**
+     * The maximum number of threads to allow in the connectionExecutors thread
+     * pool which will be used to initiate quorum server connections. Defaulting to 20.
+     * TODO: Need to tune this param.
+     */
+    private final int quorumCnxnThreadsSize = 20;
+    private Set<String> authzHosts;
+
+    private static File saslConfigFile = null;
+
+    @BeforeClass
+    public static void setupSasl() throws Exception {
+        String jaasEntries = new String(""
+                + "QuorumServer {\n"
+                + "       org.apache.zookeeper.server.auth.DigestLoginModule required\n"
+                + "       user_test=\"mypassword\";\n"
+                + "};\n"
+                + "QuorumLearner {\n"
+                + "       org.apache.zookeeper.server.auth.DigestLoginModule required\n"
+                + "       username=\"test\"\n"
+                + "       password=\"mypassword\";\n"
+                + "};\n"
+                + "QuorumLearnerInvalid {\n"
+                + "       org.apache.zookeeper.server.auth.DigestLoginModule required\n"
+                + "       username=\"test\"\n"
+                + "       password=\"invalid\";\n"
+                + "};\n");
+
+        saslConfigFile = File.createTempFile("jaas.", ".conf");
+        FileWriter fwriter = new FileWriter(saslConfigFile);
+        fwriter.write(jaasEntries);
+        fwriter.close();
+        System.setProperty("java.security.auth.login.config",
+                           saslConfigFile.getAbsolutePath());
+    }
+
+    @AfterClass
+    public static void cleanupSasl() throws Exception {
+        if (saslConfigFile != null) {
+            saslConfigFile.delete();
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        this.count = 3;
+        this.peers = new HashMap<Long,QuorumServer>(count);
+        peerQuorumPort = new int[count];
+        peerClientPort = new int[count];
+        authzHosts = new HashSet<String>();
+        for(int i = 0; i < count; i++) {
+            peerQuorumPort[i] = PortAssignment.unique();
+            peerClientPort[i] = PortAssignment.unique();
+            QuorumServer qs = new QuorumServer(i, "0.0.0.0",
+                    peerQuorumPort[i], PortAssignment.unique(), null);
+            peers.put(Long.valueOf(i), qs);
+            authzHosts.add(qs.hostname);
+        }
+        executor = new ThreadPoolExecutor(3, 10,
+                60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (executor != null) {
+            executor.shutdownNow();
+        }
+    }
+
+    @Test(timeout = 30000)
+    public void testNoAuthConnection() throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0);
+        QuorumCnxManager peer1 = createAndStartManager(1);
+
+        peer0.connectOne(1);
+        assertEventuallyConnected(peer0, 1);
+    }
+
+    @Test(timeout = 30000)
+    public void testAuthConnection() throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer",
+                                                       "QuorumLearner", true, true);
+        QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer",
+                                                       "QuorumLearner", true, true);
+        peer0.connectOne(1);
+        assertEventuallyConnected(peer0, 1);
+    }
+
+    /**
+     * Peer0 has no auth configured, Peer1 has auth configured.
+     * Peer1 connects to peer0, because null auth server sees an auth packet and connection succeeds.
+     * Peer0 connects to peer1, but connection isn't initiated because
+     * peer0's sid is lower than peer1's
+     */
+    @Test(timeout = 30000)
+    public void testClientAuthAgainstNoAuthServerWithLowerSid()
+            throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0);
+        QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer",
+                                                       "QuorumLearner", false, false);
+        peer1.connectOne(0);
+        peer0.connectOne(1);
+        assertEventuallyConnected(peer0, 1);
+    }
+
+    /**
+     * Peer0 has auth configured, Peer1 has no auth configured.
+     * Peer1 connects to peer0, but is disconnected, because peer1's sid is
+     * higher than peer0.
+     * Peer0 connects to peer1, but is disconnected, because peer1 cannot
+     * handle auth.
+     */
+    @Test(timeout = 30000)
+    public void testClientAuthAgainstNoAuthServerWithHigherSid()
+            throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer",
+                                                       "QuorumLearner", false, false);
+        QuorumCnxManager peer1 = createAndStartManager(1);
+        peer0.connectOne(1);
+        peer1.connectOne(0);
+        assertEventuallyConnected(peer0, 1);
+    }
+
+    /**
+     * No auth learner connects to a server that requires auth, when the server
+     * has a higher sid.
+     * The connection should fail in both directions.
+     */
+    @Test(timeout = 30000)
+    public void testNoAuthLearnerConnectToAuthRequiredServerWithLowerSid()
+            throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer",
+                                                       "QuorumLearner", true, true);
+        QuorumCnxManager peer1 = createAndStartManager(1);
+        peer0.connectOne(1);
+        peer1.connectOne(0);
+        assertEventuallyNotConnected(peer0, 1);
+    }
+
+    /**
+     * No auth learner connects to a server that requires auth, when the server
+     * has a higher sid.
+     * The connection should fail in both directions.
+     */
+    @Test(timeout = 30000)
+    public void testNoAuthLearnerConnectToAuthRequiredServerWithHigherSid()
+            throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0);
+        QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer",
+                                                       "QuorumLearner", true, true);
+        peer0.connectOne(1);
+        peer1.connectOne(0);
+        assertEventuallyNotConnected(peer0, 1);
+    }
+
+    /**
+     * An auth learner connects to a auth server, but the credentials are bad.
+     * The peer with the higher sid has the bad credentials.
+     * The connection will be denied.
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerBadCredToAuthRequiredServerWithLowerSid()
+            throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0,  "QuorumServer",
+                                                       "QuorumLearner", true, true);
+        QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer",
+                                                       "QuorumLearnerInvalid", true, true);
+        peer0.connectOne(1);
+        peer1.connectOne(0);
+
+        assertEventuallyNotConnected(peer0, 1);
+    }
+
+    /**
+     * An auth learner connects to a auth server, but the credentials are bad.
+     * The peer with the lower sid has the bad credentials.
+     * The connection will work, because peer1 is connecting to peer0.
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerBadCredToAuthRequiredServerWithHigherSid()
+            throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0,  "QuorumServer",
+                                                       "QuorumLearnerInvalid", true, true);
+        QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer",
+                                                       "QuorumLearner", true, true);
+        peer0.connectOne(1);
+        peer1.connectOne(0);
+        assertEventuallyConnected(peer0, 1);
+        assertEventuallyConnected(peer1, 0);
+    }
+
+    /**
+     * An auth learner connects to a auth server, but the credentials are bad.
+     * The connection should fail in both directions.
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerBadCredToNoAuthServerWithHigherSid() throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer",
+                "QuorumLearner", false, false);
+        QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer",
+                "QuorumLearnerInvalid", true, true);
+        peer1.connectOne(0);
+        assertEventuallyNotConnected(peer1, 0);
+    }
+
+    /**
+     * An auth learner connects to a auth server, but the credentials are bad.
+     * The peer with the lower sid has the bad credentials.
+     * The connection will work, because peer0 is connecting to peer1 and peer1
+     * server doesn't require sasl
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerBadCredToNoAuthServerWithLowerSid() throws Exception {
+        QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer",
+                "QuorumLearnerInvalid", true, true);
+        QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer",
+                "QuorumLearner", false, true);
+        peer0.connectOne(1);
+        assertEventuallyConnected(peer0, 1);
+        assertEventuallyConnected(peer1, 0);
+    }
+
+    /**
+     * Test verifies that the LearnerHandler should authenticate the connecting
+     * quorumpeer. Here its simulating authentication failure and it should throw
+     * SaslException
+     */
+    @Test(timeout = 30000)
+    public void testLearnerHandlerAuthFailed() throws Exception {
+        File testData = ClientBase.createTmpDir();
+        Socket leaderSocket = getSocketPair();
+        File tmpDir = File.createTempFile("test", ".dir", testData);
+        tmpDir.delete();
+        tmpDir.mkdir();
+        Leader leader = null;
+        QuorumPeer peer = createQuorumPeer(tmpDir, true, false, true,
+                "QuorumLearner", "QuorumServer",
+                QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE);
+        leader = createLeader(tmpDir, peer);
+        peer.leader = leader;
+
+        // authentication failed as qpserver didn't get auth packet from qpclient.
+        try {
+            new LearnerHandler(leaderSocket,
+                    new BufferedInputStream(leaderSocket.getInputStream()), leader);
+            Assert.fail("Must throw exception as there is an authentication failure");
+        } catch (SaslException e){
+            Assert.assertEquals("Mistakely added to learners", 0,
+                    leader.getLearners().size());
+        }
+        ClientBase.recursiveDelete(testData);
+    }
+
+    /**
+     * Test verifies that the Leader should authenticate the connecting learner
+     * quorumpeer. After the successful authentication it should add this
+     * learner to the learnerHandler list.
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerConnectsToServerWithAuthRequired()
+            throws Exception {
+        File testDataLearner = ClientBase.createTmpDir();
+        File tmpDir = File.createTempFile("test", ".dir", testDataLearner);
+        tmpDir.delete();
+        FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir);
+        QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, true, true,
+                "QuorumLearner", "QuorumServer",
+                QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE);
+        SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer);
+
+        File testDataLeader = ClientBase.createTmpDir();
+        tmpDir = File.createTempFile("test", ".dir", testDataLeader);
+        tmpDir.delete();
+        tmpDir.mkdir();
+        Leader leader = null;
+        QuorumPeer peer = createQuorumPeer(tmpDir, true, true, true, "QuorumLearner",
+                "QuorumServer",
+                QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE);
+        CountDownLatch learnerLatch = new CountDownLatch(1);
+        leader = createSimpleLeader(tmpDir, peer, learnerLatch);
+        peer.leader = leader;
+
+        startLearnerCnxAcceptorThread(leader);
+        LOG.info("Start establishing a connection with the Leader");
+        String hostname = getLeaderHostname(peer);
+        sl.connectToLeader(peer.getQuorumAddress(), hostname);
+        // wait till leader socket soTimeout period
+        Assert.assertTrue("Leader should accept the auth learner connection",
+                learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000,
+                        TimeUnit.MILLISECONDS));
+        Assert.assertEquals("Failed to added the learner", 1,
+                leader.getLearners().size());
+        ClientBase.recursiveDelete(testDataLearner);
+        ClientBase.recursiveDelete(testDataLeader);
+    }
+
+    private String getLeaderHostname(QuorumPeer peer) {
+        String hostname = null;
+        for (QuorumServer p : peer.getView().values()) {
+            if (p.id == peer.getId()) {
+                hostname = p.hostname;
+                break;
+            }
+        }
+        Assert.assertNotNull("Didn't find leader", hostname);
+        return hostname;
+    }
+
+    /**
+     * Test verifies that the Leader should authenticate the connecting learner
+     * quorumpeer. After the successful authentication it should add this
+     * learner to the learnerHandler list.
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerConnectsToServerWithAuthNotRequired()
+            throws Exception {
+        File testDataLearner = ClientBase.createTmpDir();
+        File tmpDir = File.createTempFile("test", ".dir", testDataLearner);
+        tmpDir.delete();
+        FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir);
+        QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, true, true,
+                "QuorumLearner", "QuorumServer",
+                QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE);
+        SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer);
+
+        File testDataLeader = ClientBase.createTmpDir();
+        tmpDir = File.createTempFile("test", ".dir", testDataLeader);
+        tmpDir.delete();
+        tmpDir.mkdir();
+        Leader leader = null;
+        QuorumPeer peer = createQuorumPeer(tmpDir, true, true, false, "QuorumLearner",
+                "QuorumServer",
+                QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE);
+        CountDownLatch learnerLatch = new CountDownLatch(1);
+        leader = createSimpleLeader(tmpDir, peer, learnerLatch);
+        peer.leader = leader;
+
+        startLearnerCnxAcceptorThread(leader);
+        LOG.info("Start establishing a connection with the Leader");
+        String hostname = getLeaderHostname(peer);
+        sl.connectToLeader(peer.getQuorumAddress(), hostname);
+        // wait till leader socket soTimeout period
+        Assert.assertTrue("Leader should accept the auth learner connection",
+                learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000,
+                        TimeUnit.MILLISECONDS));
+        Assert.assertEquals("Failed to added the learner", 1,
+                leader.getLearners().size());
+        ClientBase.recursiveDelete(testDataLearner);
+        ClientBase.recursiveDelete(testDataLeader);
+    }
+
+    private void startLearnerCnxAcceptorThread(Leader leader)
+            throws InterruptedException {
+        final CountDownLatch cnxAcceptorWatcher = new CountDownLatch(1);
+        leader.cnxAcceptor = leader.new LearnerCnxAcceptor(){
+            @Override
+            public void run() {
+                cnxAcceptorWatcher.countDown();
+                super.run();
+            }
+        };
+        leader.cnxAcceptor.start();
+        // waiting to start the thread
+        Assert.assertTrue("Failed to start leader.cnxAcceptor thread!",
+                cnxAcceptorWatcher.await(15, TimeUnit.SECONDS));
+        LOG.info("Started leader.cnxAcceptor:{} thread, state:{}",
+                leader.cnxAcceptor.getName(), leader.cnxAcceptor.getState());
+    }
+
+    /**
+     * Test verifies that the Auth enabled Learner is connecting to a Null Auth
+     * Leader server. Learner is failing to get an auth response from Null Auth
+     * Leader and fails the connection establishment.
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerConnectsToNullAuthServer()
+            throws Exception {
+        File testDataLearner = ClientBase.createTmpDir();
+        File tmpDir = File.createTempFile("test", ".dir", testDataLearner);
+        tmpDir.delete();
+        FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir);
+        QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, true, true,
+                "QuorumLearner", "QuorumServer",
+                QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE);
+        SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer);
+
+        File testDataLeader = ClientBase.createTmpDir();
+        tmpDir = File.createTempFile("test", ".dir", testDataLeader);
+        tmpDir.delete();
+        tmpDir.mkdir();
+        Leader leader = null;
+        QuorumPeer peer = createQuorumPeer(tmpDir, false, false, false,
+                "QuorumLearner", "QuorumServer",
+                QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE);
+        CountDownLatch learnerLatch = new CountDownLatch(1);
+        leader = createSimpleLeader(tmpDir, peer, learnerLatch);
+        peer.leader = leader;
+
+        startLearnerCnxAcceptorThread(leader);
+        LOG.info("Start establishing a connection with the Leader");
+
+        try {
+            String hostname = getLeaderHostname(peer);
+            sl.connectToLeader(peer.getQuorumAddress(), hostname);
+            Assert.fail("Must throw exception as server doesn't supports authentication");
+        } catch (IOException e) {
+            // expected
+            Assert.assertTrue("Leader should accept the auth learner connection",
+                    learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 500,
+                            TimeUnit.MILLISECONDS));
+        }
+
+        ClientBase.recursiveDelete(testDataLearner);
+        ClientBase.recursiveDelete(testDataLeader);
+    }
+
+    /**
+     * Test verifies that the No Auth enabled Learner is connecting to a No Auth
+     * Leader server. Learner should be able to establish a connection with
+     * Leader as auth is not required.
+     */
+    @Test(timeout = 30000)
+    public void testNoAuthLearnerConnectsToServerWithAuthNotRequired()
+            throws Exception {
+        File testDataLearner = ClientBase.createTmpDir();
+        File tmpDir = File.createTempFile("test", ".dir", testDataLearner);
+        tmpDir.delete();
+        FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir);
+        QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, false, false,
+                "QuorumLearner", "QuorumServer", "");
+        SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer);
+
+        File testDataLeader = ClientBase.createTmpDir();
+        tmpDir = File.createTempFile("test", ".dir", testDataLeader);
+        tmpDir.delete();
+        tmpDir.mkdir();
+        Leader leader = null;
+        QuorumPeer peer = createQuorumPeer(tmpDir, true, false, false, "QuorumLearner",
+                "QuorumServer",
+                QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE);
+        CountDownLatch learnerLatch = new CountDownLatch(1);
+        leader = createSimpleLeader(tmpDir, peer, learnerLatch);
+        peer.leader = leader;
+
+        startLearnerCnxAcceptorThread(leader);
+        LOG.info("Start establishing a connection with the Leader");
+        String hostname = getLeaderHostname(peer);
+        sl.connectToLeader(peer.getQuorumAddress(), hostname);
+
+        Assert.assertTrue("Leader should accept no auth learner connection",
+                learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000,
+                        TimeUnit.MILLISECONDS));
+        ClientBase.recursiveDelete(testDataLearner);
+        ClientBase.recursiveDelete(testDataLeader);
+    }
+
+    /**
+     * Test verifies that the No Auth enabled Learner is connecting to a No Auth
+     * Leader server. Learner shouldn't be able to establish a connection with
+     * Leader as auth as auth is required.
+     */
+    @Test(timeout = 30000)
+    public void testNoAuthLearnerConnectsToServerWithAuthRequired()
+            throws Exception {
+        File testDataLearner = ClientBase.createTmpDir();
+        File tmpDir = File.createTempFile("test", ".dir", testDataLearner);
+        tmpDir.delete();
+        FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir);
+        QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, false, false,
+                "QuorumLearner", "QuorumServer", "");
+        SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer);
+
+        File testDataLeader = ClientBase.createTmpDir();
+        tmpDir = File.createTempFile("test", ".dir", testDataLeader);
+        tmpDir.delete();
+        tmpDir.mkdir();
+        Leader leader = null;
+        QuorumPeer peer = createQuorumPeer(tmpDir, true, true, true, "QuorumLearner",
+                "QuorumServer",
+                QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE);
+        CountDownLatch learnerLatch = new CountDownLatch(1);
+        leader = createSimpleLeader(tmpDir, peer, learnerLatch);
+        peer.leader = leader;
+
+        startLearnerCnxAcceptorThread(leader);
+        LOG.info("Start establishing a connection with the Leader");
+        String hostname = getLeaderHostname(peer);
+        sl.connectToLeader(peer.getQuorumAddress(), hostname);
+        Assert.assertFalse("Leader shouldn't accept no auth learner connection",
+                learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000,
+                        TimeUnit.MILLISECONDS));
+        ClientBase.recursiveDelete(testDataLearner);
+        ClientBase.recursiveDelete(testDataLeader);
+    }
+
+    /**
+     * Test verifies that the No Auth enabled Learner is connecting to a No Auth
+     * Leader server. Learner should be able to establish a connection with
+     * Leader as auth is not required.
+     */
+    @Test(timeout = 30000)
+    public void testNoAuthLearnerConnectsToNullAuthServer()
+            throws Exception {
+        File testDataLearner = ClientBase.createTmpDir();
+        File tmpDir = File.createTempFile("test", ".dir", testDataLearner);
+        tmpDir.delete();
+        FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir);
+        QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, false, false,
+                "QuorumLearner", "QuorumServer", "");
+        SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer);
+
+        File testDataLeader = ClientBase.createTmpDir();
+        tmpDir = File.createTempFile("test", ".dir", testDataLeader);
+        tmpDir.delete();
+        tmpDir.mkdir();
+        Leader leader = null;
+        QuorumPeer peer = createQuorumPeer(tmpDir, false, false, false, "", "",
+                "");
+        CountDownLatch learnerLatch = new CountDownLatch(1);
+        leader = createSimpleLeader(tmpDir, peer, learnerLatch);
+        peer.leader = leader;
+
+        startLearnerCnxAcceptorThread(leader);
+        LOG.info("Start establishing a connection with the Leader");
+        String hostname = getLeaderHostname(peer);
+        sl.connectToLeader(peer.getQuorumAddress(), hostname);
+
+        Assert.assertTrue("Leader should accept no auth learner connection",
+                learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000,
+                        TimeUnit.MILLISECONDS));
+        ClientBase.recursiveDelete(testDataLearner);
+        ClientBase.recursiveDelete(testDataLeader);
+    }
+
+    /**
+     * SaslQuorumAuthServer throws exception on receiving an invalid quorum
+     * auth packet.
+     */
+    @Test(timeout = 30000)
+    public void testSaslQuorumAuthServerWithInvalidQuorumAuthPacket()
+            throws Exception {
+        Socket socket = getSocketPair();
+        DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
+        BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout);
+        BinaryOutputArchive boa = BinaryOutputArchive
+                .getArchive(bufferedOutput);
+        QuorumAuthPacket authPacket = QuorumAuth
+                .createPacket(QuorumAuth.Status.IN_PROGRESS, null);
+        authPacket.setMagic(Long.MAX_VALUE); // invalid magic number
+        boa.writeRecord(authPacket, null);
+        bufferedOutput.flush();
+        QuorumAuthServer authServer = new SaslQuorumAuthServer(true,
+                "QuorumServer", authzHosts);
+        BufferedInputStream is = new BufferedInputStream(
+                socket.getInputStream());
+        try {
+            authServer.authenticate(socket, new DataInputStream(is));
+            Assert.fail("Must throw exception as QuorumAuthPacket is invalid");
+        } catch (SaslException e) {
+            // expected
+        }
+    }
+
+    /**
+     * NullQuorumAuthServer should return true when no auth quorum packet
+     * received and timed out.
+     */
+    @Test(timeout = 30000)
+    public void testNullQuorumAuthServerShouldReturnTrue()
+            throws Exception {
+        Socket socket = getSocketPair();
+        QuorumAuthServer authServer = new NullQuorumAuthServer();
+        BufferedInputStream is = new BufferedInputStream(
+                socket.getInputStream());
+        // It will throw exception and fail the
+        // test if any unexpected error. Not adding any extra assertion.
+        authServer.authenticate(socket, new DataInputStream(is));
+    }
+
+    /**
+     * NullQuorumAuthServer should return true on receiving a valid quorum auth
+     * packet.
+     */
+    @Test(timeout = 30000)
+    public void testNullQuorumAuthServerWithValidQuorumAuthPacket()
+            throws Exception {
+        Socket socket = getSocketPair();
+        DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
+        BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout);
+        BinaryOutputArchive boa = BinaryOutputArchive
+                .getArchive(bufferedOutput);
+        QuorumAuthPacket authPacket = QuorumAuth
+                .createPacket(QuorumAuth.Status.IN_PROGRESS, null);
+        boa.writeRecord(authPacket, null);
+        bufferedOutput.flush();
+        QuorumAuthServer authServer = new NullQuorumAuthServer();
+        BufferedInputStream is = new BufferedInputStream(
+                socket.getInputStream());
+        // It will throw exception and fail the
+        // test if any unexpected error. Not adding any extra assertion.
+        authServer.authenticate(socket, new DataInputStream(is));
+    }
+
+    private QuorumCnxManager createAndStartManager(long sid) {
+        QuorumCnxManager peer = new QuorumCnxManager(sid, peers,
+                new NullQuorumAuthServer(), new NullQuorumAuthLearner(), 10000,
+                false, quorumCnxnThreadsSize, false);
+        executor.submit(peer.listener);
+        InetSocketAddress electionAddr = peer.view.get(sid).electionAddr;
+        waitForElectionAddrBinding(electionAddr, 15);
+        return peer;
+    }
+
+    private QuorumCnxManager createAndStartManager(long sid,
+                                                   String serverLoginContext,
+                                                   String learnerLoginContext,
+                                                   boolean serverRequireSasl,
+                                                   boolean learnerRequireSasl)
+            throws Exception {
+        QuorumAuthLearner authClient = new SaslQuorumAuthLearner(learnerRequireSasl,
+                "NOT_USING_KRB_PRINCIPAL", learnerLoginContext);
+        QuorumAuthServer authServer = new SaslQuorumAuthServer(serverRequireSasl,
+                serverLoginContext, authzHosts);
+        QuorumCnxManager peer = new QuorumCnxManager(sid, peers,
+                authServer, authClient, 10000, false, quorumCnxnThreadsSize, true);
+        executor.submit(peer.listener);
+        InetSocketAddress electionAddr = peer.view.get(sid).electionAddr;
+        waitForElectionAddrBinding(electionAddr, 15);
+        return peer;
+    }
+
+    private void waitForElectionAddrBinding(InetSocketAddress electionAddr,
+            int retries) {
+        boolean success = false;
+        while (retries > 0) {
+            Socket sock = new Socket();
+            try {
+                sock.setTcpNoDelay(true);
+                sock.setSoTimeout(5000);
+                sock.connect(electionAddr, 5000);
+                success = true;
+            } catch (IOException e) {
+                LOG.error("IOException while checking election addr", e);
+            } finally {
+                cleanup(sock);
+            }
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+            retries--;
+        }
+        Assert.assertTrue("Did not connect to election port", success);
+    }
+
+    private void cleanup(Socket sock) {
+        try {
+            sock.close();
+        } catch (IOException ie) {
+            LOG.error("Exception while closing socket", ie);
+        }
+    }
+
+    private void assertEventuallyConnected(QuorumCnxManager peer, long sid)
+            throws Exception {
+        for (int i = 0; i < 20 && !peer.connectedToPeer(sid); i++) {
+            Thread.sleep(1000);
+        }
+        Assert.assertTrue("Not connected to peer", peer.connectedToPeer(sid));
+    }
+
+    private void assertEventuallyNotConnected(QuorumCnxManager peer, long sid)
+            throws Exception {
+        for (int i = 0; i < 3 && !peer.connectedToPeer(sid); i++) {
+            Thread.sleep(1000);
+        }
+        Assert.assertFalse("Connected to peer (shouldn't be)",
+                           peer.connectedToPeer(sid));
+    }
+
+    private QuorumPeer createQuorumPeer(File tmpDir,
+            boolean isQuorumAuthEnabled, boolean isQuorumLearnerAuthRequired,
+            boolean isQuorumServerAuthRequired, String quorumLearnerLoginContext,
+            String quorumServerLoginContext, String quorumServicePrincipal)
+                    throws IOException, FileNotFoundException {
+        QuorumPeer peer = QuorumPeer.testingQuorumPeer();
+        peer.syncLimit = 2;
+        peer.initLimit = 2;
+        peer.tickTime = 2000;
+        peer.quorumPeers = new HashMap<Long, QuorumServer>();
+        peer.quorumPeers.put(0L,
+                new QuorumServer(0, "0.0.0.0", PortAssignment.unique(), null, null));
+        peer.quorumPeers.put(1L,
+                new QuorumServer(1, "0.0.0.0", PortAssignment.unique(), null, null));
+        peer.setQuorumVerifier(new QuorumMaj(3));
+        peer.setCnxnFactory(new NullServerCnxnFactory());
+        // auth
+        if (isQuorumAuthEnabled) {
+            peer.authServer = new SaslQuorumAuthServer(
+                    isQuorumServerAuthRequired, quorumServerLoginContext, authzHosts);
+            peer.authLearner = new SaslQuorumAuthLearner(
+                    isQuorumLearnerAuthRequired, quorumServicePrincipal,
+                    quorumLearnerLoginContext);
+        }
+        File version2 = new File(tmpDir, "version-2");
+        version2.mkdir();
+        FileOutputStream fos;
+        fos = new FileOutputStream(new File(version2, "currentEpoch"));
+        fos.write("0\n".getBytes());
+        fos.close();
+        fos = new FileOutputStream(new File(version2, "acceptedEpoch"));
+        fos.write("0\n".getBytes());
+        fos.close();
+        return peer;
+    }
+
+    private static final class NullServerCnxnFactory extends ServerCnxnFactory {
+        public void startup(ZooKeeperServer zkServer)
+                throws IOException, InterruptedException {
+        }
+
+        public void start() {
+        }
+
+        public void shutdown() {
+        }
+
+        public void setMaxClientCnxnsPerHost(int max) {
+        }
+
+        public void join() throws InterruptedException {
+        }
+
+        public int getMaxClientCnxnsPerHost() {
+            return 0;
+        }
+
+        public int getLocalPort() {
+            return 0;
+        }
+
+        public InetSocketAddress getLocalAddress() {
+            return null;
+        }
+
+        public Iterable<ServerCnxn> getConnections() {
+            return null;
+        }
+
+        public void configure(InetSocketAddress addr, int maxClientCnxns)
+                throws IOException {
+        }
+
+        public void closeSession(long sessionId) {
+        }
+
+        public void closeAll() {
+        }
+
+        @Override
+        public int getNumAliveConnections() {
+            return 0;
+        }
+    }
+
+    private static Socket getSocketPair() throws IOException {
+        ServerSocket ss = new ServerSocket();
+        ss.bind(null);
+        InetSocketAddress endPoint = (InetSocketAddress) ss
+                .getLocalSocketAddress();
+        Socket s = new Socket(endPoint.getAddress(), endPoint.getPort());
+        s.setSoTimeout(5000);
+        return s;
+    }
+
+    private Leader createLeader(File tmpDir, QuorumPeer peer) throws IOException,
+                    NoSuchFieldException, IllegalAccessException {
+        LeaderZooKeeperServer zk = prepareLeader(tmpDir, peer);
+        return new Leader(peer, zk);
+    }
+
+    private Leader createSimpleLeader(File tmpDir, QuorumPeer peer,
+            CountDownLatch learnerLatch) throws IOException,
+                    NoSuchFieldException, IllegalAccessException {
+        LeaderZooKeeperServer zk = prepareLeader(tmpDir, peer);
+        return new SimpleLeader(peer, zk, learnerLatch);
+    }
+
+    class SimpleLeader extends Leader {
+        final CountDownLatch learnerLatch;
+
+        SimpleLeader(QuorumPeer self, LeaderZooKeeperServer zk,
+                CountDownLatch latch) throws IOException {
+            super(self, zk);
+            this.learnerLatch = latch;
+        }
+
+        @Override
+        void addLearnerHandler(LearnerHandler learner) {
+            super.addLearnerHandler(learner);
+            learnerLatch.countDown();
+        }
+    }
+
+    private LeaderZooKeeperServer prepareLeader(File tmpDir, QuorumPeer peer)
+            throws IOException, NoSuchFieldException, IllegalAccessException {
+        FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir);
+        peer.setTxnFactory(logFactory);
+        Field addrField = peer.getClass().getDeclaredField("myQuorumAddr");
+        addrField.setAccessible(true);
+        addrField.set(peer, new InetSocketAddress(PortAssignment.unique()));
+        ZKDatabase zkDb = new ZKDatabase(logFactory);
+        LeaderZooKeeperServer zk = new LeaderZooKeeperServer(logFactory, peer,
+                new ZooKeeperServer.BasicDataTreeBuilder(), zkDb);
+        return zk;
+    }
+
+    class SimpleLearnerZooKeeperServer extends LearnerZooKeeperServer {
+        boolean startupCalled;
+
+        public SimpleLearnerZooKeeperServer(FileTxnSnapLog ftsl,
+                QuorumPeer self) throws IOException {
+            super(ftsl, 2000, 2000, 2000, null, new ZKDatabase(ftsl), self);
+        }
+
+        Learner learner;
+
+        @Override
+        public Learner getLearner() {
+            return learner;
+        }
+
+        @Override
+        public void startup() {
+            startupCalled = true;
+        }
+    }
+
+    class SimpleLearner extends Learner {
+        SimpleLearner(FileTxnSnapLog ftsl, QuorumPeer learner)
+                throws IOException {
+            self = learner;
+            zk = new SimpleLearnerZooKeeperServer(ftsl, self);
+            ((SimpleLearnerZooKeeperServer) zk).learner = this;
+        }
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java
index 85817b2..6d5eb47 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java
@@ -24,6 +24,10 @@ package org.apache.zookeeper.server.quorum;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
 import org.apache.zookeeper.WatchedEvent;
@@ -42,6 +46,8 @@ public class QuorumPeerTestBase extends ZKTestCase implements Watcher {
     protected static final Logger LOG = LoggerFactory
             .getLogger(QuorumPeerTestBase.class);
 
+    public static final int TIMEOUT = 5000;
+
     public void process(WatchedEvent event) {
         // ignore for this test
     }
@@ -60,20 +66,29 @@ public class QuorumPeerTestBase extends ZKTestCase implements Watcher {
         volatile TestQPMain main;
         final File dataDir;
         CountDownLatch mainFailed;
-
-        public MainThread(int myid, int clientPort, String quorumCfgSection)
-                throws IOException {
-            File tmpDir = ClientBase.createTmpDir();
-            LOG.info("id = " + myid + " tmpDir = " + tmpDir + " clientPort = "
+        File baseDir;
+        private final int myid;
+        private final int clientPort;
+        private final String quorumCfgSection;
+        private final Map<String, String> otherConfigs;
+
+        public MainThread(int myid, int clientPort, String quorumCfgSection,
+                Map<String, String> otherConfigs) throws IOException {
+            baseDir = ClientBase.createTmpDir();
+            this.myid = myid;
+            this.clientPort = clientPort;
+            this.quorumCfgSection = quorumCfgSection;
+            this.otherConfigs = otherConfigs;
+            LOG.info("id = " + myid + " tmpDir = " + baseDir + " clientPort = "
                     + clientPort);
-            confFile = new File(tmpDir, "zoo.cfg");
+            confFile = new File(baseDir, "zoo.cfg");
 
             FileWriter fwriter = new FileWriter(confFile);
             fwriter.write("tickTime=4000\n");
             fwriter.write("initLimit=10\n");
             fwriter.write("syncLimit=5\n");
 
-            dataDir = new File(tmpDir, "data");
+            dataDir = new File(baseDir, "data");
             if (!dataDir.mkdir()) {
                 throw new IOException("Unable to mkdir " + dataDir);
             }
@@ -87,6 +102,13 @@ public class QuorumPeerTestBase extends ZKTestCase implements Watcher {
             fwriter.write("dataDir=" + dir + "\n");
 
             fwriter.write("clientPort=" + clientPort + "\n");
+
+            // write extra configurations
+            Set<Entry<String, String>> entrySet = otherConfigs.entrySet();
+            for (Entry<String, String> entry : entrySet) {
+                fwriter.write(entry.getKey() + "=" + entry.getValue() + "\n");
+            }
+
             fwriter.write(quorumCfgSection + "\n");
             fwriter.flush();
             fwriter.close();
@@ -98,6 +120,12 @@ public class QuorumPeerTestBase extends ZKTestCase implements Watcher {
             fwriter.close();
         }
 
+        public MainThread(int myid, int clientPort, String quorumCfgSection)
+                throws IOException {
+            this(myid, clientPort, quorumCfgSection,
+                    new HashMap<String, String>());
+        }
+
         Thread currentThread;
 
         synchronized public void start() {
@@ -151,5 +179,28 @@ public class QuorumPeerTestBase extends ZKTestCase implements Watcher {
             return main.quorumPeer;
         }
 
+        public void deleteBaseDir() {
+            ClientBase.recursiveDelete(baseDir);
+        }
+
+        public int getMyid() {
+            return myid;
+        }
+
+        public int getClientPort() {
+            return clientPort;
+        }
+
+        public String getQuorumCfgSection() {
+            return quorumCfgSection;
+        }
+
+        public Map<String, String> getOtherConfigs() {
+            return otherConfigs;
+        }
+
+        public File getConfFile() {
+            return confFile;
+        }
     }
 }
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java
index ab8ce42..3ed6097 100644
--- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java
+++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java
@@ -19,7 +19,11 @@
 package org.apache.zookeeper.server.quorum;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -367,7 +371,9 @@ public class Zab1_0Test {
                 Thread.sleep(20);
             }
             
-            LearnerHandler lh = new LearnerHandler(leaderSocket, leader);
+            LearnerHandler lh = new LearnerHandler(leaderSocket,
+                    new BufferedInputStream(leaderSocket.getInputStream()),
+                    leader);
             lh.start();
             leaderSocket.setSoTimeout(4000);
 
@@ -435,8 +441,10 @@ public class Zab1_0Test {
             while(leader.cnxAcceptor == null || !leader.cnxAcceptor.isAlive()) {
                 Thread.sleep(20);
             }
-            
-            LearnerHandler lh = new LearnerHandler(leaderSocket, leader);
+
+            LearnerHandler lh = new LearnerHandler(leaderSocket,
+                    new BufferedInputStream(leaderSocket.getInputStream()),
+                    leader);
             lh.start();
             leaderSocket.setSoTimeout(4000);
 
@@ -458,7 +466,6 @@ public class Zab1_0Test {
         }
     }
     
-    
     public void testFollowerConversation(FollowerConversation conversation) throws Exception {
         File tmpDir = File.createTempFile("test", "dir", testData);
         tmpDir.delete();
@@ -473,7 +480,9 @@ public class Zab1_0Test {
             
             ServerSocket ss = new ServerSocket();
             ss.bind(null);
-            follower.setLeaderSocketAddress((InetSocketAddress)ss.getLocalSocketAddress());
+            QuorumServer leaderQS = new QuorumServer(1,
+                    (InetSocketAddress) ss.getLocalSocketAddress());
+            follower.setLeaderQuorumServer(leaderQS);
             final Follower followerForThread = follower;
             
             followerThread = new Thread() {
@@ -526,7 +535,9 @@ public class Zab1_0Test {
 
             ServerSocket ss = new ServerSocket();
             ss.bind(null);
-            observer.setLeaderSocketAddress((InetSocketAddress)ss.getLocalSocketAddress());
+            QuorumServer leaderQS = new QuorumServer(1,
+                    (InetSocketAddress) ss.getLocalSocketAddress());
+            observer.setLeaderQuorumServer(leaderQS);
             final Observer observerForThread = observer;
 
             observerThread = new Thread() {
@@ -637,6 +648,8 @@ public class Zab1_0Test {
                 tmpDir.mkdir();
                 File logDir = f.fzk.getTxnLogFactory().getDataDir().getParentFile();
                 File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile();
+                //Spy on ZK so we can check if a snapshot happened or not.
+                f.zk = spy(f.zk);
                 try {
                     Assert.assertEquals(0, f.self.getAcceptedEpoch());
                     Assert.assertEquals(0, f.self.getCurrentEpoch());
@@ -679,6 +692,10 @@ public class Zab1_0Test {
                     oa.writeRecord(qp, null);
                     zkDb.serializeSnapshot(oa);
                     oa.writeString("BenWasHere", null);
+                    Thread.sleep(10); //Give it some time to process the snap
+                    //No Snapshot taken yet, the SNAP was applied in memory
+                    verify(f.zk, never()).takeSnapshot();
+
                     qp.setType(Leader.NEWLEADER);
                     qp.setZxid(ZxidUtils.makeZxid(1, 0));
                     oa.writeRecord(qp, null);
@@ -689,7 +706,8 @@ public class Zab1_0Test {
                     Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid());
                     Assert.assertEquals(1, f.self.getAcceptedEpoch());
                     Assert.assertEquals(1, f.self.getCurrentEpoch());
-                    
+                    //Make sure that we did take the snapshot now
+                    verify(f.zk).takeSnapshot();
                     Assert.assertEquals(firstZxid, f.fzk.getLastProcessedZxid());
                     
                     // Make sure the data was recorded in the filesystem ok
@@ -765,6 +783,8 @@ public class Zab1_0Test {
                 tmpDir.mkdir();
                 File logDir = f.fzk.getTxnLogFactory().getDataDir().getParentFile();
                 File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile();
+                //Spy on ZK so we can check if a snapshot happened or not.
+                f.zk = spy(f.zk);
                 try {
                     Assert.assertEquals(0, f.self.getAcceptedEpoch());
                     Assert.assertEquals(0, f.self.getCurrentEpoch());
@@ -831,13 +851,28 @@ public class Zab1_0Test {
                     Assert.assertEquals(1, f.self.getAcceptedEpoch());
                     Assert.assertEquals(1, f.self.getCurrentEpoch());
                     
+                    //Wait for the transactions to be written out. The thread that writes them out
+                    // does not send anything back when it is done.
+                    long start = System.currentTimeMillis();
+                    while (createSessionZxid != f.fzk.getLastProcessedZxid() && (System.currentTimeMillis() - start) < 50) {
+                        Thread.sleep(1);
+                    }
+                    
                     Assert.assertEquals(createSessionZxid, f.fzk.getLastProcessedZxid());
                     
                     // Make sure the data was recorded in the filesystem ok
                     ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir));
+                    start = System.currentTimeMillis();
                     zkDb2.loadDataBase();
+                    while (zkDb2.getSessionWithTimeOuts().isEmpty() && (System.currentTimeMillis() - start) < 50) {
+                        Thread.sleep(1);
+                        zkDb2.loadDataBase();
+                    }
                     LOG.info("zkdb2 sessions:" + zkDb2.getSessions());
+                    LOG.info("zkdb2 with timeouts:" + zkDb2.getSessionWithTimeOuts());
                     Assert.assertNotNull(zkDb2.getSessionWithTimeOuts().get(4L));
+                    //Snapshot was never taken during very simple sync
+                    verify(f.zk, never()).takeSnapshot();
                 } finally {
                     recursiveDelete(tmpDir);
                 }
@@ -1338,14 +1373,14 @@ public class Zab1_0Test {
             super(self, zk);
         }
 
-        InetSocketAddress leaderAddr;
-        public void setLeaderSocketAddress(InetSocketAddress addr) {
-            leaderAddr = addr;
+        QuorumServer leaderQuorumServer;
+        public void setLeaderQuorumServer(QuorumServer quorumServer) {
+            leaderQuorumServer = quorumServer;
         }
         
         @Override
-        protected InetSocketAddress findLeader() {
-            return leaderAddr;
+        protected QuorumServer findLeader() {
+            return leaderQuorumServer;
         }
     }
     private ConversableFollower createFollower(File tmpDir, QuorumPeer peer)
@@ -1364,14 +1399,14 @@ public class Zab1_0Test {
             super(self, zk);
         }
         
-        InetSocketAddress leaderAddr;
-        public void setLeaderSocketAddress(InetSocketAddress addr) {
-            leaderAddr = addr;
+        QuorumServer leaderQuorumServer;
+        public void setLeaderQuorumServer(QuorumServer quorumServer) {
+            leaderQuorumServer = quorumServer;
         }
         
         @Override
-        protected InetSocketAddress findLeader() {
-            return leaderAddr;
+        protected QuorumServer findLeader() {
+            return leaderQuorumServer;
         }
     }
         
@@ -1389,7 +1424,7 @@ public class Zab1_0Test {
     
     private QuorumPeer createQuorumPeer(File tmpDir) throws IOException,
             FileNotFoundException {
-        QuorumPeer peer = new QuorumPeer();
+        QuorumPeer peer = QuorumPeer.testingQuorumPeer();
         peer.syncLimit = SYNC_LIMIT;
         peer.initLimit = 2;
         peer.tickTime = 2000;
@@ -1435,7 +1470,7 @@ public class Zab1_0Test {
             logFactory.append(req);
             logFactory.commit();
             ZKDatabase zkDb = new ZKDatabase(logFactory);
-            QuorumPeer peer = new QuorumPeer();
+            QuorumPeer peer = QuorumPeer.testingQuorumPeer();
             peer.setZKDatabase(zkDb);
             peer.setTxnFactory(logFactory);
             peer.getLastLoggedZxid();
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java
new file mode 100644
index 0000000..9617c70
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java
@@ -0,0 +1,120 @@
+/**
+ * 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.quorum.auth;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+
+/*
+ * This code is originally from HDFS, see the similarly named file there
+ * in case of bug fixing, history, etc.
+ *
+ * Branch : trunk
+ * Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33
+ */
+
+/**
+ * KerberosSecurityTestcase provides a base class for using MiniKdc with other
+ * test cases. KerberosSecurityTestcase starts the MiniKdc (@Before) before
+ * running tests, and stop the MiniKdc (@After) after the testcases, using
+ * default settings (working dir and kdc configurations).
+ * <p>
+ * Users can directly inherit this class and implement their own test functions
+ * using the default settings, or override functions getTestDir() and
+ * createMiniKdcConf() to provide new settings.
+ */
+public class KerberosSecurityTestcase extends QuorumAuthTestBase {
+    private static MiniKdc kdc;
+    private static File workDir;
+    private static Properties conf;
+
+    @BeforeClass
+    public static void setUpSasl() throws Exception {
+        startMiniKdc();
+    }
+
+    @AfterClass
+    public static void tearDownSasl() throws Exception {
+        stopMiniKdc();
+        FileUtils.deleteQuietly(workDir);
+    }
+
+    public static void startMiniKdc() throws Exception {
+        createTestDir();
+        createMiniKdcConf();
+
+        kdc = new MiniKdc(conf, workDir);
+        kdc.start();
+    }
+
+    /**
+     * Create a working directory, it should be the build directory. Under this
+     * directory an ApacheDS working directory will be created, this directory
+     * will be deleted when the MiniKdc stops.
+     *
+     * @throws IOException
+     */
+    public static void createTestDir() throws IOException {
+        workDir = createTmpDir(
+                new File(System.getProperty("build.test.dir", "build")));
+    }
+
+    static File createTmpDir(File parentDir) throws IOException {
+        File tmpFile = File.createTempFile("test", ".junit", parentDir);
+        // don't delete tmpFile - this ensures we don't attempt to create
+        // a tmpDir with a duplicate name
+        File tmpDir = new File(tmpFile + ".dir");
+        // never true if tmpfile does it's job
+        Assert.assertFalse(tmpDir.exists());
+        Assert.assertTrue(tmpDir.mkdirs());
+        return tmpDir;
+    }
+
+    /**
+     * Create a Kdc configuration
+     */
+    public static void createMiniKdcConf() {
+        conf = MiniKdc.createConf();
+    }
+
+    public static void stopMiniKdc() {
+        if (kdc != null) {
+            kdc.stop();
+        }
+    }
+
+    public static MiniKdc getKdc() {
+        return kdc;
+    }
+
+    public static File getWorkDir() {
+        return workDir;
+    }
+
+    public static Properties getConf() {
+        return conf;
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosTestUtils.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosTestUtils.java
new file mode 100644
index 0000000..4a75f83
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosTestUtils.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
+ *
+ *     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.quorum.auth;
+
+import java.io.File;
+import java.util.UUID;
+
+import org.apache.zookeeper.util.SecurityUtils;
+
+public class KerberosTestUtils {
+    private static String keytabFile = new File(System.getProperty("test.dir", "build"), UUID.randomUUID().toString())
+            .getAbsolutePath();
+
+    public static String getRealm() {
+        return "EXAMPLE.COM";
+    }
+
+    public static String getLearnerPrincipal() {
+        return "learner at EXAMPLE.COM";
+    }
+
+    public static String getServerPrincipal() {
+        return "zkquorum/localhost at EXAMPLE.COM";
+    }
+
+    public static String getHostLearnerPrincipal() {
+        return "learner/_HOST at EXAMPLE.COM";
+    }
+
+    public static String getHostServerPrincipal() {
+        return "zkquorum/_HOST at EXAMPLE.COM";
+    }
+
+    public static String getHostNamedLearnerPrincipal(String myHostname) {
+        return "learner/" + myHostname + "@EXAMPLE.COM";
+    }
+
+    public static String getKeytabFile() {
+        return keytabFile;
+    }
+
+    public static String replaceHostPattern(String principal) {
+        String[] components = principal.split("[/@]");
+        if (components == null || components.length < 2
+                || !components[1].equals(SecurityUtils.QUORUM_HOSTNAME_PATTERN)) {
+            return principal;
+        } else {
+            return replacePattern(components, "localhost");
+        }
+    }
+
+    public static String replacePattern(String[] components, String hostname) {
+        if (components.length == 3) {
+            return components[0] + "/" + hostname.toLowerCase() + "@"
+                    + components[2];
+        } else {
+            return components[0] + "/" + hostname.toLowerCase();
+        }
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java
new file mode 100644
index 0000000..ebe541d
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java
@@ -0,0 +1,574 @@
+/**
+ * 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.quorum.auth;
+
+import org.apache.commons.io.Charsets;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.text.StrSubstitutor;
+import org.apache.directory.api.ldap.model.schema.SchemaManager;
+import org.apache.directory.api.ldap.schemaextractor.SchemaLdifExtractor;
+import org.apache.directory.api.ldap.schemaextractor.impl.DefaultSchemaLdifExtractor;
+import org.apache.directory.api.ldap.schemaloader.LdifSchemaLoader;
+import org.apache.directory.api.ldap.schemamanager.impl.DefaultSchemaManager;
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DefaultDirectoryService;
+import org.apache.directory.server.core.api.CacheService;
+import org.apache.directory.server.core.api.DirectoryService;
+import org.apache.directory.server.core.api.InstanceLayout;
+import org.apache.directory.server.core.api.schema.SchemaPartition;
+import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
+import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
+import org.apache.directory.server.core.partition.ldif.LdifPartition;
+import org.apache.directory.server.kerberos.KerberosConfig;
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
+import org.apache.directory.server.kerberos.shared.keytab.Keytab;
+import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
+import org.apache.directory.server.protocol.shared.transport.TcpTransport;
+import org.apache.directory.server.protocol.shared.transport.UdpTransport;
+import org.apache.directory.server.xdbm.Index;
+import org.apache.directory.shared.kerberos.KerberosTime;
+import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
+import org.apache.directory.shared.kerberos.components.EncryptionKey;
+import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.ldif.LdifEntry;
+import org.apache.directory.api.ldap.model.ldif.LdifReader;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Mini KDC based on Apache Directory Server that can be embedded in testcases
+ * or used from command line as a standalone KDC.
+ * <p>
+ * <b>From within testcases:</b>
+ * <p>
+ * MiniKdc sets 2 System properties when started and un-sets them when stopped:
+ * <ul>
+ * <li>java.security.krb5.conf: set to the MiniKDC real/host/port</li>
+ * <li>sun.security.krb5.debug: set to the debug value provided in the
+ * configuration</li>
+ * </ul>
+ * Because of this, multiple MiniKdc instances cannot be started in parallel.
+ * For example, running testcases in parallel that start a KDC each. To
+ * accomplish this a single MiniKdc should be used for all testcases running
+ * in parallel.
+ * <p>
+ * MiniKdc default configuration values are:
+ * <ul>
+ *   <li>org.name=EXAMPLE (used to create the REALM)</li>
+ *   <li>org.domain=COM (used to create the REALM)</li>
+ *   <li>kdc.bind.address=localhost</li>
+ *   <li>kdc.port=0 (ephemeral port)</li>
+ *   <li>instance=DefaultKrbServer</li>
+ *   <li>max.ticket.lifetime=86400000 (1 day)</li>
+ *   <li>max.renewable.lifetime=604800000 (7 days)</li>
+ *   <li>transport=TCP</li>
+ *   <li>debug=false</li>
+ * </ul>
+ * The generated krb5.conf forces TCP connections.
+ */
+/*
+ * This code is originally from HDFS, see the file name MiniKdc there
+ * in case of bug fixing, history, etc.
+ *
+ * Branch : trunk
+ * Github Revision: 42e3a805117ff7cb054c2442f7b0e0cc54be63ad
+ */
+public class MiniKdc {
+
+    public static final String JAVA_SECURITY_KRB5_CONF =
+            "java.security.krb5.conf";
+    public static final String SUN_SECURITY_KRB5_DEBUG =
+            "sun.security.krb5.debug";
+    private static final File testData = new File(
+            System.getProperty("test.data.dir", "build/test/data"));
+
+    public static void main(String[] args) throws Exception {
+        if (args.length < 4) {
+            System.out.println("Arguments: <WORKDIR> <MINIKDCPROPERTIES> " +
+                    "<KEYTABFILE> [<PRINCIPALS>]+");
+            System.exit(1);
+        }
+        File workDir = new File(args[0]);
+        if (!workDir.exists()) {
+            throw new RuntimeException("Specified work directory does not exists: "
+                    + workDir.getAbsolutePath());
+        }
+        Properties conf = createConf();
+        File file = new File(args[1]);
+        if (!file.exists()) {
+            throw new RuntimeException("Specified configuration does not exists: "
+                    + file.getAbsolutePath());
+        }
+        Properties userConf = new Properties();
+        InputStreamReader r = null;
+        try {
+            r = new InputStreamReader(new FileInputStream(file), Charsets.UTF_8);
+            userConf.load(r);
+        } finally {
+            if (r != null) {
+                r.close();
+            }
+        }
+        for (Map.Entry<?, ?> entry : userConf.entrySet()) {
+            conf.put(entry.getKey(), entry.getValue());
+        }
+        final MiniKdc miniKdc = new MiniKdc(conf, workDir);
+        miniKdc.start();
+        File krb5conf = new File(workDir, "krb5.conf");
+        if (miniKdc.getKrb5conf().renameTo(krb5conf)) {
+            File keytabFile = new File(args[2]).getAbsoluteFile();
+            String[] principals = new String[args.length - 3];
+            System.arraycopy(args, 3, principals, 0, args.length - 3);
+            miniKdc.createPrincipal(keytabFile, principals);
+            System.out.println();
+            System.out.println("Standalone MiniKdc Running");
+            System.out.println("---------------------------------------------------");
+            System.out.println("  Realm           : " + miniKdc.getRealm());
+            System.out.println("  Running at      : " + miniKdc.getHost() + ":" +
+                    miniKdc.getHost());
+            System.out.println("  krb5conf        : " + krb5conf);
+            System.out.println();
+            System.out.println("  created keytab  : " + keytabFile);
+            System.out.println("  with principals : " + Arrays.asList(principals));
+            System.out.println();
+            System.out.println(" Do <CTRL-C> or kill <PID> to stop it");
+            System.out.println("---------------------------------------------------");
+            System.out.println();
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                @Override
+                public void run() {
+                    miniKdc.stop();
+                }
+            });
+        } else {
+            throw new RuntimeException("Cannot rename KDC's krb5conf to "
+                    + krb5conf.getAbsolutePath());
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(MiniKdc.class);
+
+    public static final String ORG_NAME = "org.name";
+    public static final String ORG_DOMAIN = "org.domain";
+    public static final String KDC_BIND_ADDRESS = "kdc.bind.address";
+    public static final String KDC_PORT = "kdc.port";
+    public static final String INSTANCE = "instance";
+    public static final String MAX_TICKET_LIFETIME = "max.ticket.lifetime";
+    public static final String MAX_RENEWABLE_LIFETIME = "max.renewable.lifetime";
+    public static final String TRANSPORT = "transport";
+    public static final String DEBUG = "debug";
+
+    private static final Set<String> PROPERTIES = new HashSet<String>();
+    private static final Properties DEFAULT_CONFIG = new Properties();
+
+    static {
+        PROPERTIES.add(ORG_NAME);
+        PROPERTIES.add(ORG_DOMAIN);
+        PROPERTIES.add(KDC_BIND_ADDRESS);
+        PROPERTIES.add(KDC_BIND_ADDRESS);
+        PROPERTIES.add(KDC_PORT);
+        PROPERTIES.add(INSTANCE);
+        PROPERTIES.add(TRANSPORT);
+        PROPERTIES.add(MAX_TICKET_LIFETIME);
+        PROPERTIES.add(MAX_RENEWABLE_LIFETIME);
+
+        DEFAULT_CONFIG.setProperty(KDC_BIND_ADDRESS, "localhost");
+        DEFAULT_CONFIG.setProperty(KDC_PORT, "0");
+        DEFAULT_CONFIG.setProperty(INSTANCE, "DefaultKrbServer");
+        DEFAULT_CONFIG.setProperty(ORG_NAME, "EXAMPLE");
+        DEFAULT_CONFIG.setProperty(ORG_DOMAIN, "COM");
+        DEFAULT_CONFIG.setProperty(TRANSPORT, "TCP");
+        DEFAULT_CONFIG.setProperty(MAX_TICKET_LIFETIME, "86400000");
+        DEFAULT_CONFIG.setProperty(MAX_RENEWABLE_LIFETIME, "604800000");
+        DEFAULT_CONFIG.setProperty(DEBUG, "true");
+    }
+
+    /**
+     * Convenience method that returns MiniKdc default configuration.
+     * <p>
+     * The returned configuration is a copy, it can be customized before using
+     * it to create a MiniKdc.
+     * @return a MiniKdc default configuration.
+     */
+    public static Properties createConf() {
+        return (Properties) DEFAULT_CONFIG.clone();
+    }
+
+    private Properties conf;
+    private DirectoryService ds;
+    private KdcServer kdc;
+    private int port;
+    private String realm;
+    private File workDir;
+    private File krb5conf;
+
+    /**
+     * Creates a MiniKdc.
+     *
+     * @param conf MiniKdc configuration.
+     * @param workDir working directory, it should be the build directory. Under
+     * this directory an ApacheDS working directory will be created, this
+     * directory will be deleted when the MiniKdc stops.
+     * @throws Exception thrown if the MiniKdc could not be created.
+     */
+    public MiniKdc(Properties conf, File workDir) throws Exception {
+        if (!conf.keySet().containsAll(PROPERTIES)) {
+            Set<String> missingProperties = new HashSet<String>(PROPERTIES);
+            missingProperties.removeAll(conf.keySet());
+            throw new IllegalArgumentException("Missing configuration properties: "
+                    + missingProperties);
+        }
+        this.workDir = new File(workDir, Long.toString(System.currentTimeMillis()));
+        if (!this.workDir.exists()
+                && !this.workDir.mkdirs()) {
+            throw new RuntimeException("Cannot create directory " + this.workDir);
+        }
+        LOG.info("Configuration:");
+        LOG.info("---------------------------------------------------------------");
+        for (Map.Entry<?, ?> entry : conf.entrySet()) {
+            LOG.info("  {}: {}", entry.getKey(), entry.getValue());
+        }
+        LOG.info("---------------------------------------------------------------");
+        this.conf = conf;
+        port = Integer.parseInt(conf.getProperty(KDC_PORT));
+        if (port == 0) {
+            ServerSocket ss = new ServerSocket(0, 1, InetAddress.getByName(conf.getProperty(KDC_BIND_ADDRESS)));
+            port = ss.getLocalPort();
+            ss.close();
+        }
+        String orgName = conf.getProperty(ORG_NAME);
+        String orgDomain = conf.getProperty(ORG_DOMAIN);
+        realm = orgName.toUpperCase(Locale.ENGLISH) + "."
+                + orgDomain.toUpperCase(Locale.ENGLISH);
+    }
+
+    /**
+     * Returns the port of the MiniKdc.
+     *
+     * @return the port of the MiniKdc.
+     */
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * Returns the host of the MiniKdc.
+     *
+     * @return the host of the MiniKdc.
+     */
+    public String getHost() {
+        return conf.getProperty(KDC_BIND_ADDRESS);
+    }
+
+    /**
+     * Returns the realm of the MiniKdc.
+     *
+     * @return the realm of the MiniKdc.
+     */
+    public String getRealm() {
+        return realm;
+    }
+
+    public File getKrb5conf() {
+        return krb5conf;
+    }
+
+    /**
+     * Starts the MiniKdc.
+     *
+     * @throws Exception thrown if the MiniKdc could not be started.
+     */
+    public synchronized void start() throws Exception {
+        if (kdc != null) {
+            throw new RuntimeException("Already started");
+        }
+        initDirectoryService();
+        initKDCServer();
+    }
+
+    private void initDirectoryService() throws Exception {
+        ds = new DefaultDirectoryService();
+        ds.setInstanceLayout(new InstanceLayout(workDir));
+
+        CacheService cacheService = new CacheService();
+        ds.setCacheService(cacheService);
+
+        // first load the schema
+        InstanceLayout instanceLayout = ds.getInstanceLayout();
+        File schemaPartitionDirectory = new File(instanceLayout.getPartitionsDirectory(), "schema");
+        SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor(instanceLayout.getPartitionsDirectory());
+        extractor.extractOrCopy();
+
+        SchemaLoader loader = new LdifSchemaLoader(schemaPartitionDirectory);
+        SchemaManager schemaManager = new DefaultSchemaManager(loader);
+        schemaManager.loadAllEnabled();
+        ds.setSchemaManager(schemaManager);
+        // Init the LdifPartition with schema
+        LdifPartition schemaLdifPartition = new LdifPartition(schemaManager);
+        schemaLdifPartition.setPartitionPath(schemaPartitionDirectory.toURI());
+
+        // The schema partition
+        SchemaPartition schemaPartition = new SchemaPartition(schemaManager);
+        schemaPartition.setWrappedPartition(schemaLdifPartition);
+        ds.setSchemaPartition(schemaPartition);
+
+        JdbmPartition systemPartition = new JdbmPartition(ds.getSchemaManager());
+        systemPartition.setId("system");
+        systemPartition.setPartitionPath(
+                new File(ds.getInstanceLayout().getPartitionsDirectory(), systemPartition.getId()).toURI());
+        systemPartition.setSuffixDn(new Dn(ServerDNConstants.SYSTEM_DN));
+        systemPartition.setSchemaManager(ds.getSchemaManager());
+        ds.setSystemPartition(systemPartition);
+
+        ds.getChangeLog().setEnabled(false);
+        ds.setDenormalizeOpAttrsEnabled(true);
+        ds.addLast(new KeyDerivationInterceptor());
+
+        // create one partition
+        String orgName = conf.getProperty(ORG_NAME).toLowerCase(Locale.ENGLISH);
+        String orgDomain = conf.getProperty(ORG_DOMAIN).toLowerCase(Locale.ENGLISH);
+
+        JdbmPartition partition = new JdbmPartition(ds.getSchemaManager());
+        partition.setId(orgName);
+        partition.setPartitionPath(new File(ds.getInstanceLayout().getPartitionsDirectory(), orgName).toURI());
+        partition.setSuffixDn(new Dn("dc=" + orgName + ",dc=" + orgDomain));
+        ds.addPartition(partition);
+        // indexes
+        Set<Index<?, ?, String>> indexedAttributes = new HashSet<Index<?, ?, String>>();
+        indexedAttributes.add(new JdbmIndex<String, Entry>("objectClass", false));
+        indexedAttributes.add(new JdbmIndex<String, Entry>("dc", false));
+        indexedAttributes.add(new JdbmIndex<String, Entry>("ou", false));
+        partition.setIndexedAttributes(indexedAttributes);
+
+        // And start the ds
+        ds.setInstanceId(conf.getProperty(INSTANCE));
+        ds.startup();
+        // context entry, after ds.startup()
+        Dn dn = new Dn("dc=" + orgName + ",dc=" + orgDomain);
+        Entry entry = ds.newEntry(dn);
+        entry.add("objectClass", "top", "domain");
+        entry.add("dc", orgName);
+        ds.getAdminSession().add(entry);
+    }
+
+    private void initKDCServer() throws Exception {
+        String orgName = conf.getProperty(ORG_NAME);
+        String orgDomain = conf.getProperty(ORG_DOMAIN);
+        String bindAddress = conf.getProperty(KDC_BIND_ADDRESS);
+        final Map<String, String> map = new HashMap<String, String>();
+        map.put("0", orgName.toLowerCase(Locale.ENGLISH));
+        map.put("1", orgDomain.toLowerCase(Locale.ENGLISH));
+        map.put("2", orgName.toUpperCase(Locale.ENGLISH));
+        map.put("3", orgDomain.toUpperCase(Locale.ENGLISH));
+        map.put("4", bindAddress);
+
+        InputStream is1 = getMinikdcResourceAsStream("minikdc.ldiff");
+
+        SchemaManager schemaManager = ds.getSchemaManager();
+        LdifReader reader = null;
+
+        try {
+            final String content = StrSubstitutor.replace(IOUtils.toString(is1), map);
+            reader = new LdifReader(new StringReader(content));
+
+            for (LdifEntry ldifEntry : reader) {
+                ds.getAdminSession().add(new DefaultEntry(schemaManager, ldifEntry.getEntry()));
+            }
+        } finally {
+            IOUtils.closeQuietly(reader);
+            IOUtils.closeQuietly(is1);
+        }
+
+        KerberosConfig kerberosConfig = new KerberosConfig();
+        kerberosConfig.setMaximumRenewableLifetime(Long.parseLong(conf.getProperty(MAX_RENEWABLE_LIFETIME)));
+        kerberosConfig.setMaximumTicketLifetime(Long.parseLong(conf.getProperty(MAX_TICKET_LIFETIME)));
+        kerberosConfig.setSearchBaseDn(String.format("dc=%s,dc=%s", orgName, orgDomain));
+        kerberosConfig.setPaEncTimestampRequired(false);
+        kdc = new KdcServer(kerberosConfig);
+        kdc.setDirectoryService(ds);
+
+        // transport
+        String transport = conf.getProperty(TRANSPORT);
+        if (transport.trim().equals("TCP")) {
+            kdc.addTransports(new TcpTransport(bindAddress, port, 3, 50));
+        } else if (transport.trim().equals("UDP")) {
+            kdc.addTransports(new UdpTransport(port));
+        } else {
+            throw new IllegalArgumentException("Invalid transport: " + transport);
+        }
+        kdc.setServiceName(conf.getProperty(INSTANCE));
+        kdc.start();
+
+        StringBuilder sb = new StringBuilder();
+        InputStream is2 = getMinikdcResourceAsStream("minikdc-krb5.conf");
+
+        BufferedReader r = null;
+
+        try {
+            r = new BufferedReader(new InputStreamReader(is2, Charsets.UTF_8));
+            String line = r.readLine();
+
+            while (line != null) {
+                sb.append(line).append("{3}");
+                line = r.readLine();
+            }
+        } finally {
+            IOUtils.closeQuietly(r);
+            IOUtils.closeQuietly(is2);
+        }
+
+        krb5conf = new File(workDir, "krb5.conf").getAbsoluteFile();
+        FileUtils.writeStringToFile(krb5conf, MessageFormat.format(sb.toString(), getRealm(), getHost(),
+                Integer.toString(getPort()), System.getProperty("line.separator")));
+        System.setProperty(JAVA_SECURITY_KRB5_CONF, krb5conf.getAbsolutePath());
+
+        System.setProperty(SUN_SECURITY_KRB5_DEBUG, conf.getProperty(DEBUG, "false"));
+
+        // refresh the config
+        Class<?> classRef;
+        if (System.getProperty("java.vendor").contains("IBM")) {
+            classRef = Class.forName("com.ibm.security.krb5.internal.Config");
+        } else {
+            classRef = Class.forName("sun.security.krb5.Config");
+        }
+        Method refreshMethod = classRef.getMethod("refresh", new Class[0]);
+        refreshMethod.invoke(classRef, new Object[0]);
+
+        LOG.info("MiniKdc listening at port: {}", getPort());
+        LOG.info("MiniKdc setting JVM krb5.conf to: {}", krb5conf.getAbsolutePath());
+    }
+
+    private InputStream getMinikdcResourceAsStream(String resourceName)
+            throws FileNotFoundException {
+        File kdcResourceFile = new File(testData, "/kerberos/" + resourceName);
+        return new FileInputStream(kdcResourceFile);
+    }
+
+    /**
+     * Stops the MiniKdc
+     */
+    public synchronized void stop() {
+        if (kdc != null) {
+            System.getProperties().remove(JAVA_SECURITY_KRB5_CONF);
+            System.getProperties().remove(SUN_SECURITY_KRB5_DEBUG);
+            kdc.stop();
+            try {
+                ds.shutdown();
+            } catch (Exception ex) {
+                LOG.error("Could not shutdown ApacheDS properly: {}", ex.toString(), ex);
+            }
+        }
+        delete(workDir);
+    }
+
+    private void delete(File f) {
+        if (f.isFile()) {
+            if (! f.delete()) {
+                LOG.warn("WARNING: cannot delete file " + f.getAbsolutePath());
+            }
+        } else {
+            for (File c: f.listFiles()) {
+                delete(c);
+            }
+            if (! f.delete()) {
+                LOG.warn("WARNING: cannot delete directory " + f.getAbsolutePath());
+            }
+        }
+    }
+
+    /**
+     * Creates a principal in the KDC with the specified user and password.
+     *
+     * @param principal principal name, do not include the domain.
+     * @param password password.
+     * @throws Exception thrown if the principal could not be created.
+     */
+    public synchronized void createPrincipal(String principal, String password) throws Exception {
+        String orgName = conf.getProperty(ORG_NAME);
+        String orgDomain = conf.getProperty(ORG_DOMAIN);
+        String baseDn = "ou=users,dc=" + orgName.toLowerCase(Locale.ENGLISH) + ",dc="
+                + orgDomain.toLowerCase(Locale.ENGLISH);
+        String content = "dn: uid=" + principal + "," + baseDn + "\n" + "objectClass: top\n" + "objectClass: person\n"
+                + "objectClass: inetOrgPerson\n" + "objectClass: krb5principal\n" + "objectClass: krb5kdcentry\n"
+                + "cn: " + principal + "\n" + "sn: " + principal + "\n" + "uid: " + principal + "\n" + "userPassword: "
+                + password + "\n" + "krb5PrincipalName: " + principal + "@" + getRealm() + "\n"
+                + "krb5KeyVersionNumber: 0";
+
+        for (LdifEntry ldifEntry : new LdifReader(new StringReader(content))) {
+            ds.getAdminSession().add(new DefaultEntry(ds.getSchemaManager(), ldifEntry.getEntry()));
+        }
+    }
+
+    /**
+     * Creates multiple principals in the KDC and adds them to a keytab file.
+     *
+     * @param keytabFile keytab file to add the created principals.
+     * @param principals principals to add to the KDC, do not include the domain.
+     * @throws Exception thrown if the principals or the keytab file could not be
+     * created.
+     */
+    public synchronized void createPrincipal(File keytabFile,
+                                             String ... principals)
+            throws Exception {
+        String generatedPassword = UUID.randomUUID().toString();
+        Keytab keytab = new Keytab();
+        List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
+        for (String principal : principals) {
+            createPrincipal(principal, generatedPassword);
+            principal = principal + "@" + getRealm();
+            KerberosTime timestamp = new KerberosTime();
+            for (Map.Entry<EncryptionType, EncryptionKey> entry : KerberosKeyFactory
+                    .getKerberosKeys(principal, generatedPassword).entrySet()) {
+                EncryptionKey ekey = entry.getValue();
+                byte keyVersion = (byte) ekey.getKeyVersion();
+                entries.add(new KeytabEntry(principal, 1L, timestamp, keyVersion, ekey));
+            }
+        }
+        keytab.setEntries(entries);
+        keytab.write(keytabFile);
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java
new file mode 100644
index 0000000..196d8be
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java
@@ -0,0 +1,184 @@
+/**
+ * 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.quorum.auth;
+
+import org.apache.directory.server.kerberos.shared.keytab.Keytab;
+import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.security.auth.Subject;
+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 java.io.File;
+import java.security.Principal;
+import java.util.Set;
+import java.util.Map;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Arrays;
+
+/*
+ * This code is originally from HDFS, see the file name TestMiniKdc there
+ * in case of bug fixing, history, etc.
+ *
+ * Branch : trunk
+ * Github Revision: 916140604ffef59466ba30832478311d3e6249bd
+ */
+public class MiniKdcTest extends KerberosSecurityTestcase {
+    private static final boolean IBM_JAVA = System.getProperty("java.vendor")
+            .contains("IBM");
+
+    @Test(timeout = 60000)
+    public void testMiniKdcStart() {
+        MiniKdc kdc = getKdc();
+        Assert.assertNotSame(0, kdc.getPort());
+    }
+
+    @Test(timeout = 60000)
+    public void testKeytabGen() throws Exception {
+        MiniKdc kdc = getKdc();
+        File workDir = getWorkDir();
+
+        kdc.createPrincipal(new File(workDir, "keytab"), "foo/bar", "bar/foo");
+        Keytab kt = Keytab.read(new File(workDir, "keytab"));
+
+        Set<String> principals = new HashSet<String>();
+        for (KeytabEntry entry : kt.getEntries()) {
+            principals.add(entry.getPrincipalName());
+        }
+        //here principals use \ instead of /
+        //because org.apache.directory.server.kerberos.shared.keytab.KeytabDecoder
+        // .getPrincipalName(IoBuffer buffer) use \\ when generates principal
+        Assert.assertEquals(new HashSet<String>(Arrays.asList(
+                "foo\\bar@" + kdc.getRealm(), "bar\\foo@" + kdc.getRealm())),
+                principals);
+      }
+
+    private static class KerberosConfiguration extends Configuration {
+        private String principal;
+        private String keytab;
+        private boolean isInitiator;
+
+        private KerberosConfiguration(String principal, File keytab,
+                boolean client) {
+            this.principal = principal;
+            this.keytab = keytab.getAbsolutePath();
+            this.isInitiator = client;
+        }
+
+        public static Configuration createClientConfig(String principal,
+                File keytab) {
+            return new KerberosConfiguration(principal, keytab, true);
+        }
+
+        public static Configuration createServerConfig(String principal,
+                File keytab) {
+            return new KerberosConfiguration(principal, keytab, false);
+        }
+
+        private static String getKrb5LoginModuleName() {
+            return System.getProperty("java.vendor").contains("IBM")
+                    ? "com.ibm.security.auth.module.Krb5LoginModule"
+                    : "com.sun.security.auth.module.Krb5LoginModule";
+        }
+
+        @Override
+        public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+            Map<String, String> options = new HashMap<String, String>();
+            options.put("principal", principal);
+            options.put("refreshKrb5Config", "true");
+            if (IBM_JAVA) {
+                options.put("useKeytab", keytab);
+                options.put("credsType", "both");
+            } else {
+                options.put("keyTab", keytab);
+                options.put("useKeyTab", "true");
+                options.put("storeKey", "true");
+                options.put("doNotPrompt", "true");
+                options.put("useTicketCache", "true");
+                options.put("renewTGT", "true");
+                options.put("isInitiator", Boolean.toString(isInitiator));
+            }
+            String ticketCache = System.getenv("KRB5CCNAME");
+            if (ticketCache != null) {
+                options.put("ticketCache", ticketCache);
+            }
+            options.put("debug", "true");
+
+            return new AppConfigurationEntry[] {
+                    new AppConfigurationEntry(getKrb5LoginModuleName(),
+                            AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                            options) };
+        }
+    }
+
+    @Test(timeout = 60000)
+    public void testKerberosLogin() throws Exception {
+        MiniKdc kdc = getKdc();
+        File workDir = getWorkDir();
+        LoginContext loginContext = null;
+        try {
+            String principal = "foo";
+            File keytab = new File(workDir, "foo.keytab");
+            kdc.createPrincipal(keytab, principal);
+
+            Set<Principal> principals = new HashSet<Principal>();
+            principals.add(new KerberosPrincipal(principal));
+
+            // client login
+            Subject subject = new Subject(false, principals,
+                    new HashSet<Object>(), new HashSet<Object>());
+            loginContext = new LoginContext("", subject, null,
+                    KerberosConfiguration.createClientConfig(principal,
+                            keytab));
+            loginContext.login();
+            subject = loginContext.getSubject();
+            Assert.assertEquals(1, subject.getPrincipals().size());
+            Assert.assertEquals(KerberosPrincipal.class,
+                    subject.getPrincipals().iterator().next().getClass());
+            Assert.assertEquals(principal + "@" + kdc.getRealm(),
+                    subject.getPrincipals().iterator().next().getName());
+            loginContext.logout();
+
+            // server login
+            subject = new Subject(false, principals, new HashSet<Object>(),
+                    new HashSet<Object>());
+            loginContext = new LoginContext("", subject, null,
+                    KerberosConfiguration.createServerConfig(principal,
+                            keytab));
+            loginContext.login();
+            subject = loginContext.getSubject();
+            Assert.assertEquals(1, subject.getPrincipals().size());
+            Assert.assertEquals(KerberosPrincipal.class,
+                    subject.getPrincipals().iterator().next().getClass());
+            Assert.assertEquals(principal + "@" + kdc.getRealm(),
+                    subject.getPrincipals().iterator().next().getName());
+            loginContext.logout();
+
+        } finally {
+            if (loginContext != null) {
+                loginContext.logout();
+            }
+        }
+    }
+
+}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java
new file mode 100644
index 0000000..219d5bc
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java
@@ -0,0 +1,248 @@
+/**
+ * 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.quorum.auth;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.zookeeper.PortAssignment;
+import org.apache.zookeeper.ZKTestCase;
+import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread;
+import org.apache.zookeeper.test.ClientBase;
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * QuorumAuthTestBase provides a base class for testing quorum peer mutual
+ * authentication using SASL mechanisms.
+ */
+public class QuorumAuthTestBase extends ZKTestCase {
+    protected static final Logger LOG = LoggerFactory.getLogger(QuorumAuthTestBase.class);
+    protected List<MainThread> mt = new ArrayList<MainThread>();
+    protected static File jaasConfigDir;
+
+    public static void setupJaasConfig(String jaasEntries) {
+        try {
+            jaasConfigDir = ClientBase.createTmpDir();
+            File saslConfFile = new File(jaasConfigDir, "jaas.conf");
+            FileWriter fwriter = new FileWriter(saslConfFile);
+            fwriter.write(jaasEntries);
+            fwriter.close();
+            System.setProperty("java.security.auth.login.config",
+                    saslConfFile.getAbsolutePath());
+        } catch (IOException ioe) {
+            LOG.error("Failed to create tmp directory to hold JAAS conf file", ioe);
+            // could not create tmp directory to hold JAAS conf file : test will
+            // fail now.
+        }
+    }
+
+    public static void cleanupJaasConfig() {
+        if (jaasConfigDir != null) {
+            FileUtils.deleteQuietly(jaasConfigDir);
+        }
+    }
+
+    protected String startQuorum(final int serverCount,
+            Map<String, String> authConfigs, int authServerCount,
+            boolean delayedServerStartup) throws IOException {
+        StringBuilder connectStr = new StringBuilder();
+        final int[] clientPorts = startQuorum(serverCount, 0, connectStr,
+                authConfigs, authServerCount, delayedServerStartup);
+        for (int i = 0; i < serverCount; i++) {
+            Assert.assertTrue("waiting for server " + i + " being up",
+                    ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i],
+                            ClientBase.CONNECTION_TIMEOUT));
+        }
+        return connectStr.toString();
+    }
+
+    /**
+     * Starts the given number of quorum servers and will wait for the quorum
+     * formation.
+     *
+     * @param serverCount
+     *            total server count includes participants + observers
+     * @param observerCount
+     *            number of observers
+     * @param authConfigs
+     *            configuration parameters for authentication
+     * @param authServerCount
+     *            number of auth enabled servers
+     * @return client port for the respective servers
+     * @throws IOException
+     */
+    protected String startQuorum(final int serverCount, int observerCount,
+            Map<String, String> authConfigs, int authServerCount)
+                    throws IOException {
+        StringBuilder connectStr = new StringBuilder();
+        final int[] clientPorts = startQuorum(serverCount, observerCount,
+                connectStr, authConfigs, authServerCount, false);
+        for (int i = 0; i < serverCount; i++) {
+            Assert.assertTrue("waiting for server " + i + " being up",
+                    ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i],
+                            ClientBase.CONNECTION_TIMEOUT));
+        }
+        return connectStr.toString();
+    }
+
+    /**
+     * Starts the given number of quorum servers and won't wait for the quorum
+     * formation.
+     *
+     * @param serverCount
+     *            total server count includes participants + observers
+     * @param observerCount
+     *            number of observers
+     * @param connectStr
+     *            connection string where clients can used for connection
+     *            establishment
+     * @param authConfigs
+     *            configuration parameters for authentication
+     * @param authServerCount
+     *            number of auth enabled servers
+     * @param delayedServerStartup
+     *            true flag value to add delay between server's startup, false otherwise.
+     * @return client port for the respective servers
+     * @throws IOException
+     */
+    protected int[] startQuorum(final int serverCount, int observerCount,
+            StringBuilder connectStr, Map<String, String> authConfigs,
+            int authServerCount, boolean delayedServerStartup)
+                    throws IOException {
+        final int clientPorts[] = new int[serverCount];
+        StringBuilder sb = new StringBuilder();
+
+        // If there are any Observers then the Observer server details will be
+        // placed first in the configuration section.
+        for (int i = 0; i < serverCount; i++) {
+            clientPorts[i] = PortAssignment.unique();
+            String server = "";
+            if (observerCount > 0 && i < observerCount) {
+                // add observer learner type
+                server = String.format("server.%d=localhost:%d:%d:observer",
+                        i, PortAssignment.unique(), PortAssignment.unique());
+            } else {
+                // add participant learner type
+                server = String.format("server.%d=localhost:%d:%d:participant",
+                        i, PortAssignment.unique(), PortAssignment.unique());
+            }
+            sb.append(server + "\n");
+            connectStr.append("127.0.0.1:" + clientPorts[i]);
+            if (i < serverCount - 1) {
+                connectStr.append(",");
+            }
+        }
+        String quorumCfg = sb.toString();
+        // servers with authentication interfaces configured
+        int i = 0;
+        for (; i < authServerCount; i++) {
+            if (observerCount > 0 && i < observerCount) {
+                String obsCfgSection = quorumCfg + "\npeerType=observer";
+                quorumCfg = obsCfgSection;
+            }
+            startServer(authConfigs, clientPorts[i], quorumCfg, i, delayedServerStartup);
+        }
+        // servers without any authentication configured
+        for (int j = 0; j < serverCount - authServerCount; j++, i++) {
+            if (observerCount > 0 && i < observerCount) {
+                String obsCfgSection = quorumCfg + "\npeerType=observer";
+                quorumCfg = obsCfgSection;
+            }
+            startServer(null, clientPorts[i], quorumCfg, i, delayedServerStartup);
+        }
+        return clientPorts;
+    }
+
+    private void startServer(Map<String, String> authConfigs,
+            final int clientPort, String quorumCfg, int i,
+            boolean delayedServerStartup) throws IOException {
+        MainThread mthread;
+        if (authConfigs != null) {
+            mthread = new MainThread(i, clientPort, quorumCfg, authConfigs);
+        } else {
+            mthread = new MainThread(i, clientPort, quorumCfg);
+        }
+        mt.add(mthread);
+        mthread.start();
+
+        if (delayedServerStartup) {
+            addDelayBeforeStartingNextServer(mthread);
+        }
+    }
+
+    private void addDelayBeforeStartingNextServer(MainThread mThread) {
+        // Refer https://issues.apache.org/jira/browse/ZOOKEEPER-2712
+        LOG.info("Waiting to finish login context init(Krb login), "
+                + "as there are potential concurrency issues in ApacheDS "
+                + "if multiple servers starts together!");
+        int retries = 60; // 15secs delay
+        while (retries > 0) {
+            if (mThread.getQuorumPeer() != null
+                    && mThread.getQuorumPeer().hasAuthInitialized()) {
+                try {
+                    Thread.sleep(1000); // adding 1sec grace period.
+                } catch (InterruptedException e) {
+                    LOG.info("Ignore InterruptedException");
+                }
+                break;
+            }
+            // moving to next retry cycle
+            retries--;
+            try {
+                Thread.sleep(250);
+            } catch (InterruptedException e) {
+                LOG.info("Ignore InterruptedException");
+            }
+        }
+    }
+
+    protected void startServer(MainThread restartPeer,
+            Map<String, String> authConfigs) throws IOException {
+        MainThread mthread = new MainThread(restartPeer.getMyid(),
+                restartPeer.getClientPort(), restartPeer.getQuorumCfgSection(),
+                authConfigs);
+        mt.add(mthread);
+        mthread.start();
+    }
+
+    void shutdownAll() {
+        for (int i = 0; i < mt.size(); i++) {
+            shutdown(i);
+        }
+    }
+
+    MainThread shutdown(int index) {
+        MainThread mainThread = mt.get(index);
+        try {
+            mainThread.shutdown();
+        } catch (InterruptedException e) {
+        } finally {
+            mt.remove(index);
+        }
+        mainThread.deleteBaseDir();
+        return mainThread;
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java
new file mode 100644
index 0000000..4eeccf3
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java
@@ -0,0 +1,239 @@
+/**
+ * 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.quorum.auth;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread;
+import org.apache.zookeeper.test.ClientBase;
+import org.apache.zookeeper.test.ClientTest;
+import org.apache.zookeeper.test.ClientBase.CountdownWatcher;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Rolling upgrade should do in three steps:
+ *
+ * step-1) Stop the server and set the flags and restart the server.
+ * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false and quorum.auth.serverRequireSasl=false
+ * Ensure that all the servers should complete this step. Now, move to next step.
+ *
+ * step-2) Stop the server one by one and change the flags and restart the server.
+ * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=false
+ * Ensure that all the servers should complete this step. Now, move to next step.
+ *
+ * step-3) Stop the server one by one and change the flags and restart the server.
+ * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=true
+ * Now, all the servers are fully upgraded and running in secured mode.
+ */
+public class QuorumAuthUpgradeTest extends QuorumAuthTestBase {
+    static {
+        String jaasEntries = new String("" + "QuorumServer {\n"
+                + "       org.apache.zookeeper.server.auth.DigestLoginModule required\n"
+                + "       user_test=\"mypassword\";\n" + "};\n"
+                + "QuorumLearner {\n"
+                + "       org.apache.zookeeper.server.auth.DigestLoginModule required\n"
+                + "       username=\"test\"\n"
+                + "       password=\"mypassword\";\n" + "};\n");
+        setupJaasConfig(jaasEntries);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        shutdownAll();
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        cleanupJaasConfig();
+    }
+
+    /**
+     * Test to verify that servers are able to start without any authentication.
+     * peer0 -> quorum.auth.enableSasl=false
+     * peer1 -> quorum.auth.enableSasl=false
+     */
+    @Test(timeout = 30000)
+    public void testNullAuthLearnerServer() throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false");
+
+        String connectStr = startQuorum(2, authConfigs, 0, false);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT,
+                watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+        zk.close();
+    }
+
+    /**
+     * Test to verify that servers are able to form quorum.
+     * peer0 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false
+     * peer1 -> quorum.auth.enableSasl=false, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerAgainstNullAuthServer() throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+
+        String connectStr = startQuorum(2, authConfigs, 1, false);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT,
+                watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+        zk.close();
+    }
+
+    /**
+     * Test to verify that servers are able to form quorum.
+     * peer0 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false
+     * peer1 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerAgainstNoAuthRequiredServer() throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+
+        String connectStr = startQuorum(2, authConfigs, 2, false);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT,
+                watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+        zk.close();
+    }
+
+    /**
+     * Test to verify that servers are able to form quorum.
+     * peer0 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=true
+     * peer1 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=true
+     */
+    @Test(timeout = 30000)
+    public void testAuthLearnerServer() throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+
+        String connectStr = startQuorum(2, authConfigs, 2, false);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT,
+                watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+        zk.close();
+    }
+
+    /**
+     * Rolling upgrade should do in three steps:
+     *
+     * step-1) Stop the server and set the flags and restart the server.
+     * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false and quorum.auth.serverRequireSasl=false
+     * Ensure that all the servers should complete this step. Now, move to next step.
+     *
+     * step-2) Stop the server one by one and change the flags and restart the server.
+     * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=false
+     * Ensure that all the servers should complete this step. Now, move to next step.
+     *
+     * step-3) Stop the server one by one and change the flags and restart the server.
+     * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=true
+     * Now, all the servers are fully upgraded and running in secured mode.
+     */
+    @Test(timeout = 90000)
+    public void testRollingUpgrade() throws Exception {
+        // Start peer0,1,2 servers with quorum.auth.enableSasl=false and
+        // quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false
+        // Assume this is an existing cluster.
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false");
+
+        String connectStr = startQuorum(3, authConfigs, 0, false);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT,
+                watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT_SEQUENTIAL);
+
+        //1. Upgrade peer0,1,2 with quorum.auth.enableSasl=true and
+        // quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "false");
+        restartServer(authConfigs, 0, zk, watcher);
+        restartServer(authConfigs, 1, zk, watcher);
+        restartServer(authConfigs, 2, zk, watcher);
+
+        //2. Upgrade peer0,1,2 with quorum.auth.enableSasl=true and
+        // quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=false
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false");
+        restartServer(authConfigs, 0, zk, watcher);
+        restartServer(authConfigs, 1, zk, watcher);
+        restartServer(authConfigs, 2, zk, watcher);
+
+        //3. Upgrade peer0,1,2 with quorum.auth.enableSasl=true and
+        // quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=true
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        restartServer(authConfigs, 0, zk, watcher);
+        restartServer(authConfigs, 1, zk, watcher);
+        restartServer(authConfigs, 2, zk, watcher);
+
+        //4. Restart peer2 with quorum.auth.learnerEnableSasl=false and
+        // quorum.auth.serverRequireSasl=false. It should fail to join the
+        // quorum as this needs auth.
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false");
+        MainThread m = shutdown(2);
+        startServer(m, authConfigs);
+        Assert.assertFalse("waiting for server 2 being up", ClientBase
+                .waitForServerUp("127.0.0.1:" + m.getClientPort(), 5000));
+    }
+
+    private void restartServer(Map<String, String> authConfigs, int index,
+            ZooKeeper zk, CountdownWatcher watcher) throws IOException,
+                    KeeperException, InterruptedException, TimeoutException {
+        LOG.info("Restarting server myid=" + index);
+        MainThread m = shutdown(index);
+        startServer(m, authConfigs);
+        Assert.assertTrue("waiting for server" + index + "being up",
+                ClientBase.waitForServerUp("127.0.0.1:" + m.getClientPort(),
+                        ClientBase.CONNECTION_TIMEOUT));
+        watcher.waitForConnected(ClientTest.CONNECTION_TIMEOUT);
+        zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT_SEQUENTIAL);
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java
new file mode 100644
index 0000000..c2f4cc3
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java
@@ -0,0 +1,380 @@
+/**
+ * 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.quorum.auth;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.PortAssignment;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.server.quorum.QuorumPeer;
+import org.apache.zookeeper.server.quorum.QuorumPeerMain;
+import org.apache.zookeeper.server.quorum.QuorumPeerTestBase;
+import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState;
+import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
+import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread;
+import org.apache.zookeeper.test.ClientBase;
+import org.apache.zookeeper.test.ClientBase.CountdownWatcher;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class QuorumDigestAuthTest extends QuorumAuthTestBase {
+
+    private ZooKeeper zk;
+    static {
+        String jaasEntries = new String(""
+                + "QuorumServer {\n"
+                + "       org.apache.zookeeper.server.auth.DigestLoginModule required\n"
+                + "       user_test=\"mypassword\";\n" + "};\n"
+                + "QuorumLearner {\n"
+                + "       org.apache.zookeeper.server.auth.DigestLoginModule required\n"
+                + "       username=\"test\"\n"
+                + "       password=\"mypassword\";\n" + "};\n"
+                + "QuorumLearnerInvalid {\n"
+                + "       org.apache.zookeeper.server.auth.DigestLoginModule required\n"
+                + "       username=\"test\"\n"
+                + "       password=\"invalid\";\n" + "};" + "\n");
+        setupJaasConfig(jaasEntries);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        for (MainThread mainThread : mt) {
+            mainThread.shutdown();
+            mainThread.deleteBaseDir();
+        }
+        if (zk != null) {
+            zk.close();
+        }
+    }
+
+    @AfterClass
+    public static void cleanup(){
+        cleanupJaasConfig();
+    }
+
+    /**
+     * Test to verify that server is able to start with valid credentials
+     */
+    @Test(timeout = 30000)
+    public void testValidCredentials() throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+
+        String connectStr = startQuorum(3, authConfigs, 3, false);
+        CountdownWatcher watcher = new CountdownWatcher();
+        zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE,
+                    CreateMode.PERSISTENT);
+        }
+    }
+
+    /**
+     * Test to verify that server is able to start with invalid credentials if
+     * the configuration is set to quorum.auth.serverRequireSasl=false.
+     * Quorum will talk each other even if the authentication is not succeeded
+     */
+    @Test(timeout = 30000)
+    public void testSaslNotRequiredWithInvalidCredentials() throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearnerInvalid");
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false");
+        String connectStr = startQuorum(3, authConfigs, 3, false);
+        CountdownWatcher watcher = new CountdownWatcher();
+        zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE,
+                    CreateMode.PERSISTENT);
+        }
+    }
+
+    /**
+     * Test to verify that server shouldn't start with invalid credentials
+     * if the configuration is set to quorum.auth.serverRequireSasl=true,
+     * quorum.auth.learnerRequireSasl=true
+     */
+    @Test(timeout = 30000)
+    public void testSaslRequiredInvalidCredentials() throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearnerInvalid");
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        int serverCount = 2;
+        final int[] clientPorts = startQuorum(serverCount, 0,
+                new StringBuilder(), authConfigs, serverCount, false);
+        for (int i = 0; i < serverCount; i++) {
+            boolean waitForServerUp = ClientBase.waitForServerUp(
+                    "127.0.0.1:" + clientPorts[i], QuorumPeerTestBase.TIMEOUT);
+            Assert.assertFalse("Shouldn't start server with invalid credentials",
+                    waitForServerUp);
+        }
+    }
+
+    /**
+     * If quorumpeer learner is not auth enabled then self won't be able to join
+     * quorum. So this test is ensuring that the quorumpeer learner is also auth
+     * enabled while enabling quorum server require sasl.
+     */
+    @Test(timeout = 10000)
+    public void testEnableQuorumServerRequireSaslWithoutQuorumLearnerRequireSasl()
+            throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT,
+                "QuorumLearner");
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "false");
+        MainThread mthread = new MainThread(1, PortAssignment.unique(), "",
+                authConfigs);
+        String args[] = new String[1];
+        args[0] = mthread.getConfFile().toString();
+        try {
+            new QuorumPeerMain() {
+                @Override
+                protected void initializeAndRun(String[] args)
+                        throws ConfigException, IOException {
+                    super.initializeAndRun(args);
+                }
+            }.initializeAndRun(args);
+            Assert.fail("Must throw exception as quorumpeer learner is not enabled!");
+        } catch (ConfigException e) {
+            // expected
+        }
+    }
+
+
+    /**
+     * If quorumpeer learner is not auth enabled then self won't be able to join
+     * quorum. So this test is ensuring that the quorumpeer learner is also auth
+     * enabled while enabling quorum server require sasl.
+     */
+    @Test(timeout = 10000)
+    public void testEnableQuorumAuthenticationConfigurations()
+            throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT,
+                "QuorumLearner");
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false");
+
+        // case-1) 'quorum.auth.enableSasl' is off. Tries to enable server sasl.
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "false");
+        MainThread mthread = new MainThread(1, PortAssignment.unique(), "",
+                authConfigs);
+        String args[] = new String[1];
+        args[0] = mthread.getConfFile().toString();
+        try {
+            new QuorumPeerMain() {
+                @Override
+                protected void initializeAndRun(String[] args)
+                        throws ConfigException, IOException {
+                    super.initializeAndRun(args);
+                }
+            }.initializeAndRun(args);
+            Assert.fail("Must throw exception as quorum sasl is not enabled!");
+        } catch (ConfigException e) {
+            // expected
+        }
+
+        // case-1) 'quorum.auth.enableSasl' is off. Tries to enable learner sasl.
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        try {
+            new QuorumPeerMain() {
+                @Override
+                protected void initializeAndRun(String[] args)
+                        throws ConfigException, IOException {
+                    super.initializeAndRun(args);
+                }
+            }.initializeAndRun(args);
+            Assert.fail("Must throw exception as quorum sasl is not enabled!");
+        } catch (ConfigException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Test to verify that Observer server is able to join quorum.
+     */
+    @Test(timeout = 30000)
+    public void testObserverWithValidCredentials() throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+
+        // Starting auth enabled 5-node cluster. 3-Participants and 2-Observers.
+        int totalServerCount = 5;
+        int observerCount = 2;
+        String connectStr = startQuorum(totalServerCount, observerCount,
+                authConfigs, totalServerCount);
+        CountdownWatcher watcher = new CountdownWatcher();
+        zk = new ZooKeeper(connectStr.toString(), ClientBase.CONNECTION_TIMEOUT,
+                watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT);
+    }
+
+    /**
+     * Test to verify that non-auth enabled Observer server should be rejected
+     * by the auth enabled quorum servers.
+     */
+    @Test(timeout = 30000)
+    public void testNonAuthEnabledObserverJoiningAuthEnabledQuorum()
+            throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+
+        // Starting auth enabled 3-node cluster.
+        int totalServerCount = 3;
+        String connectStr = startQuorum(totalServerCount, authConfigs,
+                totalServerCount, false);
+
+        CountdownWatcher watcher = new CountdownWatcher();
+        zk = new ZooKeeper(connectStr.toString(), ClientBase.CONNECTION_TIMEOUT,
+                watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT_SEQUENTIAL);
+
+        // Adding a non-auth enabled Observer to the 3-node auth cluster.
+        String quorumCfgSection = mt.get(0).getQuorumCfgSection();
+        int observerMyid = totalServerCount + 1;
+        StringBuilder newObsCfgSection = new StringBuilder(quorumCfgSection);
+        newObsCfgSection.append("\n");
+        newObsCfgSection.append(String.format(
+                "server.%d=localhost:%d:%d:observer", observerMyid,
+                PortAssignment.unique(), PortAssignment.unique()));
+        newObsCfgSection.append("\npeerType=observer");
+        newObsCfgSection.append("\n");
+        int clientPort = PortAssignment.unique();
+        newObsCfgSection.append("127.0.0.1:" + clientPort);
+        MainThread mthread = new MainThread(observerMyid, clientPort,
+                newObsCfgSection.toString());
+        mt.add(mthread);
+        mthread.start();
+
+        boolean waitForServerUp = ClientBase.waitForServerUp(
+                "127.0.0.1:" + clientPort, QuorumPeerTestBase.TIMEOUT);
+        Assert.assertFalse(
+                "Non-auth enabled Observer shouldn't be able join auth-enabled quorum",
+                waitForServerUp);
+
+        // quorum shouldn't be disturbed due to rejection.
+        zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT_SEQUENTIAL);
+    }
+
+    /**
+     * Test to verify that server is able to reform quorum if the Leader goes
+     * down.
+     */
+    @Test(timeout = 30000)
+    public void testRelectionWithValidCredentials() throws Exception {
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+
+        String connectStr = startQuorum(3, authConfigs, 3, false);
+        CountdownWatcher watcher = new CountdownWatcher();
+        zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT_SEQUENTIAL);
+        watcher.reset();
+
+        // Shutdown Leader to trigger re-election
+        QuorumPeer leaderQP = getLeaderQuorumPeer(mt);
+        LOG.info("Shutdown Leader sid:{} to trigger quorum leader-election",
+                leaderQP.getId());
+        shutdownQP(leaderQP);
+
+        // Wait for quorum formation
+        QuorumPeer newLeaderQP = waitForLeader();
+        assertNotNull("New leader must have been elected by now", newLeaderQP);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT_SEQUENTIAL);
+    }
+
+    private QuorumPeer waitForLeader() throws InterruptedException {
+        int retryCnt = 0;
+        QuorumPeer newLeaderQP = null;
+        while (retryCnt < 30) {
+            newLeaderQP = getLeaderQuorumPeer(mt);
+            if (newLeaderQP != null) {
+                LOG.info("Number of retries:{} to findout new Leader",
+                        retryCnt);
+                break;
+            }
+            retryCnt--;
+            Thread.sleep(500);
+        }
+        return newLeaderQP;
+    }
+
+    private void shutdownQP(QuorumPeer qp) throws InterruptedException {
+        assertNotNull("QuorumPeer doesn't exist!", qp);
+        qp.shutdown();
+
+        int retryCnt = 30;
+        while (retryCnt > 0) {
+            if (qp.getPeerState() == ServerState.LOOKING) {
+                LOG.info("Number of retries:{} to change the server state to {}",
+                        retryCnt, ServerState.LOOKING);
+                break;
+            }
+            Thread.sleep(500);
+            retryCnt--;
+        }
+        Assert.assertEquals(
+                "After shutdown, QuorumPeer should change its state to LOOKING",
+                ServerState.LOOKING, qp.getPeerState());
+    }
+
+    private QuorumPeer getLeaderQuorumPeer(List<MainThread> mtList) {
+        for (MainThread mt : mtList) {
+            QuorumPeer quorumPeer = mt.getQuorumPeer();
+            if (null != quorumPeer
+                    && ServerState.LEADING == quorumPeer.getPeerState()) {
+                return quorumPeer;
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java
new file mode 100644
index 0000000..e3eddf7
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java
@@ -0,0 +1,115 @@
+/**
+ * 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.quorum.auth;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread;
+import org.apache.zookeeper.test.ClientBase;
+import org.apache.zookeeper.test.ClientBase.CountdownWatcher;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+public class QuorumKerberosAuthTest extends KerberosSecurityTestcase {
+    private static File keytabFile;
+    static {
+        String keytabFilePath = FilenameUtils.normalize(KerberosTestUtils.getKeytabFile(), true);
+        String jaasEntries = new String(""
+                + "QuorumServer {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       principal=\"" + KerberosTestUtils.getServerPrincipal() + "\";\n" + "};\n"
+                + "QuorumLearner {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       isInitiator=true\n"
+                + "       principal=\"" + KerberosTestUtils.getLearnerPrincipal() + "\";\n" + "};\n");
+        setupJaasConfig(jaasEntries);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        // create keytab
+        keytabFile = new File(KerberosTestUtils.getKeytabFile());
+        String learnerPrincipal = KerberosTestUtils.getLearnerPrincipal();
+        String serverPrincipal = KerberosTestUtils.getServerPrincipal();
+        learnerPrincipal = learnerPrincipal.substring(0, learnerPrincipal.lastIndexOf("@"));
+        serverPrincipal = serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@"));
+        getKdc().createPrincipal(keytabFile, learnerPrincipal, serverPrincipal);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        for (MainThread mainThread : mt) {
+            mainThread.shutdown();
+            mainThread.deleteBaseDir();
+        }
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        if(keytabFile != null){
+            FileUtils.deleteQuietly(keytabFile);
+        }
+        cleanupJaasConfig();
+    }
+
+    /**
+     * Test to verify that server is able to start with valid credentials
+     */
+    @Test(timeout = 120000)
+    public void testValidCredentials() throws Exception {
+        String serverPrincipal = KerberosTestUtils.getServerPrincipal();
+        serverPrincipal = serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@"));
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal);
+        String connectStr = startQuorum(3, authConfigs, 3, true);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        }
+        zk.close();
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java
new file mode 100644
index 0000000..55deefb
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java
@@ -0,0 +1,192 @@
+/**
+ * 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.quorum.auth;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.PortAssignment;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread;
+import org.apache.zookeeper.test.ClientBase;
+import org.apache.zookeeper.test.ClientBase.CountdownWatcher;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import junit.framework.Assert;
+
+public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase {
+    private static File keytabFile;
+    private static String hostServerPrincipal = KerberosTestUtils.getHostServerPrincipal();
+    private static String hostLearnerPrincipal = KerberosTestUtils.getHostLearnerPrincipal();
+    private static String hostNamedLearnerPrincipal = KerberosTestUtils.getHostNamedLearnerPrincipal("myhost");
+    static {
+        setupJaasConfigEntries(hostServerPrincipal, hostLearnerPrincipal, hostNamedLearnerPrincipal);
+    }
+
+    private static void setupJaasConfigEntries(String hostServerPrincipal,
+            String hostLearnerPrincipal, String hostNamedLearnerPrincipal) {
+        String keytabFilePath = FilenameUtils.normalize(KerberosTestUtils.getKeytabFile(), true);
+        String jaasEntries = new String(""
+                + "QuorumServer {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       principal=\"" + KerberosTestUtils.replaceHostPattern(hostServerPrincipal) + "\";\n" + "};\n"
+                + "QuorumLearner {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       isInitiator=true\n"
+                + "       principal=\"" + KerberosTestUtils.replaceHostPattern(hostLearnerPrincipal) + "\";\n" + "};\n"
+                + "QuorumLearnerMyHost {\n"
+                + "       com.sun.security.auth.module.Krb5LoginModule required\n"
+                + "       useKeyTab=true\n"
+                + "       keyTab=\"" + keytabFilePath + "\"\n"
+                + "       storeKey=true\n"
+                + "       useTicketCache=false\n"
+                + "       debug=true\n"
+                + "       doNotPrompt=true\n"
+                + "       refreshKrb5Config=true\n"
+                + "       isInitiator=true\n"
+                + "       principal=\"" + hostNamedLearnerPrincipal + "\";\n" + "};\n");
+        setupJaasConfig(jaasEntries);
+    }
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        // create keytab
+        keytabFile = new File(KerberosTestUtils.getKeytabFile());
+
+        // Creates principals in the KDC and adds them to a keytab file.
+        String learnerPrincipal = hostLearnerPrincipal.substring(0, hostLearnerPrincipal.lastIndexOf("@"));
+        learnerPrincipal = KerberosTestUtils.replaceHostPattern(learnerPrincipal);
+        String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@"));
+        serverPrincipal = KerberosTestUtils.replaceHostPattern(serverPrincipal);
+
+        // learner with ipaddress in principal
+        String learnerPrincipal2 = hostNamedLearnerPrincipal.substring(0, hostNamedLearnerPrincipal.lastIndexOf("@"));
+        getKdc().createPrincipal(keytabFile, learnerPrincipal, learnerPrincipal2, serverPrincipal);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        for (MainThread mainThread : mt) {
+            mainThread.shutdown();
+            mainThread.deleteBaseDir();
+        }
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        if(keytabFile != null){
+            FileUtils.deleteQuietly(keytabFile);
+        }
+        cleanupJaasConfig();
+    }
+
+    /**
+     * Test to verify that server is able to start with valid credentials
+     */
+    @Test(timeout = 120000)
+    public void testValidCredentials() throws Exception {
+        String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@"));
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal);
+        String connectStr = startQuorum(3, authConfigs, 3, true);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        }
+        zk.close();
+    }
+
+    /**
+     * Test to verify that the bad server connection to the quorum should be rejected.
+     */
+    @Test(timeout = 120000)
+    public void testConnectBadServer() throws Exception {
+        String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@"));
+        Map<String, String> authConfigs = new HashMap<String, String>();
+        authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true");
+        authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal);
+        String connectStr = startQuorum(3, authConfigs, 3, true);
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        }
+        zk.close();
+
+        String quorumCfgSection = mt.get(0).getQuorumCfgSection();
+        StringBuilder sb = new StringBuilder();
+        sb.append(quorumCfgSection);
+
+        int myid = mt.size() + 1;
+        final int clientPort = PortAssignment.unique();
+        String server = String.format("server.%d=localhost:%d:%d:participant",
+                myid, PortAssignment.unique(), PortAssignment.unique());
+        sb.append(server + "\n");
+        quorumCfgSection = sb.toString();
+        authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT,
+                "QuorumLearnerMyHost");
+        MainThread badServer = new MainThread(myid, clientPort, quorumCfgSection,
+                authConfigs);
+        badServer.start();
+        watcher = new CountdownWatcher();
+        connectStr = "127.0.0.1:" + clientPort;
+        zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher);
+        try{
+            watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT/3);
+            Assert.fail("Must throw exception as the myHost is not an authorized one!");
+        } catch (TimeoutException e){
+            // expected
+        } finally {
+            zk.close();
+            badServer.shutdown();
+            badServer.deleteBaseDir();
+        }
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java
index fdcfac4..6037ff2 100644
--- a/src/java/test/org/apache/zookeeper/test/ClientBase.java
+++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java
@@ -664,4 +664,32 @@ public abstract class ClientBase extends ZKTestCase {
         }
         return sb.toString();
     }
+
+    public static ZooKeeper createZKClient(String cxnString) throws Exception {
+        return createZKClient(cxnString, CONNECTION_TIMEOUT);
+    }
+
+    /**
+     * Returns ZooKeeper client after connecting to ZooKeeper Server. Session
+     * timeout is {@link #CONNECTION_TIMEOUT}
+     *
+     * @param cxnString
+     *            connection string in the form of host:port
+     * @param sessionTimeout
+     * @throws IOException
+     *             in cases of network failure
+     */
+    public static ZooKeeper createZKClient(String cxnString, int sessionTimeout) throws IOException {
+        CountdownWatcher watcher = new CountdownWatcher();
+        ZooKeeper zk = new ZooKeeper(cxnString, sessionTimeout, watcher);
+        try {
+            watcher.waitForConnected(CONNECTION_TIMEOUT);
+        } catch (InterruptedException e) {
+            Assert.fail("ZooKeeper client can not connect to " + cxnString);
+        }
+        catch (TimeoutException e) {
+            Assert.fail("ZooKeeper client can not connect to " + cxnString);
+        }
+        return zk;
+    }
 }
diff --git a/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java b/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java
index 22f9dde..d6cb1e2 100644
--- a/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java
+++ b/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java
@@ -24,6 +24,7 @@ import java.io.File;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.NetworkInterface;
+import java.net.SocketException;
 import java.util.Enumeration;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -58,15 +59,19 @@ public class ClientPortBindTest extends ZKTestCase implements Watcher {
         // if we have a loopback and it has an address use it
         while(intfs.hasMoreElements()) {
             NetworkInterface i = intfs.nextElement();
-            if (i.isLoopback()) {
-                Enumeration<InetAddress> addrs = i.getInetAddresses();
-                while (addrs.hasMoreElements()) {
+            try {
+                if (i.isLoopback()) {
+                  Enumeration<InetAddress> addrs = i.getInetAddresses();
+                  while (addrs.hasMoreElements()) {
                     InetAddress a = addrs.nextElement();
                     if(a.isLoopbackAddress()) {
-                        bindAddress = a.getHostAddress();
-                        break;
+                      bindAddress = a.getHostAddress();
+                      break;
                     }
+                  }
                 }
+            } catch (SocketException se) {
+                LOG.warn("Couldn't find  loopback interface: " + se.getMessage());
             }
         }
         if (bindAddress == null) {
diff --git a/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java b/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java
index 8088505..0ecac6e 100644
--- a/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java
+++ b/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java
@@ -41,7 +41,7 @@ public class FLEPredicateTest extends ZKTestCase {
     
     class MockFLE extends FastLeaderElection {
         MockFLE(QuorumPeer peer){
-            super(peer, new QuorumCnxManager(peer));
+            super(peer, peer.createCnxnManager());
         }
         
         boolean predicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch){
diff --git a/src/java/test/org/apache/zookeeper/test/FourLetterWordsWhiteListTest.java b/src/java/test/org/apache/zookeeper/test/FourLetterWordsWhiteListTest.java
new file mode 100644
index 0000000..613346f
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/test/FourLetterWordsWhiteListTest.java
@@ -0,0 +1,252 @@
+/**
+ * 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.test;
+
+import java.io.IOException;
+
+import org.apache.zookeeper.TestableZooKeeper;
+import org.apache.zookeeper.server.ServerCnxn;
+import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FourLetterWordsWhiteListTest extends ClientBase {
+    protected static final Logger LOG =
+        LoggerFactory.getLogger(FourLetterWordsTest.class);
+
+    @Rule
+    public Timeout timeout = new Timeout(30000);
+
+    /*
+     * ZOOKEEPER-2693: test white list of four letter words.
+     * For 3.5.x default white list is empty. Verify that is
+     * the case (except 'stat' command which is enabled in ClientBase
+     * which other tests depend on.).
+     */
+    @Test(timeout=30000)
+    public void testFourLetterWordsAllDisabledByDefault() throws Exception {
+        stopServer();
+        ServerCnxn.resetWhiteList();
+        System.setProperty("zookeeper.4lw.commands.whitelist", "stat");
+        startServer();
+
+        // Default white list for 3.5.x is empty, so all command should fail.
+        verifyAllCommandsFail();
+
+        TestableZooKeeper zk = createClient();
+
+        verifyAllCommandsFail();
+
+        zk.getData("/", true, null);
+
+        verifyAllCommandsFail();
+
+        zk.close();
+
+        verifyFuzzyMatch("stat", "Outstanding");
+        verifyAllCommandsFail();
+    }
+
+    @Test(timeout=30000)
+    public void testFourLetterWordsEnableSomeCommands() throws Exception {
+        stopServer();
+        ServerCnxn.resetWhiteList();
+        System.setProperty("zookeeper.4lw.commands.whitelist", "stat, ruok, isro");
+        startServer();
+        // stat, ruok and isro are white listed.
+        verifyFuzzyMatch("stat", "Outstanding");
+        verifyExactMatch("ruok", "imok");
+        verifyExactMatch("isro", "rw");
+
+        // Rest of commands fail.
+        verifyExactMatch("conf", generateExpectedMessage("conf"));
+        verifyExactMatch("cons", generateExpectedMessage("cons"));
+        verifyExactMatch("crst", generateExpectedMessage("crst"));
+        verifyExactMatch("dump", generateExpectedMessage("dump"));
+        verifyExactMatch("envi", generateExpectedMessage("envi"));
+        verifyExactMatch("gtmk", generateExpectedMessage("gtmk"));
+        verifyExactMatch("stmk", generateExpectedMessage("stmk"));
+        verifyExactMatch("srst", generateExpectedMessage("srst"));
+        verifyExactMatch("wchc", generateExpectedMessage("wchc"));
+        verifyExactMatch("wchp", generateExpectedMessage("wchp"));
+        verifyExactMatch("wchs", generateExpectedMessage("wchs"));
+        verifyExactMatch("mntr", generateExpectedMessage("mntr"));
+    }
+
+    @Test(timeout=30000)
+    public void testISROEnabledWhenReadOnlyModeEnabled() throws Exception {
+        stopServer();
+        ServerCnxn.resetWhiteList();
+        System.setProperty("zookeeper.4lw.commands.whitelist", "stat");
+        System.setProperty("readonlymode.enabled", "true");
+        startServer();
+        verifyExactMatch("isro", "rw");
+        System.clearProperty("readonlymode.enabled");
+    }
+
+    @Test(timeout=30000)
+    public void testFourLetterWordsInvalidConfiguration() throws Exception {
+        stopServer();
+        ServerCnxn.resetWhiteList();
+        System.setProperty("zookeeper.4lw.commands.whitelist", "foo bar" +
+                " foo,,, " +
+                "bar :.,@#$%^&*() , , , , bar, bar, stat,        ");
+        startServer();
+
+        // Just make sure we are good when admin made some mistakes in config file.
+        verifyAllCommandsFail();
+        // But still, what's valid in white list will get through.
+        verifyFuzzyMatch("stat", "Outstanding");
+    }
+
+    @Test(timeout=30000)
+    public void testFourLetterWordsEnableAllCommandsThroughAsterisk() throws Exception {
+        stopServer();
+        ServerCnxn.resetWhiteList();
+        System.setProperty("zookeeper.4lw.commands.whitelist", "*");
+        startServer();
+        verifyAllCommandsSuccess();
+    }
+
+    @Test(timeout=30000)
+    public void testFourLetterWordsEnableAllCommandsThroughExplicitList() throws Exception {
+        stopServer();
+        ServerCnxn.resetWhiteList();
+        System.setProperty("zookeeper.4lw.commands.whitelist",
+                "ruok, envi, conf, stat, srvr, cons, dump," +
+                        "wchs, wchp, wchc, srst, crst, " +
+                        "mntr, gtmk, isro, stmk");
+        startServer();
+        verifyAllCommandsSuccess();
+    }
+
+    private void verifyAllCommandsSuccess() throws Exception {
+        verifyExactMatch("ruok", "imok");
+        verifyFuzzyMatch("envi", "java.version");
+        verifyFuzzyMatch("conf", "clientPort");
+        verifyFuzzyMatch("stat", "Outstanding");
+        verifyFuzzyMatch("srvr", "Outstanding");
+        verifyFuzzyMatch("cons", "queued");
+        verifyFuzzyMatch("dump", "Session");
+        verifyFuzzyMatch("wchs", "watches");
+        verifyFuzzyMatch("wchp", "");
+        verifyFuzzyMatch("wchc", "");
+
+        verifyFuzzyMatch("srst", "reset");
+        verifyFuzzyMatch("crst", "reset");
+
+        verifyFuzzyMatch("stat", "Outstanding");
+        verifyFuzzyMatch("srvr", "Outstanding");
+        verifyFuzzyMatch("cons", "queued");
+        verifyFuzzyMatch("gtmk", "306");
+        verifyFuzzyMatch("isro", "rw");
+
+        TestableZooKeeper zk = createClient();
+        String sid = getHexSessionId(zk.getSessionId());
+
+        verifyFuzzyMatch("stat", "queued");
+        verifyFuzzyMatch("srvr", "Outstanding");
+        verifyFuzzyMatch("cons", sid);
+        verifyFuzzyMatch("dump", sid);
+
+        zk.getData("/", true, null);
+
+        verifyFuzzyMatch("stat", "queued");
+        verifyFuzzyMatch("srvr", "Outstanding");
+        verifyFuzzyMatch("cons", sid);
+        verifyFuzzyMatch("dump", sid);
+
+        verifyFuzzyMatch("wchs", "watching 1");
+        verifyFuzzyMatch("wchp", sid);
+        verifyFuzzyMatch("wchc", sid);
+        zk.close();
+
+        verifyExactMatch("ruok", "imok");
+        verifyFuzzyMatch("envi", "java.version");
+        verifyFuzzyMatch("conf", "clientPort");
+        verifyFuzzyMatch("stat", "Outstanding");
+        verifyFuzzyMatch("srvr", "Outstanding");
+        verifyFuzzyMatch("cons", "queued");
+        verifyFuzzyMatch("dump", "Session");
+        verifyFuzzyMatch("wchs", "watch");
+        verifyFuzzyMatch("wchp", "");
+        verifyFuzzyMatch("wchc", "");
+
+        verifyFuzzyMatch("srst", "reset");
+        verifyFuzzyMatch("crst", "reset");
+
+        verifyFuzzyMatch("stat", "Outstanding");
+        verifyFuzzyMatch("srvr", "Outstanding");
+        verifyFuzzyMatch("cons", "queued");
+        verifyFuzzyMatch("mntr", "zk_server_state\tstandalone");
+        verifyFuzzyMatch("mntr", "num_alive_connections");
+        verifyFuzzyMatch("stat", "Connections");
+        verifyFuzzyMatch("srvr", "Connections");
+    }
+
+    private void verifyAllCommandsFail() throws Exception {
+        verifyExactMatch("ruok", generateExpectedMessage("ruok"));
+        verifyExactMatch("conf", generateExpectedMessage("conf"));
+        verifyExactMatch("cons", generateExpectedMessage("cons"));
+        verifyExactMatch("crst", generateExpectedMessage("crst"));
+        verifyExactMatch("dump", generateExpectedMessage("dump"));
+        verifyExactMatch("envi", generateExpectedMessage("envi"));
+        verifyExactMatch("gtmk", generateExpectedMessage("gtmk"));
+        verifyExactMatch("stmk", generateExpectedMessage("stmk"));
+        verifyExactMatch("srst", generateExpectedMessage("srst"));
+        verifyExactMatch("wchc", generateExpectedMessage("wchc"));
+        verifyExactMatch("wchp", generateExpectedMessage("wchp"));
+        verifyExactMatch("wchs", generateExpectedMessage("wchs"));
+        verifyExactMatch("mntr", generateExpectedMessage("mntr"));
+        verifyExactMatch("isro", generateExpectedMessage("isro"));
+
+        // srvr is enabled by default due to the sad fact zkServer.sh uses it.
+        verifyFuzzyMatch("srvr", "Outstanding");
+    }
+
+    private void verifyFuzzyMatch(String cmd, String expected) throws IOException {
+        String resp = sendRequest(cmd);
+        LOG.info("cmd " + cmd + " expected " + expected + " got " + resp);
+        Assert.assertTrue(resp.contains(expected));
+    }
+
+    private String generateExpectedMessage(String command) {
+        return command + " is not executed because it is not in the whitelist.";
+    }
+
+    private void verifyExactMatch(String cmd, String expected) throws IOException {
+        String resp = sendRequest(cmd);
+        LOG.info("cmd " + cmd + " expected an exact match of " + expected + "; got " + resp);
+        Assert.assertTrue(resp.trim().equals(expected));
+    }
+
+    private String sendRequest(String cmd) throws IOException {
+      HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0);
+      return send4LetterWord(hpobj.host, hpobj.port, cmd);
+    }
+
+    private String sendRequest(String cmd, int timeout) throws IOException {
+        HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0);
+        return send4LetterWord(hpobj.host, hpobj.port, cmd, timeout);
+    }
+}
diff --git a/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java b/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java
index c6573f4..051da13 100644
--- a/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java
+++ b/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java
@@ -20,9 +20,10 @@ package org.apache.zookeeper.test;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
-import java.net.InetSocketAddress;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Properties;
 import java.util.Set;
 
@@ -30,8 +31,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.zookeeper.PortAssignment;
 import org.apache.zookeeper.TestableZooKeeper;
+import org.apache.zookeeper.jmx.CommonNames;
 import org.apache.zookeeper.server.quorum.QuorumPeer;
+import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType;
 import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
+import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState;
 import org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical;
 import org.junit.Assert;
 import org.junit.Test;
@@ -139,23 +143,28 @@ public class HierarchicalQuorumTest extends ClientBase {
                         : QuorumPeer.LearnerType.PARTICIPANT));
 
         LOG.info("creating QuorumPeer 1 port " + port1);
+        List <QuorumPeer> qps = new ArrayList<QuorumPeer>();
         QuorumHierarchical hq1 = new QuorumHierarchical(qp); 
         s1 = new QuorumPeer(peers, s1dir, s1dir, port1, 3, 1, tickTime, initLimit, syncLimit, hq1);
+        qps.add(s1);
         Assert.assertEquals(port1, s1.getClientPort());
         
         LOG.info("creating QuorumPeer 2 port " + port2);
         QuorumHierarchical hq2 = new QuorumHierarchical(qp); 
         s2 = new QuorumPeer(peers, s2dir, s2dir, port2, 3, 2, tickTime, initLimit, syncLimit, hq2);
+        qps.add(s2);
         Assert.assertEquals(port2, s2.getClientPort());
         
         LOG.info("creating QuorumPeer 3 port " + port3);
         QuorumHierarchical hq3 = new QuorumHierarchical(qp); 
         s3 = new QuorumPeer(peers, s3dir, s3dir, port3, 3, 3, tickTime, initLimit, syncLimit, hq3);
+        qps.add(s3);
         Assert.assertEquals(port3, s3.getClientPort());
         
         LOG.info("creating QuorumPeer 4 port " + port4);
         QuorumHierarchical hq4 = new QuorumHierarchical(qp); 
         s4 = new QuorumPeer(peers, s4dir, s4dir, port4, 3, 4, tickTime, initLimit, syncLimit, hq4);
+        qps.add(s4);
         if (withObservers) {
             s4.setLearnerType(QuorumPeer.LearnerType.OBSERVER);
         }
@@ -164,6 +173,7 @@ public class HierarchicalQuorumTest extends ClientBase {
         LOG.info("creating QuorumPeer 5 port " + port5);
         QuorumHierarchical hq5 = new QuorumHierarchical(qp); 
         s5 = new QuorumPeer(peers, s5dir, s5dir, port5, 3, 5, tickTime, initLimit, syncLimit, hq5);
+        qps.add(s5);
         if (withObservers) {
             s5.setLearnerType(QuorumPeer.LearnerType.OBSERVER);
         }
@@ -219,6 +229,7 @@ public class HierarchicalQuorumTest extends ClientBase {
             ensureNames.add("name0=ReplicatedServer_id" + i);
         }
         JMXEnv.ensureAll(ensureNames.toArray(new String[ensureNames.size()]));
+        verifyElectionTimeTakenJMXAttribute(qps);
     }
 
     @Override
@@ -264,6 +275,33 @@ public class HierarchicalQuorumTest extends ClientBase {
         return createClient(watcher, hp);
     }
 
+    private void verifyElectionTimeTakenJMXAttribute(List<QuorumPeer> peers)
+            throws Exception {
+        LOG.info("Verify QuorumPeer#electionTimeTaken jmx bean attribute");
+
+        for (int i = 1; i <= peers.size(); i++) {
+            QuorumPeer qp = peers.get(i - 1);
+            if (qp.getLearnerType() == LearnerType.OBSERVER) {
+                continue; // Observer don't have electionTimeTaken attribute.
+            }
+            Long electionTimeTaken = -1L;
+            String bean = "";
+            if (qp.getPeerState() == ServerState.FOLLOWING) {
+                bean = String.format(
+                        "%s:name0=ReplicatedServer_id%d,name1=replica.%d,name2=Follower",
+                        CommonNames.DOMAIN, i, i);
+            } else if (qp.getPeerState() == ServerState.LEADING) {
+                bean = String.format(
+                        "%s:name0=ReplicatedServer_id%d,name1=replica.%d,name2=Leader",
+                        CommonNames.DOMAIN, i, i);
+            }
+            electionTimeTaken = (Long) JMXEnv.ensureBeanAttribute(bean,
+                    "ElectionTimeTaken");
+            Assert.assertTrue("Wrong electionTimeTaken value!",
+                    electionTimeTaken >= 0);
+        }
+    }
+
     @Test
     public void testHierarchicalQuorum() throws Throwable {
         cht.runHammer(5, 10);
diff --git a/src/java/test/org/apache/zookeeper/test/JMXEnv.java b/src/java/test/org/apache/zookeeper/test/JMXEnv.java
index f9cdef7..f67c67c 100644
--- a/src/java/test/org/apache/zookeeper/test/JMXEnv.java
+++ b/src/java/test/org/apache/zookeeper/test/JMXEnv.java
@@ -37,6 +37,7 @@ import junit.framework.TestCase;
 
 import org.apache.zookeeper.jmx.CommonNames;
 import org.apache.zookeeper.jmx.MBeanRegistry;
+import org.junit.Assert;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -274,4 +275,51 @@ public class JMXEnv {
         }
         return false;
     }
+
+    /**
+     * Ensure that the specified bean name and its attribute is registered. Note
+     * that these are components of the name. It waits in a loop up to 60
+     * seconds before failing if there is a mismatch. This will return the beans
+     * which are not matched.
+     *
+     * @param expectedName
+     *            - expected bean
+     * @param expectedAttribute
+     *            - expected attribute
+     * @return the value of the attribute
+     *
+     * @throws Exception
+     */
+    public static Object ensureBeanAttribute(String expectedName,
+            String expectedAttribute) throws Exception {
+        String value = "";
+        LOG.info("ensure bean:{}, attribute:{}", new Object[] { expectedName,
+                expectedAttribute });
+
+        Set<ObjectName> beans;
+        int nTry = 0;
+        do {
+            if (nTry++ > 0) {
+                Thread.sleep(500);
+            }
+            try {
+                beans = conn().queryNames(
+                        new ObjectName(CommonNames.DOMAIN + ":*"), null);
+            } catch (MalformedObjectNameException e) {
+                throw new RuntimeException(e);
+            }
+            LOG.info("expect:" + expectedName);
+            for (ObjectName bean : beans) {
+                // check the existence of name in bean
+                if (bean.toString().equals(expectedName)) {
+                    LOG.info("found:{} {}", new Object[] { expectedName, bean });
+                    return conn().getAttribute(bean, expectedAttribute);
+                }
+            }
+        } while (nTry < 120);
+        Assert.fail("Failed to find bean:" + expectedName + ", attribute:"
+                + expectedAttribute);
+        return value;
+    }
+
 }
diff --git a/src/java/test/org/apache/zookeeper/test/WatcherTest.java b/src/java/test/org/apache/zookeeper/test/WatcherTest.java
index 893f335..1c06690 100644
--- a/src/java/test/org/apache/zookeeper/test/WatcherTest.java
+++ b/src/java/test/org/apache/zookeeper/test/WatcherTest.java
@@ -45,6 +45,8 @@ import org.junit.Test;
 public class WatcherTest extends ClientBase {
     protected static final Logger LOG = LoggerFactory.getLogger(WatcherTest.class);
 
+    private long timeOfLastWatcherInvocation;
+
     private final class MyStatCallback implements StatCallback {
         int rc;
         public void processResult(int rc, String path, Object ctx, Stat stat) {
@@ -60,6 +62,7 @@ public class WatcherTest extends ClientBase {
         public void process(WatchedEvent event) {
             super.process(event);
             if (event.getType() != Event.EventType.None) {
+                timeOfLastWatcherInvocation = System.currentTimeMillis();
                 try {
                     events.put(event);
                 } catch (InterruptedException e) {
@@ -173,7 +176,6 @@ public class WatcherTest extends ClientBase {
     }
 
     final static int COUNT = 100;
-    boolean hasSeenDelete = true;
     /**
      * This test checks that watches for pending requests do not get triggered,
      * but watches set by previous requests do.
@@ -207,7 +209,7 @@ public class WatcherTest extends ClientBase {
        startServer();
        watches[COUNT/2-1].waitForConnected(60000);
        Assert.assertEquals(null, zk.exists("/test", false));
-       Thread.sleep(10);
+       waitForAllWatchers();
        for(int i = 0; i < COUNT/2; i++) {
            Assert.assertEquals("For " + i, 1, watches[i].events.size());
        }
@@ -221,6 +223,18 @@ public class WatcherTest extends ClientBase {
        Assert.assertEquals(COUNT, count[0]);
        zk.close();
     }
+
+    /**
+     * Wait until no watcher has been fired in the last second to ensure that all watches
+     * that are waiting to be fired have been fired
+     * @throws Exception
+     */
+    private void waitForAllWatchers() throws Exception {
+        timeOfLastWatcherInvocation = System.currentTimeMillis();
+        while (System.currentTimeMillis() - timeOfLastWatcherInvocation < 1000) {
+            Thread.sleep(1000);
+        }
+    }
     
     final int TIMEOUT = 5000;
 
diff --git a/src/lastRevision.bat b/src/lastRevision.bat
index e31a6b9..7814f7f 100644
--- a/src/lastRevision.bat
+++ b/src/lastRevision.bat
@@ -1,23 +1,22 @@
-echo off
-rem Licensed to the Apache Software Foundation (ASF) under one
-rem or more contributor license agreements.  See the NOTICE file
-rem distributed with this work for additional information
-rem regarding copyright ownership.  The ASF licenses this file
-rem to you under the Apache License, Version 2.0 (the
-rem "License"); you may not use this file except in compliance
-rem with the License.  You may obtain a copy of the License at
-rem
-rem     http://www.apache.org/licenses/LICENSE-2.0
-rem
-rem Unless required by applicable law or agreed to in writing, software
-rem distributed under the License is distributed on an "AS IS" BASIS,
-rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-rem See the License for the specific language governing permissions and
-rem limitations under the License.
-
-rem Find the current revision, store it in a file, for DOS
-svn info | findstr Revision > %1
-
-For /F "tokens=1,2 delims= " %%a In (%1) Do (
-	echo lastRevision=%%b> %1
-)
+echo off
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem     http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+rem Find the current revision, store it in a file, for DOS
+
+for /f "delims=" %%i in ('git rev-parse HEAD') do set rev=%%i
+       echo lastRevision=%rev% > %1
+)
diff --git a/src/lastRevision.sh b/src/lastRevision.sh
index a462990..0690c7d 100755
--- a/src/lastRevision.sh
+++ b/src/lastRevision.sh
@@ -16,6 +16,6 @@
 
 # Find the current revision, store it in a file
 FILE=$1
-LASTREV=`svn info | grep '^Revision' | sed -e 's/Revision: *//'`
+LASTREV=`git rev-parse HEAD`
 
 echo "lastRevision=${LASTREV}" > $FILE
diff --git a/src/pom.template b/src/pom.template
new file mode 100644
index 0000000..a02c0b3
--- /dev/null
+++ b/src/pom.template
@@ -0,0 +1,41 @@
+SKIP_LINE  ***************************************************************
+SKIP_LINE  * Licensed to the Apache Software Foundation (ASF) under one
+SKIP_LINE  * or more contributor license agreements.  See the NOTICE file
+SKIP_LINE  * distributed with this work for additional information
+SKIP_LINE  * regarding copyright ownership.  The ASF licenses this file
+SKIP_LINE  * to you under the Apache License, Version 2.0 (the
+SKIP_LINE  * "License"); you may not use this file except in compliance
+SKIP_LINE  * with the License.  You may obtain a copy of the License at
+SKIP_LINE  * 
+SKIP_LINE  *   http://www.apache.org/licenses/LICENSE-2.0
+SKIP_LINE  * 
+SKIP_LINE  * Unless required by applicable law or agreed to in writing,
+SKIP_LINE  * software distributed under the License is distributed on an
+SKIP_LINE  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+SKIP_LINE  * KIND, either express or implied.  See the License for the
+SKIP_LINE  * specific language governing permissions and limitations
+SKIP_LINE  * under the License.
+SKIP_LINE  ***************************************************************
+<?xml version="1.0" encoding="UTF-8"?>
+${ivy.pom.license}
+${ivy.pom.header}
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>${ivy.pom.groupId}</groupId>
+  <artifactId>${ivy.pom.artifactId}</artifactId>
+  <packaging>${ivy.pom.packaging}</packaging>
+  <version>${ivy.pom.version}</version>
+  <name>${ivy.pom.name}</name>
+  <description>${ivy.pom.description}</description>
+  <url>${ivy.pom.url}</url>
+
+  <licenses>
+    <license>
+      <name>The Apache Software License, Version 2.0</name>
+      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+      <distribution>repo</distribution>
+   </license>
+  </licenses>
+</project>
diff --git a/src/zookeeper.jute b/src/zookeeper.jute
index 6521e54..27a5a7e 100644
--- a/src/zookeeper.jute
+++ b/src/zookeeper.jute
@@ -220,6 +220,11 @@ module org.apache.zookeeper.server.quorum {
         buffer data; // Only significant when type is request
         vector<org.apache.zookeeper.data.Id> authinfo;
     }
+    class QuorumAuthPacket {
+        long magic;
+        int status;
+        buffer token;
+    }
 }
 
 module org.apache.zookeeper.server.persistence {
diff --git a/zookeeper-3.4.10.jar.asc b/zookeeper-3.4.10.jar.asc
new file mode 100644
index 0000000..23b8bfb
--- /dev/null
+++ b/zookeeper-3.4.10.jar.asc
@@ -0,0 +1,17 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1
+
+iQIcBAABAgAGBQJY07FsAAoJEPXOyzy16b0ta4kP/3Yd+0FkpFb72uwvl4ZOMmtv
+Z2aFweI8RPTrFUbCVQQSmFoKF7MhmaQyLRvOMdRAf4GBE+9J6D5EyS8/6iC7lI8W
+PhcgLKx3wvt+dp7eV6nKa8YutDpGuNuXcdLYgkki3FPOXQybrnxnV2KkXVSC9eUq
+wlMMSYnZ7ut/BVRAMEio/WK07JKU1SA1gv/c+q6fG2jxxJpLmS3jziRTg4hZBaiw
+i3ytEZvz+41r4mu4/7wprwdqYaQwX9fV49L7fYLwnCb8+uZLWTbPGP6csDlSfWJe
+nHtSS9KRxmlLWtTLZNWkLNZOHQ4pH5cvS/wCtX9+EC99YHEKY4uo6nbqMohhY+4K
+emIreIEGCH4WGcOBJHHI6ODsp0M4WPxv4Geyoc0J7b2hsPnyJIWxvRa0DK1J/M/n
+VH3R3dw2dqKU8wWEumPz9Cv9yBU8Zh87y1runGdZ8J1eP81BRIrRqUxTmwh4YPur
+GhCERWCq6ttAYEVoFui3acE0+VtXhZHnAKDX5/Uedb/DUx9Kruvh8+Ji4X0+mDjw
+vQb7D1YW+cOthhSUef+4X0kDieWQMFr1xkdiyokCzdtpCZOGOqSiiAdlxax1qujV
+VrE39ugLYkpvMXiCvwoDgGWLqk5xffLe2D/9uY0RjISGBn5LRHPR7r/nPIHOGvZH
+eOXHNEYPvlrnIIoFqzmn
+=LBTq
+-----END PGP SIGNATURE-----
diff --git a/zookeeper-3.4.10.jar.md5 b/zookeeper-3.4.10.jar.md5
new file mode 100644
index 0000000..3cdd729
--- /dev/null
+++ b/zookeeper-3.4.10.jar.md5
@@ -0,0 +1 @@
+450dbad05d829607bc45b9ccad789890
diff --git a/zookeeper-3.4.10.jar.sha1 b/zookeeper-3.4.10.jar.sha1
new file mode 100644
index 0000000..f48caca
--- /dev/null
+++ b/zookeeper-3.4.10.jar.sha1
@@ -0,0 +1 @@
+d276cb84ef1ace18245ead2f6b9aa88e21e14993
diff --git a/zookeeper-3.4.9.jar.asc b/zookeeper-3.4.9.jar.asc
deleted file mode 100644
index 0256d61..0000000
--- a/zookeeper-3.4.9.jar.asc
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1
-
-iQIcBAABAgAGBQJXu/58AAoJEPXOyzy16b0tF4kP/1l5+u3RsUGETQmiPKGRgaqp
-Aznx8rcbh4MpU+1sXj/XZBRmYTEVnylIH3gIHryfl40+OwdYBcLJ5KcRUBYrpga/
-wELvusafv9MIjmWXj47MblmGJMCP6KWnSPZL0S8Zxa6Dohe7Z+ZWyoXNSvdIBbHZ
-lrDAiMHDILvKaloo7h5yDeadxxBlwzRArhoC5q717frgrNQXvmg1YUwbgWaYYLyF
-yQXStA2jdmlaaiOlU045URBMPwhw+t9trxkiCgD/k/JpouSkqhlbDbjGx9330Mbz
-QKQUZY+6D1XF7E9HDwnhVdZHMwsgoOioptyck3I70a3DQYXGlp2uwdaGay9T5o3n
-y5P0xek5l6k29JD5S9NBMGEK8XrxmWAQFnMcQFouQbWmeGDKF0Aks8Ad78GuH7hj
-d1sWjsD7ZRNAyNdrStl/qhLa+2ycpHeSMOrKS+6xtclKofSB9L3c6tE+Gs5+n0ao
-QXtrglAxwEWb/KfhCdfW7kSp2TR5aswRd1SDW04MI2NhcpGN068/hMeTQBw0dOCW
-6r0oJh0fpxTV/78hE3islbyOdsarUA2axyoEoo5C5gkJjPV7yLiFBn8oqpmOR4I+
-NcJTf1/pHL6Q+gVRztZOAoMSDmr/p8J2STMK+pWPH4MuOCp6alC399i0A3dEKX4W
-TeUIZssMk3fUiHASo9EP
-=wyRd
------END PGP SIGNATURE-----
diff --git a/zookeeper-3.4.9.jar.md5 b/zookeeper-3.4.9.jar.md5
deleted file mode 100644
index 3c8d842..0000000
--- a/zookeeper-3.4.9.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-596d53448b98abeb50278c4181d10219
diff --git a/zookeeper-3.4.9.jar.sha1 b/zookeeper-3.4.9.jar.sha1
deleted file mode 100644
index 724523c..0000000
--- a/zookeeper-3.4.9.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a93d1909d78a9fa251844938f7a857a380c377e9

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/zookeeper.git



More information about the pkg-java-commits mailing list