[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