├── CHANGELOG.md ├── LICENSE.txt ├── NOTICE.txt ├── OSSMETADATA ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── netflix │ └── curator │ └── x │ └── zkclientbridge │ └── CuratorZKClientBridge.java └── test └── java └── org └── I0Itec └── zkclient ├── AbstractBaseZkClientTest.java ├── AbstractConnectionTest.java ├── ContentWatcherTest.java ├── DeferredGatewayStarter.java ├── DistributedQueueTest.java ├── InMemoryConnectionTest.java ├── MemoryZkClientTest.java ├── ServerZkClientTest.java ├── TestUtil.java ├── ZkClientSerializationTest.java ├── ZkConnectionTest.java ├── testutil ├── ZkPathUtil.java └── ZkTestSystem.java └── util └── ZkPathUtilTest.java /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | END OF LIFE 2 | =============== 3 | 4 | Curator has move to Apache. Please see: http://curator.apache.incubator.org 5 | 6 | The previous Netflix branch is now in a branch named "archive". 7 | 8 | 2.0.0 - June 3, 2013 9 | ==================== 10 | * Possible final release - removed code branches that are now in Apache. Added 11 | a version of ZkClient bridge that uses Apache Curator. 12 | 13 | 1.3.4 - xxxxxxxxxxxxx 14 | ===================== 15 | * Issue 257: Fixed a race condition in LeaderLatch that cause the recipe to create 16 | two nodes in some edge cases. 17 | 18 | 1.3.3 - March 6, 2013 19 | ===================== 20 | * Issue 250: Restore support for passing null to usingWatcher(). 21 | 22 | * Issue 251: Allow a custom Executor Service to be used for PathChildrenCache. 23 | 24 | * DistributedDoubleBarrier wasn't handling wait expiration correctly and was sending negative 25 | numbers to wait(). 26 | 27 | * Issue 254: Check that executorService isn't null before closing. 28 | 29 | * Pull 258: Fix bad performing use of Guava's transform. 30 | 31 | 1.3.2 - February 6, 2013 32 | ======================== 33 | * MAJOR BUG FIX - Issue 232: ZooKeeper guarantees that "A watch object, or function/context pair, will only 34 | be triggered once for a given notification." Curator was breaking this guarantee by internally creating a 35 | new Watcher object each time one was needed. This is now fixed and ZooKeeper's guarantee is restored. Big 36 | thanks to user barkbay for his persistence and help on this. 37 | 38 | * Issue 247: POST_INITIALIZED_EVENT wasn't correctly handling an initially empty node. 39 | 40 | * Issue 245: Auth info specified in the CuratorFrameworkFactory.Builder was not being re-set in cases 41 | where the internal ZooKeeper handle was recreated. i.e. if the cluster has issues auth info would be lost. 42 | 43 | * The default watcher in the ZooKeeper handle is now cleared before the ZooKeeper handle is closed. This avoids 44 | an edge case where events meant for the old ZooKeeper handle get processed. 45 | 46 | 1.3.1 - January 28, 2013 47 | ======================== 48 | * Tightened up a possible race deep inside the connection management. 49 | 50 | * PathChildrenCache.rebuild() and PathChildrenCache.rebuildNode() were not handling deleted nodes. 51 | 52 | * Issue 237: New feature. PathChildrenCache now optionally posts an event when the initial cache is 53 | populated. To accommodate this behavior there is a new version of start() that takes an enum. See the 54 | Javadoc for each value. For this new behavior, use StartMode.POST_INITIALIZED_EVENT. Once the cache 55 | is initialized a PathChildrenCacheEvent.Type.INITIALIZED will be posted. Huge thanks to user philflesh 56 | for the idea and co-implementation. 57 | 58 | 1.3.0 - January 10, 2013 59 | ======================== 60 | * MAJOR CHANGE (thus a version bump): I'd always thought that if the client is disconnected from the server 61 | long enough then an Expired event would be generated. Testing, however, shows this not to be the case. I believe 62 | it's related to ZOOKEEPER-1159. The behavior associated with this is that if the clients lost connection to the 63 | cluster for longer than the session expiration they would _never_ be able to reconnect. The connection would 64 | be permanently lost. Many users were seeing this as endless log messages indicating "Connection timed out 65 | for connection...". As a workaround, in 1.3.0+ when the Curator state changes to LOST, a flag will be set 66 | so that the next time Curator needs to get the ZooKeeper instance, the current instance will be closed and a new 67 | ZooKeeper instance will be allocated (as if the session had expired). 68 | 69 | * Added checks for illegal namespaces. 70 | 71 | * Issue 232: NodeCache wasn't handling server connection issues well. It would repeatedly execute checkExists() 72 | with a watcher causing the heap to fill with watcher objects. 73 | 74 | * Issue 233: An internal idiom being used to create an EnsurePath instance with the parent of a passed in path 75 | wasn't correct. Due to an unfortunate implementation of ZKPaths.PathAndNode (mea culpa) the root path is specified 76 | differently than non-root paths. To work around this, I added a method to EnsurePath - excludingLast() - that 77 | can be used instead of the idiom. 78 | 79 | * Issue 230: Added a filter to control which IP address are returned by ServiceInstanceBuilder.getAllLocalIPs(). 80 | Set the filter via ServiceInstanceBuilder.setLocalIpFilter(). 81 | 82 | 1.2.6 - January 1, 2013 83 | ======================= 84 | * Issue 214: Added rebuildNode method to PathChildrenCache. 85 | 86 | * Added a NodeCache to compliment the PathChildrenCache. The doc is here: 87 | https://github.com/Netflix/curator/wiki/Node-Cache 88 | 89 | * Creating nodes in background wasn't handling createParentsIfNeeded. 90 | 91 | * Issue 216: Rewrote LeaderLatch to better handle connection/server instability. At the same time, made 92 | most of the calls async which will help concurrency and performance. 93 | 94 | * Issue 217: DistributedAtomicLong (et al) should use ensurePath internally to be consistent with 95 | other recipes. 96 | 97 | * Issue 220: When creating a ServiceCacheImpl, a PathChildrenCache is created. The cache loads all existing services, 98 | but because preloading does not create events, ServiceCacheImpl never notices this. ServiceCacheImpl.getInstances() 99 | will return an empty list. 100 | 101 | * Issue 221: client.getACL().forPath("/") throws a NullPointerException, because the Zookeeper 102 | API expects a Stat, but GetACLBuilderImpl initializes responseStat to null. 103 | 104 | * Issue 222: Counter and log messages reversed in RetryLoop.takeException(). 105 | 106 | * New feature: CuratorTempFramework. Temporary CuratorFramework instances are meant for single requests to 107 | ZooKeeper ensembles over a failure prone network such as a WAN. The APIs available from CuratorTempFramework 108 | are limited. Further, the connection will be closed after a period of inactivity. Based on an idea mentioned in a 109 | post by Camille Fournier: http://whilefalse.blogspot.com/2012/12/building-global-highly-available.html - details 110 | here: https://github.com/Netflix/curator/wiki/Temporary-Framework 111 | 112 | * Issue 224: ExponentialBackoffRetry was not protected against edge-cases where a too big maxRetries argument 113 | was used. It now also incorporates a maxSleep value. 114 | 115 | 1.2.5 - November 27, 2012 116 | ========================= 117 | * Depend on ZooKeeper 3.4.5 118 | 119 | * Issue 177: PathChildrenCache wasn't shutting down the executor when closed. Also, reworked the event 120 | queue to avoid potential herding of messages in unstable conditions. The herding could result in runaway 121 | memory allocation as reported in the issue. NOTE: due to this change, the PathChildrenCache node 122 | refresh code and the PathChildrenCacheListener notification threads have been merged. Do not block 123 | for very long inside of your PathChildrenCacheListener or you will prevent the cache from getting 124 | updated. 125 | 126 | * Issue 200: Post-creation services registered in ServiceDiscovery via registerService() were 127 | not being treated the same as the service passed in the constructor. Consequently they wouldn't get 128 | re-registered if there were connection problems. 129 | 130 | * Creating nodes withProtection() is now supported in the background. e.g. 131 | client.create().withProtection().inBackground()... 132 | 133 | * Added methods to InterProcessSemaphoreV2: setNodeData() and getParticipantNodes() and, to the Lease 134 | interface, getData(). 135 | 136 | * Issue 205 - already started error message was misleading. 137 | 138 | * Pull 209 - Fixed inconsistent API for get() in DiscoveryResource.java - thanks to user dougnukem 139 | 140 | * Issue 211 - Added getState() method to CuratorFramework. 141 | 142 | * Issue 212 - There wasn't a good way to update the data for a Service. I've added a new method 143 | ServiceDiscovery: updateService(). NOTE: this method requires all ServiceDiscovery instances to be using 144 | version 1.2.5 of Curator. Internally, ServiceCache now uses PathChildrenCache. 145 | 146 | * Pull 210 - For convenience, a version of {@link DiscoveryContext} that uses any generic type as the 147 | payload. Thanks to user dougnukem. 148 | 149 | 1.2.4 - November 2, 2012 150 | ======================== 151 | * Depend on ZooKeeper 3.4.4 152 | 153 | * Added a new Examples sub project - better late than never. 154 | 155 | * Guaranteed deletes were not working correctly if CuratorFramework.usingNamespace() was used. 156 | 157 | * I can't believe this has been like this for so long. The executor passed to listeners was never used. 158 | Doh!!! Major bug. 159 | 160 | * Issue 188: Display a meaningful message if the value node is corrupted 161 | 162 | * Issue 194: Initial sync() operation should occur immediately - like the change in 1.2.3 for all "background" 163 | operations. 164 | 165 | * Added support for ZK 3.4's read only operation as described here: 166 | http://wiki.apache.org/hadoop/ZooKeeper/GSoCReadOnlyMode - CuratorFrameworkFactory.Builder has a new 167 | method to set canBeReadOnly(). There is a new ConnectionState: READ_ONLY. Note: Your servers need to 168 | see a system property set "readonlymode.enabled" as true. This isn't documented anywhere that I can see. 169 | 170 | * Pull Request: 196 - Fix some issues with NamespaceFacade stemming from inconsistent state. Thanks to 171 | Answashe. 172 | 173 | * Issue 197 - Possible NullPointerException from ConnectionStateManager line 133 that is caused by a race 174 | condition. In CuratorFrameworkImpl, connectionStateManager.start() is called after client.start(). 175 | 176 | 1.2.3 - October 6, 2012 177 | ======================= 178 | * Previously, all background operations (i.e. when the inBackground() method is used) 179 | were put into a queue and processed in an internal thread. Curator does this to handle retries in 180 | background operations. This can be improved, however. The first time the background operation is 181 | executed the ZooKeeper method can be called directly - without queuing. This will get the operation 182 | into ZooKeeper immediately and will help prevent Curator's internal queue from backing up. 183 | 184 | * Issue 173: The DistributedQueue (and, thus, all the other queue recipes) was unnecessarily 185 | calling getChildren (with a watch) after each group of children was processed. It can just as easily 186 | wait for the internal cache to get its watch notified. This change creates an edge case, though, 187 | for ErrorMode.REQUEUE. Consequently, when in mode ErrorMode.REQUEUE the DistributedQueue now 188 | deletes the bad message and re-creates it. This required the use of ZooKeeper 3.4.x's transactions. 189 | So, if you use ErrorMode.REQUEUE you MUST be running ZooKeeper 3.4+. 190 | 191 | 1.2.2 - September 30, 2012 192 | ========================== 193 | * NOTE: The 1.0.x branch is not being released and has been deprecated. It was advised many versions 194 | ago that this was coming. So, here it is. 195 | 196 | * For ZKClient Bridge: 1. Previous method of sending initial connect event to ZKClient was 197 | unreliable; 2. Added an option to not close the curator instance 198 | 199 | * The default connection timeout has increased to 15 seconds. The default session timeout has 200 | increased to 60 seconds. These both can now be overridden via system properties: 201 | "curator-default-connection-timeout" and "curator-default-session-timeout". 202 | 203 | * Thanks to Ben Bangert: the InterProcessSemaphore waiting semantics weren't ideal. The nth 204 | waiting node has to wait for all nodes in front of it. I've improved this a bit. However, the algorithm 205 | used still suffers from potential out of order acquisition as well as potential starvation if a given client 206 | does not release a lease. Therefore, I'm deprecating InterProcessSemaphore in favor of the new 207 | InterProcessSemaphoreV2 which is based on Ben's algorithm. 208 | 209 | * Issue 164: The PathChildrenCache no longer clears its internal state when there is a connection 210 | issue. Consequently, the PathChildrenCacheEvent.Type values have changed. Instead of a RESET event 211 | there are events that match the ConnectionState events. 212 | 213 | 1.1.18/1.0.20 - September 4, 2012 214 | ================================= 215 | * New extension project: "ZKClient Bridge". A bridge between Curator and ZKClient. Useful for 216 | projects that would like to use Curator but don't want to risk porting working code that uses 217 | ZKClient. 218 | 219 | 1.1.17/1.0.18 - August 30, 2012 220 | =============================== 221 | * Issue 132: If namespace is set in builder, getNamespace() does not return it 222 | 223 | * Issue 131: If connection is lost to the server, the ServiceInstance needs to re-register once 224 | there is a re-connection. 225 | 226 | * PathChildrenCache was not sending UPDATE messages when a node's data changed in the case 227 | that false was passed in the constructor for cacheData. 228 | 229 | * Merge 136 from wt: Add eclipse support to gradle. 230 | 231 | * Merge 137 from pbh101: ConnectionState declares IOException, never throws it 232 | 233 | 1.1.16/1.0.17 - August 2, 2012 234 | ============================== 235 | * Merge 114 from amuraru: Make sure internal executor services are not started until startup. 236 | 237 | * Merge 116 from samuelgmartinez: Fix for Issue 115: Wrong behaviour in LeaderLatch when a candidate 238 | loses connection 239 | 240 | * Issue 118: Ignore nonode exceptions when deleting lock path 241 | 242 | * Added a non-reentrant mutex: InterProcessSemaphoreMutex. This mutex doesn't have the threading 243 | restrictions that InterProcessMutex has. This should help with issues 75 and 117. 244 | 245 | * Merge 122 from ithaka that addresses Issue #98 - JsonInstanceSerializer does not deserialize 246 | custom payload types. IMPORTANT! This change introduces a breaking incompatibility with List payloads 247 | that will show up in environments that mix the old code and the new code. The new code will throw a 248 | JsonMappingException when it attempts to read a List payload written by the old code. The old code, 249 | when it reads a List payload generated by the new code, will generate a list with two elements, 250 | the first of which is the class name of the List type, and the second of which is the original list. 251 | 252 | * Issue 121: Apply bytecode rewriting to turn off JMX registrations to TestingServer as well as 253 | TestingCluster. 254 | 255 | * Issue 125: Use ScheduledThreadPoolExecutor instead of blocking threads that have period work. 256 | 257 | * Issue 126: Added getNamespace() method. 258 | 259 | * Issue 120: Additional check for connection loss in DistributedDoubleBarrier. 260 | 261 | 1.1.15/1.0.16 - July 14, 2012 262 | ============================= 263 | * Added ChildReaper. This builds on the newly added Reaper. This utility monitors a single node 264 | and reaps empty children of that node. 265 | 266 | * Issue 107: The namespace wrapper was corrupting the path if the EnsurePath handler had an error. 267 | The best thing to do is let the code continue. 268 | 269 | * Issue 109: Make duplicate close() calls in CuratorFrameworkImpl a NOP instead of an error. 270 | 271 | * A more complete solution for background build-ups. The previous implementation did the retry sleep 272 | in the background process which ends up blocking ZooKeeper. During connection problems, this would 273 | cause ZooKeeper packets/watchers to back up. The new implementation uses a DelayQueue to simulate a 274 | sleep in the background. NOTE: this caused a change to the RetryPolicy APIs. 275 | 276 | 1.1.14/1.0.15 - July 6, 2012 277 | ============================ 278 | * Merge #100 from bbeck: Added BoundedExponentialBackoffRetry. 279 | 280 | * Merge #102 from artemip: Added REAP_UNTIL_GONE mode to Reaper; Remove items from activePaths once 281 | they are deletes; Tests 282 | 283 | * Issue 99: The Double Barrier should allow more than the max to enter the barrier. I don't see any 284 | harm in this. 285 | 286 | * Issue 103: Important change/fix for ExhibitorEnsembleProvider: the previous implementation wasn't 287 | handling outages very well. The connectionString could get stuck to an old value if the list of 288 | Exhibitors all went down and couldn't be contacted. Now, a backup provider is required and the backup 289 | is used to update the list of Exhibitors should there be connection problems. 290 | 291 | * IMPORTANT NOTE: The 1.0.x branch of Curator is now end of life. There will be a few more releases 292 | but please migrate to the 1.1.x branch. 293 | 294 | 1.1.13/1.0.14 - June 25, 2012 295 | ============================= 296 | * New queue features: a) bounded queues: use setMaxItems() in the builder to set an (approx) upper 297 | bound on the queue size; b) the builder now has an option to turn off background puts; c) queues now 298 | default to flushing remaining puts when closed - this can be controlled in the builder via 299 | finalFlushTime(). 300 | 301 | * Issue 82: Generalized (and deprecated) nonNamespaceView() by adding the usingNamespace() method 302 | to allow getting a facade of the client that uses a specified namespace. 303 | 304 | * createParentsIfNeeded() should now perform better. Instead of "pre" checking, it now only does the 305 | check if KeeperException.NoNodeException is thrown. LockInternals now uses this method and, so, should 306 | perform a bit better. 307 | 308 | * Added a new utility: Reaper. This can be used to clean up parent lock nodes so that they don't 309 | stay around as garbage. See the Utilities wiki for details: https://github.com/Netflix/curator/wiki/Utilities 310 | 311 | * Unit tests should be a lot less noisy. A system property now turns off most internal error logging. 312 | 313 | * Issue 88: Children processor should wait for all nodes to be processed before fetching more items 314 | 315 | 1.1.12/1.0.13 - June 5, 2012 316 | ============================ 317 | * Pull Request 81: Avoid invalid ipv6 localhost addresses 318 | 319 | * Another big bug: guaranteed deletions were not working with namespaces. 320 | 321 | 1.1.11/1.0.12 - June 1, 2012 322 | ============================ 323 | * MAJOR BUG FIX!!!! Many of the Curator recipes rely on the internal class LockInternals. It has 324 | a bug that exhibits when the ZooKeeper cluster is unstable. There is an edge case that causes 325 | LockInternals to leak a node in the lock path that it is managing. This results in a deadlock. The 326 | leak occurs when retries are exhausted. NOTE: TestLockCleanlinessWithFaults now tests for this 327 | condition. 328 | 329 | * Added some missing combinations in the backgrounding API 330 | 331 | * Added QueueSharder utility. Due to limitations in ZooKeeper's transport layer, a single queue 332 | will break if it has more than 10K-ish items in it. This class provides a facade over multiple 333 | distributed queues. It monitors the queues and if any one of them goes over a threshold, a new 334 | queue is added. Puts are distributed amongst the queues. 335 | 336 | * Issue 80: Check for null data before decompressing data in getData(). 337 | 338 | * Merge from user bbeck - enhanced the testing in-memory ZK server to handle some edge cases. A nice 339 | benefit is that it starts up faster. Thanks Brandon! 340 | 341 | 1.1.10/1.0.11 - May 17, 2012 342 | ============================ 343 | * Generalized the ProtectedEphemeralSequential so that it works with any create mode. 344 | withProtectedEphemeralSequential() is deprecated in favor of the new method withProtection(). 345 | 346 | * Update all uses of Preconditions to make sure they print a reasonable diagnostic message. 347 | 348 | * Added a new wrapped Watcher type that can throw exceptions as a convenience. The various 349 | usingWatcher() methods now can take CuratorWatcher instances. 350 | 351 | * InterProcessSemaphore and LeaderSelector weren't respecting the default bytes feature. 352 | 353 | * Make the default data for nodes be the local IP address. This helps in debugging and enables 354 | the deadlock analysis in Exhibitor. 355 | 356 | * New recipe added: DistributedDelayQueue 357 | 358 | 1.1.9/1.0.10 - May 10, 2012 359 | =========================== 360 | * Based on suggestion in Issue 67: Added new concept of UriSpec to the ServiceInstance in the 361 | Service Discovery Curator extension. 362 | 363 | * User "Pierre-Luc Bertrand" pointed out a potential race condition that would cause a SysConnected 364 | to get sent before an Expired. So, now I push the event to the parent watcher before resetting 365 | the connection in ConnectionState.process(WatchedEvent) 366 | 367 | * New Feature: SessionFailRetryLoop. Huge thanks to Pierre-Luc Bertrand for his work on this. 368 | SessionFailRetryLoop is a special type of retry loop that causes all Curator methods in a thread to 369 | fail when a session failure is detected. This enables sets of Curator operations that must be tied 370 | to a single ZooKeeper session. See Tech Note 3 for details: https://github.com/Netflix/curator/wiki/Tech-Note-3 371 | 372 | * Several users have expressed dissatisfaction with the LeaderSelector implementation - requiring a 373 | thread, etc. So, LeaderLatch has been added which behaves a lot like a CountDownLatch but for leader 374 | selection. 375 | 376 | 1.1.8/1.0.9 - April 17, 2012 377 | ============================ 378 | * Added methods to compress data via create() and setData() and to decompress data via getData(). The 379 | compression is GZIP by default. You can change this via the CuratorFrameworkFactory by specifying 380 | a CompressionProvider. 381 | 382 | * Added ZookeeperFactory to the client as a testing aid. 383 | 384 | * Added ACLProvider to make it easier to use ACLs and recipes. It can be set via the 385 | CuratorFrameworkFactory builder. 386 | 387 | * Several of the recipes were creating new watcher objects each time they were needed when the watcher(s) 388 | can be created once in the constructor. 389 | 390 | * Issue 62: DistributedQueue wasn't handling getting interrupted very well. It was logging an error. 391 | 392 | * Issue 64: wasn't handling SASL events. Any non-SysConnected event was being treated as a disconnection. 393 | 394 | * Issue 65: Accepted a pull request that fixes a bug in RetryUntilElapsed. 395 | 396 | * Issue 66: Bad log string - needed String.format() 397 | 398 | 1.1.7/1.0.8 - April 6, 2012 399 | =========================== 400 | * Accepted a change so that testng is testCompile in Gradle 401 | 402 | * Rewrote TestingServer and TestingCluster based on work by Jeremie BORDIER (ahfeel) 403 | 404 | * Rewrote the log4j property files 405 | 406 | * Moved to ZK 3.4.3 407 | 408 | * More work on the Exhibitor integration 409 | 410 | 1.1.5/1.0.6 - March 23, 2012 411 | ============================ 412 | * Moved to Gradle as the build system. 413 | 414 | * Added SimpleDistributedQueue, a drop-in replacement for the DistributedQueue that comes with the 415 | ZK distribution. 416 | 417 | * IMPORTANT CHANGE TO LeaderSelector. Previous versions of Curator overloaded the start() method 418 | to allow re-queueing. THIS IS NO LONGER SUPPORTED. Instead, there is a new method, requeue(), that 419 | does this. Calling start() more than once will now throw an exception. 420 | 421 | * LeaderSelector now supports auto re-queueing. In previous versions, it wasn't trivial to requeue 422 | the instance. Now, make a call to autoRequeue() to put the instance in a mode where it will requeue 423 | itself when the leader selector listener returns. 424 | 425 | * The mechanism that calls any kind of Curator listener wasn't protected against exceptions. Thus, 426 | an exception in a listener could break the listener event thread. 427 | 428 | * deleteDirectoryContents() no longer checks for sym links. This was a major issue in the Guava 429 | version and possibly one of the reasons they removed the method altogether. 430 | 431 | 1.1.4/1.0.5 - March 12, 2012 432 | ============================ 433 | * Introduced a parent interface for Queues so that they can have some common methods 434 | 435 | * Added new Recipe: DistributedIdQueue - a version of DistributedQueue that allows IDs to be 436 | associated with queue items. Items can then be removed from the queue if needed. 437 | 438 | * Curator can now be configured to poll a cluster of Exhibitor (https://github.com/Netflix/exhibitor) 439 | instances to get the connection string to use with the ZooKeeper client. Should the connection 440 | string change, any new connections will use the new connection string. 441 | 442 | 1.1.3/1.0.4 - March 7, 2012 443 | =========================== 444 | * Issue 27: This bug exposed major problems with the PathChildrenCache. I ended up completely 445 | rewriting it. The original version was very inefficient and prone to herding. This new version 446 | is as efficient as possible and adds some nice new features. The major new feature is that when 447 | calling start(), you can have the cache load an initial working set of data. 448 | 449 | * Issue 31: It turns out an instance of InterProcessMutex could not be shared in multiple threads. My 450 | assumption was that users would create a new InterProcessMutex per thread. But, this restriction is 451 | arbitrary. For comparison, the JDK Lock doesn't have this requirement. I've fixed this however it 452 | was a significant change internally. I'm counting on my tests to prove correctness. 453 | 454 | * EnsurePath wasn't doing its work in a RetryLoop. 455 | 456 | * Added a new class to the Test module, Timing, that is used to better coordinate timings in tests 457 | 458 | * LockInternals had a retry loop for all failures when it was only needed if the session expired 459 | and the lock node was lost. So, I refined the code to handle this specific case. 460 | 461 | * Issue 34: PathChildrenCache should ensure the path 462 | 463 | * Moved to Guava 11.x 464 | 465 | * Lots of work on the Gradle build. NOTE: Gradle will soon become the build system for Curator 466 | 467 | 1.1.2/1.0.3 - Feb. 8, 2012 468 | ========================== 469 | * Added listener to Queues to listen for put completion 470 | 471 | * Issue 24: If InterProcesMutex.release() failed to delete the node (due to connection errors, etc.) 472 | the instance was left in an inconsistent state that would cause a future call to acquire() to 473 | succeed without actually creating the lock. A new feature (see next bullet) was added to solve this 474 | problem: guaranteed deletes. The various lock-based recipes now use this feature. 475 | 476 | * New feature: guaranteed deletes. The delete builder now has a method that will record failed node 477 | deletions and attempt to delete them in the background until successful. NOTE: you will still get 478 | an exception when the deletion fails. But, you can be assured that as long as the CuratorFramework 479 | instance is open attempts will be made to delete the node: 480 | client.delete().guaranteed() ... 481 | 482 | 1.1.1/1.0.2 - Jan. 21, 2012 483 | =========================== 484 | * Issue 22: Make ServiceCache close itself down properly. 485 | 486 | * Issue 21: Move TestNG to the top-level pom and define its scope as test 487 | 488 | * Issue 17: ConnectionStateManager should use the builder's thread factory if present 489 | 490 | 1.1.0 - Jan. 5, 2012 491 | ===================== 492 | * 1.1.x marks a separate branch of Curator: 493 | - 1.0.x will stay compatible with ZooKeeper 3.3.x 494 | - 1.1.x+ will require ZooKeeper 3.4.x+ 495 | 496 | * Added support for ZooKeeper 3.4's Transactions: 497 | - CuratorFramework has a new method: inTransaction() that starts a 498 | transaction builder 499 | - See TestTransactions for examples 500 | 501 | 1.0.1 - Jan. 4, 2012 502 | ===================== 503 | * Updated and tested against ZooKeeper 3.4.2 504 | 505 | 1.0.0 - Dec. 31, 2011 506 | ===================== 507 | * Added a REST server for Service Discovery 508 | * Switched to slf4j for logging 509 | * Moved to 1.0 version 510 | * Curator is now feature complete 511 | 512 | 0.6.4 - Dec. 7, 2011 513 | ===================== 514 | * Added Barrier 515 | 516 | * Added Double Barrier 517 | 518 | * Added Read/Write lock 519 | 520 | * Added revocation to InterProcessMutex 521 | 522 | * Fixed (hopefully) intermittent failures with testRetry() 523 | 524 | * Updates/enhancements to Discovery based on suggestions from Eishay Smith 525 | 526 | 0.6.3 - Nov. 30, 2011 527 | ===================== 528 | * Added Service Discovery 529 | 530 | 0.6.1 - Nov. 18, 2011 531 | ===================== 532 | * Added new methods to LeaderSelector to identify/get all Participants 533 | 534 | * Moved to ZooKeeper 3.3.3 535 | 536 | * Made the TestingCluster not throw an assertion error due to internal JMX registrations 537 | in ZK. This is done with Javaassist ugliness. 538 | 539 | * Refactored listeners in Curator to a common methodology 540 | 541 | * Major changes to error handling. Adding a ConnectionStateManager that allows users to 542 | listen for connection changes. Connection loss is first treated as a recoverable Suspension. 543 | If the connection is not re-established, the state changes to connection loss. Any recipes 544 | that are affected by this have been updated. 545 | 546 | * PathChildrenCache now handles connection state changes much better. 547 | 548 | * All Curator created threads now have a meaningful name. 549 | 550 | 0.5.2 - Nov. 14, 2011 551 | ===================== 552 | * Jérémie Bordier posted on the ZK mailing list about a split brain issue with the Leader Selector. 553 | If the Leader is connected to a server that suffers a network partition, it needs to get notified 554 | that it has lost leadership. Curator handled this somewhat but only if the client application 555 | executed periodic ZooKeeper operations. I've enhanced the CuratorFramework implementation to check 556 | for disconnection and executed a background sync (with retries). This will cause any listener's 557 | unhandledError() method to get called when there is a network partition. 558 | 559 | * New utility: TestingCluster. Allows for testing with an in-memory ZK ensemble. 560 | 561 | * Reworked distributed atomic implementations. I was unhappy with the complexity of the previous 562 | one. Now, there's a simpler master implementation DistributedAtomicValue that is the basis for the 563 | others. Adding new versions should be simpler as well. 564 | 565 | 0.5.1 - Nov. 12, 2011 566 | ===================== 567 | * Another pass at fixing the semaphore. Went back to the model of the count being merely a 568 | convention. Added a new recipe for a SharedCount that can be used in place of the count convention 569 | with the semaphore. This is the best of both worlds. The semaphore code is a lot simpler and will 570 | perform better. Thanks to Monal Daxini for the idea. 571 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ==== 2 | Copyright 2012 Netflix, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | ==== 16 | 17 | 18 | Apache License 19 | Version 2.0, January 2004 20 | http://www.apache.org/licenses/ 21 | 22 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 23 | 24 | 1. Definitions. 25 | 26 | "License" shall mean the terms and conditions for use, reproduction, 27 | and distribution as defined by Sections 1 through 9 of this document. 28 | 29 | "Licensor" shall mean the copyright owner or entity authorized by 30 | the copyright owner that is granting the License. 31 | 32 | "Legal Entity" shall mean the union of the acting entity and all 33 | other entities that control, are controlled by, or are under common 34 | control with that entity. For the purposes of this definition, 35 | "control" means (i) the power, direct or indirect, to cause the 36 | direction or management of such entity, whether by contract or 37 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 38 | outstanding shares, or (iii) beneficial ownership of such entity. 39 | 40 | "You" (or "Your") shall mean an individual or Legal Entity 41 | exercising permissions granted by this License. 42 | 43 | "Source" form shall mean the preferred form for making modifications, 44 | including but not limited to software source code, documentation 45 | source, and configuration files. 46 | 47 | "Object" form shall mean any form resulting from mechanical 48 | transformation or translation of a Source form, including but 49 | not limited to compiled object code, generated documentation, 50 | and conversions to other media types. 51 | 52 | "Work" shall mean the work of authorship, whether in Source or 53 | Object form, made available under the License, as indicated by a 54 | copyright notice that is included in or attached to the work 55 | (an example is provided in the Appendix below). 56 | 57 | "Derivative Works" shall mean any work, whether in Source or Object 58 | form, that is based on (or derived from) the Work and for which the 59 | editorial revisions, annotations, elaborations, or other modifications 60 | represent, as a whole, an original work of authorship. For the purposes 61 | of this License, Derivative Works shall not include works that remain 62 | separable from, or merely link (or bind by name) to the interfaces of, 63 | the Work and Derivative Works thereof. 64 | 65 | "Contribution" shall mean any work of authorship, including 66 | the original version of the Work and any modifications or additions 67 | to that Work or Derivative Works thereof, that is intentionally 68 | submitted to Licensor for inclusion in the Work by the copyright owner 69 | or by an individual or Legal Entity authorized to submit on behalf of 70 | the copyright owner. For the purposes of this definition, "submitted" 71 | means any form of electronic, verbal, or written communication sent 72 | to the Licensor or its representatives, including but not limited to 73 | communication on electronic mailing lists, source code control systems, 74 | and issue tracking systems that are managed by, or on behalf of, the 75 | Licensor for the purpose of discussing and improving the Work, but 76 | excluding communication that is conspicuously marked or otherwise 77 | designated in writing by the copyright owner as "Not a Contribution." 78 | 79 | "Contributor" shall mean Licensor and any individual or Legal Entity 80 | on behalf of whom a Contribution has been received by Licensor and 81 | subsequently incorporated within the Work. 82 | 83 | 2. Grant of Copyright License. Subject to the terms and conditions of 84 | this License, each Contributor hereby grants to You a perpetual, 85 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 86 | copyright license to reproduce, prepare Derivative Works of, 87 | publicly display, publicly perform, sublicense, and distribute the 88 | Work and such Derivative Works in Source or Object form. 89 | 90 | 3. Grant of Patent License. Subject to the terms and conditions of 91 | this License, each Contributor hereby grants to You a perpetual, 92 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 93 | (except as stated in this section) patent license to make, have made, 94 | use, offer to sell, sell, import, and otherwise transfer the Work, 95 | where such license applies only to those patent claims licensable 96 | by such Contributor that are necessarily infringed by their 97 | Contribution(s) alone or by combination of their Contribution(s) 98 | with the Work to which such Contribution(s) was submitted. If You 99 | institute patent litigation against any entity (including a 100 | cross-claim or counterclaim in a lawsuit) alleging that the Work 101 | or a Contribution incorporated within the Work constitutes direct 102 | or contributory patent infringement, then any patent licenses 103 | granted to You under this License for that Work shall terminate 104 | as of the date such litigation is filed. 105 | 106 | 4. Redistribution. You may reproduce and distribute copies of the 107 | Work or Derivative Works thereof in any medium, with or without 108 | modifications, and in Source or Object form, provided that You 109 | meet the following conditions: 110 | 111 | (a) You must give any other recipients of the Work or 112 | Derivative Works a copy of this License; and 113 | 114 | (b) You must cause any modified files to carry prominent notices 115 | stating that You changed the files; and 116 | 117 | (c) You must retain, in the Source form of any Derivative Works 118 | that You distribute, all copyright, patent, trademark, and 119 | attribution notices from the Source form of the Work, 120 | excluding those notices that do not pertain to any part of 121 | the Derivative Works; and 122 | 123 | (d) If the Work includes a "NOTICE" text file as part of its 124 | distribution, then any Derivative Works that You distribute must 125 | include a readable copy of the attribution notices contained 126 | within such NOTICE file, excluding those notices that do not 127 | pertain to any part of the Derivative Works, in at least one 128 | of the following places: within a NOTICE text file distributed 129 | as part of the Derivative Works; within the Source form or 130 | documentation, if provided along with the Derivative Works; or, 131 | within a display generated by the Derivative Works, if and 132 | wherever such third-party notices normally appear. The contents 133 | of the NOTICE file are for informational purposes only and 134 | do not modify the License. You may add Your own attribution 135 | notices within Derivative Works that You distribute, alongside 136 | or as an addendum to the NOTICE text from the Work, provided 137 | that such additional attribution notices cannot be construed 138 | as modifying the License. 139 | 140 | You may add Your own copyright statement to Your modifications and 141 | may provide additional or different license terms and conditions 142 | for use, reproduction, or distribution of Your modifications, or 143 | for any such Derivative Works as a whole, provided Your use, 144 | reproduction, and distribution of the Work otherwise complies with 145 | the conditions stated in this License. 146 | 147 | 5. Submission of Contributions. Unless You explicitly state otherwise, 148 | any Contribution intentionally submitted for inclusion in the Work 149 | by You to the Licensor shall be under the terms and conditions of 150 | this License, without any additional terms or conditions. 151 | Notwithstanding the above, nothing herein shall supersede or modify 152 | the terms of any separate license agreement you may have executed 153 | with Licensor regarding such Contributions. 154 | 155 | 6. Trademarks. This License does not grant permission to use the trade 156 | names, trademarks, service marks, or product names of the Licensor, 157 | except as required for reasonable and customary use in describing the 158 | origin of the Work and reproducing the content of the NOTICE file. 159 | 160 | 7. Disclaimer of Warranty. Unless required by applicable law or 161 | agreed to in writing, Licensor provides the Work (and each 162 | Contributor provides its Contributions) on an "AS IS" BASIS, 163 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 164 | implied, including, without limitation, any warranties or conditions 165 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 166 | PARTICULAR PURPOSE. You are solely responsible for determining the 167 | appropriateness of using or redistributing the Work and assume any 168 | risks associated with Your exercise of permissions under this License. 169 | 170 | 8. Limitation of Liability. In no event and under no legal theory, 171 | whether in tort (including negligence), contract, or otherwise, 172 | unless required by applicable law (such as deliberate and grossly 173 | negligent acts) or agreed to in writing, shall any Contributor be 174 | liable to You for damages, including any direct, indirect, special, 175 | incidental, or consequential damages of any character arising as a 176 | result of this License or out of the use or inability to use the 177 | Work (including but not limited to damages for loss of goodwill, 178 | work stoppage, computer failure or malfunction, or any and all 179 | other commercial damages or losses), even if such Contributor 180 | has been advised of the possibility of such damages. 181 | 182 | 9. Accepting Warranty or Additional Liability. While redistributing 183 | the Work or Derivative Works thereof, You may choose to offer, 184 | and charge a fee for, acceptance of support, warranty, indemnity, 185 | or other liability obligations and/or rights consistent with this 186 | License. However, in accepting such obligations, You may act only 187 | on Your own behalf and on Your sole responsibility, not on behalf 188 | of any other Contributor, and only if You agree to indemnify, 189 | defend, and hold each Contributor harmless for any liability 190 | incurred by, or claims asserted against, such Contributor by reason 191 | of your accepting any such warranty or additional liability. 192 | 193 | END OF TERMS AND CONDITIONS 194 | 195 | APPENDIX: How to apply the Apache License to your work. 196 | 197 | To apply the Apache License to your work, attach the following 198 | boilerplate notice, with the fields enclosed by brackets "[]" 199 | replaced with your own identifying information. (Don't include 200 | the brackets!) The text should be enclosed in the appropriate 201 | comment syntax for the file format. We also recommend that a 202 | file or class name and description of purpose be included on the 203 | same "printed page" as the copyright notice for easier 204 | identification within third-party archives. 205 | 206 | Copyright 2011 Netflix 207 | 208 | Licensed under the Apache License, Version 2.0 (the "License"); 209 | you may not use this file except in compliance with the License. 210 | You may obtain a copy of the License at 211 | 212 | http://www.apache.org/licenses/LICENSE-2.0 213 | 214 | Unless required by applicable law or agreed to in writing, software 215 | distributed under the License is distributed on an "AS IS" BASIS, 216 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 217 | See the License for the specific language governing permissions and 218 | limitations under the License. 219 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | ==== 2 | Copyright 2012 Netflix, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | ==== 16 | 17 | Curator 18 | Copyright 2011 Netflix, Inc. 19 | 20 | This product includes software developed by The Apache Software 21 | Foundation (http://www.apache.org/). 22 | 23 | Alternative collection types provided by Google Guava from 24 | http://code.google.com/p/guava-libraries/ 25 | Copyright (C) 2007 Google Inc. 26 | -------------------------------------------------------------------------------- /OSSMETADATA: -------------------------------------------------------------------------------- 1 | osslifecycle=active 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | http://netflix.github.com/curator/curator.png 2 | 3 | # IMPORTANT NOTE!!! 4 | 5 | Curator has moved to Apache. The Netflix Curator project will remain to hold Netflix 6 | extensions to Curator. 7 | 8 | http://curator.apache.org 9 | 10 | The previous Netflix branch is now in a branch named "archive". 11 | 12 | # ZKCLIENT BRIDGE 13 | 14 | Please see the doc at https://github.com/Netflix/curator/wiki/ZKClient-Bridge 15 | 16 | Jordan Zimmerman (mailto:jzimmerman@netflix.com) 17 | 18 | # LICENSE 19 | 20 | Copyright 2011 Netflix, Inc. 21 | 22 | Licensed under the Apache License, Version 2.0 (the "License"); 23 | you may not use this file except in compliance with the License. 24 | You may obtain a copy of the License at 25 | 26 | http://www.apache.org/licenses/LICENSE-2.0 27 | 28 | Unless required by applicable law or agreed to in writing, software 29 | distributed under the License is distributed on an "AS IS" BASIS, 30 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 31 | See the License for the specific language governing permissions and 32 | limitations under the License. 33 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.netflix.curator 6 | curator-x-zkclient-bridge 7 | 3.0.1-SNAPSHOT 8 | 9 | 10 | org.sonatype.oss 11 | oss-parent 12 | 7 13 | 14 | 15 | 16 | 17 | The Apache Software License, Version 2.0 18 | http://www.apache.org/licenses/LICENSE-2.0.txt 19 | repo 20 | 21 | 22 | 23 | Apache Curator ZKClient Bridge 24 | 25 | You may be trying to integrate a project that uses ZKClient and would like a way to use Curator with that 26 | project (for example Kafka). The ZKClient Bridge accomplishes that. 27 | 28 | http://curator.apache.org 29 | 2011 30 | 31 | 32 | scm:git:git@github.com:randgalt/curator.git 33 | scm:git:git@github.com:randgalt/curator.git 34 | scm:git:git@github.com:randgalt/curator.git 35 | curator-x-zkclient-bridge-3.0.0 36 | 37 | 38 | 39 | UTF-8 40 | UTF-8 41 | UTF-8 42 | 43 | 1.6 44 | 45 | 1 46 | 47 | 2.9.1 48 | 2.5.1 49 | 3.1 50 | 2.17 51 | 1.0 52 | 2.5 53 | 1.6 54 | 2.4 55 | 56 | 2.7.1 57 | 0.4 58 | 4.12 59 | 1.10.19 60 | 61 | 62 | 63 | 64 | org.apache.curator 65 | curator-framework 66 | ${curator-version} 67 | 68 | 69 | 70 | com.101tec 71 | zkclient 72 | ${zkclient-version} 73 | 74 | 75 | 76 | org.apache.curator 77 | curator-test 78 | ${curator-version} 79 | test 80 | 81 | 82 | 83 | junit 84 | junit 85 | ${junit-version} 86 | test 87 | 88 | 89 | 90 | org.mockito 91 | mockito-core 92 | ${mockito-version} 93 | test 94 | 95 | 96 | 97 | 98 | 99 | oss 100 | 101 | 102 | 103 | maven-gpg-plugin 104 | ${maven-gpg-plugin-version} 105 | 106 | ${gpg.passphrase} 107 | true 108 | 109 | 110 | 111 | gpg 112 | 113 | sign 114 | 115 | verify 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | maven-install-plugin 128 | ${maven-install-plugin-version} 129 | 130 | true 131 | 132 | 133 | 134 | 135 | maven-compiler-plugin 136 | ${maven-compiler-plugin-version} 137 | 138 | ${jdk-version} 139 | ${jdk-version} 140 | 141 | 142 | 143 | 144 | maven-surefire-plugin 145 | ${maven-surefire-plugin-version} 146 | 147 | ${surefire-forkcount} 148 | false 149 | true 150 | 151 | 152 | 153 | 154 | maven-source-plugin 155 | ${maven-source-plugin-version} 156 | 157 | 158 | attach-sources 159 | 160 | jar 161 | 162 | 163 | 164 | 165 | 166 | 167 | maven-javadoc-plugin 168 | ${maven-javadoc-plugin-version} 169 | 170 | true 171 | 172 | -J-Xmx1g 173 | 174 | false 175 | 176 | 177 | 178 | jar 179 | 180 | jar 181 | 182 | package 183 | 184 | 185 | 186 | 187 | 188 | maven-release-plugin 189 | ${maven-release-plugin-version} 190 | 191 | -Dmaven.test.skip=true 192 | forked-path 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /src/main/java/com/netflix/curator/x/zkclientbridge/CuratorZKClientBridge.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.curator.x.zkclientbridge; 17 | 18 | import org.I0Itec.zkclient.IZkConnection; 19 | import org.apache.curator.framework.CuratorFramework; 20 | import org.apache.curator.framework.api.BackgroundCallback; 21 | import org.apache.curator.framework.api.CuratorEvent; 22 | import org.apache.curator.framework.api.CuratorListener; 23 | import org.apache.zookeeper.CreateMode; 24 | import org.apache.zookeeper.KeeperException; 25 | import org.apache.zookeeper.WatchedEvent; 26 | import org.apache.zookeeper.Watcher; 27 | import org.apache.zookeeper.ZooKeeper; 28 | import org.apache.zookeeper.data.Stat; 29 | import java.util.List; 30 | import java.util.concurrent.atomic.AtomicReference; 31 | 32 | /** 33 | *

34 | * Bridge between ZKClient and Curator. Accomplished via an implementation for 35 | * {@link IZkConnection} which is the abstraction ZKClient uses to wrap the raw ZooKeeper handle 36 | *

37 | * 38 | *

39 | * Once allocated, bridge to ZKClient via: 40 | * 41 | * ZKClient zkClient = new ZkClient(new CuratorZKClientBridge(curatorInstance, timeout)); 42 | * 43 | *

44 | */ 45 | public class CuratorZKClientBridge implements IZkConnection 46 | { 47 | private final CuratorFramework curator; 48 | private final AtomicReference listener = new AtomicReference(null); 49 | 50 | /** 51 | * @param curator Curator instance to bridge 52 | */ 53 | public CuratorZKClientBridge(CuratorFramework curator) 54 | { 55 | this.curator = curator; 56 | } 57 | 58 | /** 59 | * Return the client 60 | * 61 | * @return client 62 | */ 63 | public CuratorFramework getCurator() 64 | { 65 | return curator; 66 | } 67 | 68 | @Override 69 | public void connect(final Watcher watcher) 70 | { 71 | if ( watcher != null ) 72 | { 73 | CuratorListener localListener = new CuratorListener() 74 | { 75 | @Override 76 | public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception 77 | { 78 | if ( event.getWatchedEvent() != null ) 79 | { 80 | watcher.process(event.getWatchedEvent()); 81 | } 82 | } 83 | }; 84 | curator.getCuratorListenable().addListener(localListener); 85 | listener.set(localListener); 86 | 87 | try 88 | { 89 | BackgroundCallback callback = new BackgroundCallback() 90 | { 91 | @Override 92 | public void processResult(CuratorFramework client, CuratorEvent event) throws Exception 93 | { 94 | WatchedEvent fakeEvent = new WatchedEvent(Watcher.Event.EventType.None, curator.getZookeeperClient().isConnected() ? Watcher.Event.KeeperState.SyncConnected : Watcher.Event.KeeperState.Disconnected, null); 95 | watcher.process(fakeEvent); 96 | } 97 | }; 98 | curator.checkExists().inBackground(callback).forPath("/foo"); 99 | } 100 | catch ( Exception e ) 101 | { 102 | throw new RuntimeException(e); 103 | } 104 | } 105 | } 106 | 107 | @Override 108 | public void close() throws InterruptedException 109 | { 110 | // NOTE: the curator instance is NOT closed here 111 | 112 | CuratorListener localListener = listener.getAndSet(null); 113 | if ( localListener != null ) 114 | { 115 | curator.getCuratorListenable().removeListener(localListener); 116 | } 117 | } 118 | 119 | @Override 120 | public String create(String path, byte[] data, CreateMode mode) throws KeeperException, InterruptedException 121 | { 122 | try 123 | { 124 | return curator.create().withMode(mode).forPath(path, data); 125 | } 126 | catch ( Exception e ) 127 | { 128 | adjustException(e); 129 | } 130 | return null; // will never execute 131 | } 132 | 133 | @Override 134 | public void delete(String path) throws InterruptedException, KeeperException 135 | { 136 | try 137 | { 138 | curator.delete().forPath(path); 139 | } 140 | catch ( Exception e ) 141 | { 142 | adjustException(e); 143 | } 144 | } 145 | 146 | @Override 147 | public boolean exists(String path, boolean watch) throws KeeperException, InterruptedException 148 | { 149 | try 150 | { 151 | return watch ? (curator.checkExists().watched().forPath(path) != null) : (curator.checkExists().forPath(path) != null); 152 | } 153 | catch ( Exception e ) 154 | { 155 | adjustException(e); 156 | } 157 | return false; // will never execute 158 | } 159 | 160 | @Override 161 | public List getChildren(String path, boolean watch) throws KeeperException, InterruptedException 162 | { 163 | try 164 | { 165 | return watch ? curator.getChildren().watched().forPath(path) : curator.getChildren().forPath(path); 166 | } 167 | catch ( Exception e ) 168 | { 169 | adjustException(e); 170 | } 171 | return null; // will never execute 172 | } 173 | 174 | @Override 175 | public byte[] readData(String path, Stat stat, boolean watch) throws KeeperException, InterruptedException 176 | { 177 | try 178 | { 179 | if ( stat != null ) 180 | { 181 | return watch ? curator.getData().storingStatIn(stat).watched().forPath(path) : curator.getData().storingStatIn(stat).forPath(path); 182 | } 183 | else 184 | { 185 | return watch ? curator.getData().watched().forPath(path) : curator.getData().forPath(path); 186 | } 187 | } 188 | catch ( Exception e ) 189 | { 190 | adjustException(e); 191 | } 192 | return null; // will never execute 193 | } 194 | 195 | @Override 196 | public void writeData(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException 197 | { 198 | writeDataReturnStat(path, data, expectedVersion); 199 | } 200 | 201 | @Override 202 | public Stat writeDataReturnStat(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException { 203 | try 204 | { 205 | curator.setData().withVersion(expectedVersion).forPath(path, data); 206 | } 207 | catch ( Exception e ) 208 | { 209 | adjustException(e); 210 | } 211 | return null; // will never execute 212 | } 213 | 214 | @Override 215 | public ZooKeeper.States getZookeeperState() 216 | { 217 | try 218 | { 219 | return curator.getZookeeperClient().getZooKeeper().getState(); 220 | } 221 | catch ( Exception e ) 222 | { 223 | throw new RuntimeException(e); 224 | } 225 | } 226 | 227 | @Override 228 | public long getCreateTime(String path) throws KeeperException, InterruptedException 229 | { 230 | try 231 | { 232 | Stat stat = curator.checkExists().forPath(path); 233 | return (stat != null) ? stat.getCtime() : 0; 234 | } 235 | catch ( Exception e ) 236 | { 237 | adjustException(e); 238 | } 239 | return 0; 240 | } 241 | 242 | @Override 243 | public String getServers() 244 | { 245 | return curator.getZookeeperClient().getCurrentConnectionString(); 246 | } 247 | 248 | private void adjustException(Exception e) throws KeeperException, InterruptedException 249 | { 250 | if ( e instanceof KeeperException ) 251 | { 252 | throw (KeeperException)e; 253 | } 254 | 255 | if ( e instanceof InterruptedException ) 256 | { 257 | throw (InterruptedException)e; 258 | } 259 | 260 | throw new RuntimeException(e); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/AbstractBaseZkClientTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 4 | import org.apache.curator.test.TestingServer; 5 | import org.apache.log4j.Logger; 6 | import org.junit.After; 7 | import org.junit.Assert; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import java.util.List; 12 | import java.util.concurrent.Callable; 13 | import java.util.concurrent.TimeUnit; 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | 16 | import static org.junit.Assert.*; 17 | import static org.mockito.Mockito.mock; 18 | 19 | public abstract class AbstractBaseZkClientTest { 20 | 21 | protected static final Logger LOG = Logger.getLogger(AbstractBaseZkClientTest.class); 22 | protected TestingServer _zkServer; 23 | protected ZkClient _client; 24 | 25 | @Before 26 | public void setUp() throws Exception { 27 | LOG.info("------------ BEFORE -------------"); 28 | 29 | } 30 | 31 | @After 32 | public void tearDown() throws Exception { 33 | LOG.info("------------ AFTER -------------"); 34 | } 35 | 36 | @Test(expected = RuntimeException.class, timeout = 15000) 37 | public void testUnableToConnect() throws Exception { 38 | LOG.info("--- testUnableToConnect"); 39 | // we are using port 4711 to avoid conflicts with the zk server that is 40 | // started by the Spring context 41 | ZkTestSystem.createZkClient("localhost:4712"); 42 | } 43 | 44 | @Test 45 | public void testWriteAndRead() throws Exception { 46 | LOG.info("--- testWriteAndRead"); 47 | String data = "something"; 48 | String path = "/a"; 49 | _client.createPersistent(path, data); 50 | String data2 = _client.readData(path); 51 | Assert.assertEquals(data, data2); 52 | _client.delete(path); 53 | } 54 | 55 | @Test 56 | public void testDelete() throws Exception { 57 | LOG.info("--- testDelete"); 58 | String path = "/a"; 59 | assertFalse(_client.delete(path)); 60 | _client.createPersistent(path, null); 61 | assertTrue(_client.delete(path)); 62 | assertFalse(_client.delete(path)); 63 | } 64 | 65 | @Test 66 | public void testDeleteRecursive() throws Exception { 67 | LOG.info("--- testDeleteRecursive"); 68 | 69 | // should be able to call this on a not existing directory 70 | _client.deleteRecursive("/doesNotExist"); 71 | } 72 | 73 | @Test 74 | public void testWaitUntilExists() { 75 | LOG.info("--- testWaitUntilExists"); 76 | // create /gaga node asynchronously 77 | new Thread() { 78 | @Override 79 | public void run() { 80 | try { 81 | Thread.sleep(100); 82 | _client.createPersistent("/gaga"); 83 | } catch (Exception e) { 84 | // ignore 85 | } 86 | } 87 | }.start(); 88 | 89 | // wait until this was created 90 | assertTrue(_client.waitUntilExists("/gaga", TimeUnit.SECONDS, 5)); 91 | assertTrue(_client.exists("/gaga")); 92 | 93 | // waiting for /neverCreated should timeout 94 | assertFalse(_client.waitUntilExists("/neverCreated", TimeUnit.MILLISECONDS, 100)); 95 | } 96 | 97 | @Test 98 | public void testDataChanges1() throws Exception { 99 | LOG.info("--- testDataChanges1"); 100 | String path = "/a"; 101 | final Holder holder = new Holder(); 102 | 103 | IZkDataListener listener = new IZkDataListener() { 104 | 105 | @Override 106 | public void handleDataChange(String dataPath, Object data) throws Exception { 107 | holder.set((String) data); 108 | } 109 | 110 | @Override 111 | public void handleDataDeleted(String dataPath) throws Exception { 112 | holder.set(null); 113 | } 114 | }; 115 | _client.subscribeDataChanges(path, listener); 116 | _client.createPersistent(path, "aaa"); 117 | 118 | // wait some time to make sure the event was triggered 119 | String contentFromHolder = TestUtil.waitUntil("b", new Callable() { 120 | 121 | @Override 122 | public String call() throws Exception { 123 | return holder.get(); 124 | } 125 | }, TimeUnit.SECONDS, 5); 126 | 127 | assertEquals("aaa", contentFromHolder); 128 | } 129 | 130 | @Test 131 | public void testDataChanges2() throws Exception { 132 | LOG.info("--- testDataChanges2"); 133 | String path = "/a"; 134 | final AtomicInteger countChanged = new AtomicInteger(0); 135 | final AtomicInteger countDeleted = new AtomicInteger(0); 136 | 137 | IZkDataListener listener = new IZkDataListener() { 138 | 139 | @Override 140 | public void handleDataChange(String dataPath, Object data) throws Exception { 141 | countChanged.incrementAndGet(); 142 | } 143 | 144 | @Override 145 | public void handleDataDeleted(String dataPath) throws Exception { 146 | countDeleted.incrementAndGet(); 147 | } 148 | }; 149 | _client.subscribeDataChanges(path, listener); 150 | 151 | // create node 152 | _client.createPersistent(path, "aaa"); 153 | 154 | // wait some time to make sure the event was triggered 155 | TestUtil.waitUntil(1, new Callable() { 156 | 157 | @Override 158 | public Integer call() throws Exception { 159 | return countChanged.get(); 160 | } 161 | }, TimeUnit.SECONDS, 5); 162 | assertEquals(1, countChanged.get()); 163 | assertEquals(0, countDeleted.get()); 164 | 165 | countChanged.set(0); 166 | countDeleted.set(0); 167 | // delete node, this should trigger a delete event 168 | _client.delete(path); 169 | // wait some time to make sure the event was triggered 170 | TestUtil.waitUntil(1, new Callable() { 171 | 172 | @Override 173 | public Integer call() throws Exception { 174 | return countDeleted.get(); 175 | } 176 | }, TimeUnit.SECONDS, 5); 177 | assertEquals(0, countChanged.get()); 178 | assertEquals(1, countDeleted.get()); 179 | 180 | // test if watch was reinstalled after the file got deleted 181 | countChanged.set(0); 182 | _client.createPersistent(path, "aaa"); 183 | 184 | // wait some time to make sure the event was triggered 185 | TestUtil.waitUntil(1, new Callable() { 186 | 187 | @Override 188 | public Integer call() throws Exception { 189 | return countChanged.get(); 190 | } 191 | }, TimeUnit.SECONDS, 5); 192 | assertEquals(1, countChanged.get()); 193 | 194 | // test if changing the contents notifies the listener 195 | _client.writeData(path, "bbb"); 196 | 197 | // wait some time to make sure the event was triggered 198 | TestUtil.waitUntil(2, new Callable() { 199 | 200 | @Override 201 | public Integer call() throws Exception { 202 | return countChanged.get(); 203 | } 204 | }, TimeUnit.SECONDS, 5); 205 | assertEquals(2, countChanged.get()); 206 | } 207 | 208 | @Test(timeout = 15000) 209 | public void testHandleChildChanges() throws Exception { 210 | LOG.info("--- testHandleChildChanges"); 211 | String path = "/a"; 212 | final AtomicInteger count = new AtomicInteger(0); 213 | final Holder> children = new Holder>(); 214 | 215 | IZkChildListener listener = new IZkChildListener() { 216 | 217 | @Override 218 | public void handleChildChange(String parentPath, List currentChilds) throws Exception { 219 | count.incrementAndGet(); 220 | children.set(currentChilds); 221 | } 222 | }; 223 | _client.subscribeChildChanges(path, listener); 224 | 225 | // ---- 226 | // Create the root node should throw the first child change event 227 | // ---- 228 | _client.createPersistent(path); 229 | 230 | // wait some time to make sure the event was triggered 231 | TestUtil.waitUntil(1, new Callable() { 232 | 233 | @Override 234 | public Integer call() throws Exception { 235 | return count.get(); 236 | } 237 | }, TimeUnit.SECONDS, 5); 238 | assertEquals(1, count.get()); 239 | assertEquals(0, children.get().size()); 240 | 241 | // ---- 242 | // Creating a child node should throw another event 243 | // ---- 244 | count.set(0); 245 | _client.createPersistent(path + "/child1"); 246 | 247 | // wait some time to make sure the event was triggered 248 | TestUtil.waitUntil(1, new Callable() { 249 | 250 | @Override 251 | public Integer call() throws Exception { 252 | return count.get(); 253 | } 254 | }, TimeUnit.SECONDS, 5); 255 | assertEquals(1, count.get()); 256 | assertEquals(1, children.get().size()); 257 | assertEquals("child1", children.get().get(0)); 258 | 259 | // ---- 260 | // Creating another child and deleting the node should also throw an event 261 | // ---- 262 | count.set(0); 263 | _client.createPersistent(path + "/child2"); 264 | _client.deleteRecursive(path); 265 | 266 | // wait some time to make sure the event was triggered 267 | Boolean eventReceived = TestUtil.waitUntil(true, new Callable() { 268 | 269 | @Override 270 | public Boolean call() throws Exception { 271 | return count.get() > 0 && children.get() == null; 272 | } 273 | }, TimeUnit.SECONDS, 5); 274 | assertTrue(eventReceived); 275 | assertNull(children.get()); 276 | 277 | // ---- 278 | // Creating root again should throw an event 279 | // ---- 280 | count.set(0); 281 | _client.createPersistent(path); 282 | 283 | // wait some time to make sure the event was triggered 284 | eventReceived = TestUtil.waitUntil(true, new Callable() { 285 | 286 | @Override 287 | public Boolean call() throws Exception { 288 | return count.get() > 0 && children.get() != null; 289 | } 290 | }, TimeUnit.SECONDS, 5); 291 | assertTrue(eventReceived); 292 | assertEquals(0, children.get().size()); 293 | 294 | // ---- 295 | // Creating child now should throw an event 296 | // ---- 297 | count.set(0); 298 | _client.createPersistent(path + "/child"); 299 | 300 | // wait some time to make sure the event was triggered 301 | eventReceived = TestUtil.waitUntil(true, new Callable() { 302 | 303 | @Override 304 | public Boolean call() throws Exception { 305 | return count.get() > 0; 306 | } 307 | }, TimeUnit.SECONDS, 5); 308 | assertTrue(eventReceived); 309 | assertEquals(1, children.get().size()); 310 | assertEquals("child", children.get().get(0)); 311 | 312 | // ---- 313 | // Deleting root node should throw an event 314 | // ---- 315 | count.set(0); 316 | _client.deleteRecursive(path); 317 | 318 | // wait some time to make sure the event was triggered 319 | eventReceived = TestUtil.waitUntil(true, new Callable() { 320 | 321 | @Override 322 | public Boolean call() throws Exception { 323 | return count.get() > 0 && children.get() == null; 324 | } 325 | }, TimeUnit.SECONDS, 5); 326 | assertTrue(eventReceived); 327 | assertNull(children.get()); 328 | } 329 | 330 | @Test 331 | public void testGetCreationTime() throws Exception { 332 | long start = System.currentTimeMillis(); 333 | Thread.sleep(100); 334 | String path = "/a"; 335 | _client.createPersistent(path); 336 | Thread.sleep(100); 337 | long end = System.currentTimeMillis(); 338 | long creationTime = _client.getCreationTime(path); 339 | assertTrue(start < creationTime && end > creationTime); 340 | } 341 | 342 | @Test 343 | public void testNumberOfListeners() { 344 | IZkChildListener zkChildListener = mock(IZkChildListener.class); 345 | _client.subscribeChildChanges("/", zkChildListener); 346 | assertEquals(1, _client.numberOfListeners()); 347 | 348 | IZkDataListener zkDataListener = mock(IZkDataListener.class); 349 | _client.subscribeDataChanges("/a", zkDataListener); 350 | assertEquals(2, _client.numberOfListeners()); 351 | 352 | _client.subscribeDataChanges("/b", zkDataListener); 353 | assertEquals(3, _client.numberOfListeners()); 354 | 355 | IZkStateListener zkStateListener = mock(IZkStateListener.class); 356 | _client.subscribeStateChanges(zkStateListener); 357 | assertEquals(4, _client.numberOfListeners()); 358 | 359 | _client.unsubscribeChildChanges("/", zkChildListener); 360 | assertEquals(3, _client.numberOfListeners()); 361 | 362 | _client.unsubscribeDataChanges("/b", zkDataListener); 363 | assertEquals(2, _client.numberOfListeners()); 364 | 365 | _client.unsubscribeDataChanges("/a", zkDataListener); 366 | assertEquals(1, _client.numberOfListeners()); 367 | 368 | _client.unsubscribeStateChanges(zkStateListener); 369 | assertEquals(0, _client.numberOfListeners()); 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/AbstractConnectionTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import org.I0Itec.zkclient.testutil.ZkPathUtil; 4 | import org.apache.zookeeper.CreateMode; 5 | import org.apache.zookeeper.KeeperException; 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | import java.util.List; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | 13 | public abstract class AbstractConnectionTest { 14 | 15 | private final IZkConnection _connection; 16 | 17 | public AbstractConnectionTest(IZkConnection connection) { 18 | _connection = connection; 19 | } 20 | 21 | @Test 22 | public void testGetChildren_OnEmptyFileSystem() throws KeeperException, InterruptedException { 23 | InMemoryConnection connection = new InMemoryConnection(); 24 | List children = connection.getChildren("/", false); 25 | assertEquals(0, children.size()); 26 | } 27 | 28 | @Test 29 | @Ignore("I don't understand this test -JZ") 30 | public void testSequentials() throws KeeperException, InterruptedException { 31 | String sequentialPath = _connection.create("/a", new byte[0], CreateMode.EPHEMERAL_SEQUENTIAL); 32 | int firstSequential = Integer.parseInt(sequentialPath.substring(2)); 33 | assertEquals("/a" + ZkPathUtil.leadingZeros(firstSequential++, 10), sequentialPath); 34 | assertEquals("/a" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/a", new byte[0], CreateMode.EPHEMERAL_SEQUENTIAL)); 35 | assertEquals("/a" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/a", new byte[0], CreateMode.PERSISTENT_SEQUENTIAL)); 36 | assertEquals("/b" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/b", new byte[0], CreateMode.EPHEMERAL_SEQUENTIAL)); 37 | assertEquals("/b" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/b", new byte[0], CreateMode.PERSISTENT_SEQUENTIAL)); 38 | assertEquals("/a" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/a", new byte[0], CreateMode.EPHEMERAL_SEQUENTIAL)); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ContentWatcherTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.concurrent.Callable; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 9 | import org.apache.curator.test.TestingServer; 10 | import org.apache.log4j.Logger; 11 | import org.junit.After; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | 15 | public class ContentWatcherTest { 16 | 17 | private static final Logger LOG = Logger.getLogger(ContentWatcherTest.class); 18 | 19 | private static final String FILE_NAME = "/ContentWatcherTest"; 20 | private TestingServer _zkServer; 21 | private ZkClient _zkClient; 22 | 23 | @Before 24 | public void setUp() throws Exception { 25 | LOG.info("------------ BEFORE -------------"); 26 | _zkServer = new TestingServer(4711); 27 | _zkClient = ZkTestSystem.createZkClient(_zkServer.getConnectString()); 28 | } 29 | 30 | @After 31 | public void tearDown() throws Exception { 32 | if (_zkClient != null) { 33 | _zkClient.close(); 34 | } 35 | if (_zkServer != null) { 36 | _zkServer.close(); 37 | } 38 | LOG.info("------------ AFTER -------------"); 39 | } 40 | 41 | @Test 42 | public void testGetContent() throws Exception { 43 | LOG.info("--- testGetContent"); 44 | _zkClient.createPersistent(FILE_NAME, "a"); 45 | final ContentWatcher watcher = new ContentWatcher(_zkClient, FILE_NAME); 46 | watcher.start(); 47 | assertEquals("a", watcher.getContent()); 48 | 49 | // update the content 50 | _zkClient.writeData(FILE_NAME, "b"); 51 | 52 | String contentFromWatcher = TestUtil.waitUntil("b", new Callable() { 53 | 54 | @Override 55 | public String call() throws Exception { 56 | return watcher.getContent(); 57 | } 58 | }, TimeUnit.SECONDS, 5); 59 | 60 | assertEquals("b", contentFromWatcher); 61 | watcher.stop(); 62 | } 63 | 64 | @Test 65 | public void testGetContentWaitTillCreated() throws InterruptedException { 66 | LOG.info("--- testGetContentWaitTillCreated"); 67 | final Holder contentHolder = new Holder(); 68 | 69 | Thread thread = new Thread() { 70 | @Override 71 | public void run() { 72 | ContentWatcher watcher = new ContentWatcher(_zkClient, FILE_NAME); 73 | try { 74 | watcher.start(); 75 | contentHolder.set(watcher.getContent()); 76 | watcher.stop(); 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | }; 82 | 83 | thread.start(); 84 | 85 | // create content after 200ms 86 | Thread.sleep(200); 87 | _zkClient.createPersistent(FILE_NAME, "aaa"); 88 | 89 | // we give the thread some time to pick up the change 90 | thread.join(1000); 91 | assertEquals("aaa", contentHolder.get()); 92 | } 93 | 94 | @Test 95 | public void testHandlingNullContent() throws InterruptedException { 96 | LOG.info("--- testHandlingNullContent"); 97 | _zkClient.createPersistent(FILE_NAME, null); 98 | ContentWatcher watcher = new ContentWatcher(_zkClient, FILE_NAME); 99 | watcher.start(); 100 | assertEquals(null, watcher.getContent()); 101 | watcher.stop(); 102 | } 103 | 104 | @Test(timeout = 20000) 105 | public void testHandlingOfConnectionLoss() throws Exception { 106 | LOG.info("--- testHandlingOfConnectionLoss"); 107 | final Gateway gateway = new Gateway(4712, 4711); 108 | gateway.start(); 109 | final ZkClient zkClient = ZkTestSystem.createZkClient("localhost:4712"); 110 | 111 | // disconnect 112 | gateway.stop(); 113 | 114 | // reconnect after 250ms and create file with content 115 | new Thread() { 116 | @Override 117 | public void run() { 118 | try { 119 | Thread.sleep(250); 120 | gateway.start(); 121 | zkClient.createPersistent(FILE_NAME, "aaa"); 122 | zkClient.writeData(FILE_NAME, "b"); 123 | } catch (Exception e) { 124 | // ignore 125 | } 126 | } 127 | }.start(); 128 | 129 | final ContentWatcher watcher = new ContentWatcher(zkClient, FILE_NAME); 130 | watcher.start(); 131 | 132 | TestUtil.waitUntil("b", new Callable() { 133 | 134 | @Override 135 | public String call() throws Exception { 136 | return watcher.getContent(); 137 | } 138 | }, TimeUnit.SECONDS, 5); 139 | assertEquals("b", watcher.getContent()); 140 | 141 | watcher.stop(); 142 | zkClient.close(); 143 | gateway.stop(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/DeferredGatewayStarter.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | public class DeferredGatewayStarter extends Thread { 4 | 5 | private final Gateway _zkServer; 6 | private int _delay; 7 | 8 | public DeferredGatewayStarter(Gateway gateway, int delay) { 9 | _zkServer = gateway; 10 | _delay = delay; 11 | } 12 | 13 | @Override 14 | public void run() { 15 | try { 16 | Thread.sleep(_delay); 17 | _zkServer.start(); 18 | } catch (Exception e) { 19 | // ignore 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/DistributedQueueTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 4 | import org.apache.curator.test.TestingServer; 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import java.io.IOException; 10 | import java.util.*; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | import static org.junit.Assert.assertNull; 14 | 15 | public class DistributedQueueTest { 16 | 17 | private TestingServer _zkServer; 18 | private ZkClient _zkClient; 19 | 20 | @Before 21 | public void setUp() throws Exception { 22 | _zkServer = new TestingServer(4711); 23 | _zkClient = ZkTestSystem.createZkClient(_zkServer.getConnectString()); 24 | } 25 | 26 | @After 27 | public void tearDown() throws IOException { 28 | if (_zkClient != null) { 29 | _zkClient.close(); 30 | } 31 | if (_zkServer != null) { 32 | _zkServer.close(); 33 | } 34 | } 35 | 36 | @Test(timeout = 15000) 37 | public void testDistributedQueue() { 38 | _zkClient.createPersistent("/queue"); 39 | 40 | DistributedQueue distributedQueue = new DistributedQueue(_zkClient, "/queue"); 41 | distributedQueue.offer(17L); 42 | distributedQueue.offer(18L); 43 | distributedQueue.offer(19L); 44 | 45 | assertEquals(Long.valueOf(17L), distributedQueue.poll()); 46 | assertEquals(Long.valueOf(18L), distributedQueue.poll()); 47 | assertEquals(Long.valueOf(19L), distributedQueue.poll()); 48 | assertNull(distributedQueue.poll()); 49 | } 50 | 51 | @Test(timeout = 15000) 52 | public void testPeek() { 53 | _zkClient.createPersistent("/queue"); 54 | 55 | DistributedQueue distributedQueue = new DistributedQueue(_zkClient, "/queue"); 56 | distributedQueue.offer(17L); 57 | distributedQueue.offer(18L); 58 | 59 | assertEquals(Long.valueOf(17L), distributedQueue.peek()); 60 | assertEquals(Long.valueOf(17L), distributedQueue.peek()); 61 | assertEquals(Long.valueOf(17L), distributedQueue.poll()); 62 | assertEquals(Long.valueOf(18L), distributedQueue.peek()); 63 | assertEquals(Long.valueOf(18L), distributedQueue.poll()); 64 | assertNull(distributedQueue.peek()); 65 | } 66 | 67 | @Test(timeout = 30000) 68 | public void testMultipleReadingThreads() throws InterruptedException { 69 | _zkClient.createPersistent("/queue"); 70 | 71 | final DistributedQueue distributedQueue = new DistributedQueue(_zkClient, "/queue"); 72 | 73 | // insert 100 elements 74 | for (int i = 0; i < 100; i++) { 75 | distributedQueue.offer(new Long(i)); 76 | } 77 | 78 | // 3 reading threads 79 | final Set readElements = Collections.synchronizedSet(new HashSet()); 80 | List threads = new ArrayList(); 81 | final List exceptions = new Vector(); 82 | 83 | for (int i = 0; i < 3; i++) { 84 | Thread thread = new Thread() { 85 | @Override 86 | public void run() { 87 | try { 88 | while (true) { 89 | Long value = distributedQueue.poll(); 90 | if (value == null) { 91 | return; 92 | } 93 | readElements.add(value); 94 | } 95 | } catch (Exception e) { 96 | exceptions.add(e); 97 | e.printStackTrace(); 98 | } 99 | } 100 | }; 101 | threads.add(thread); 102 | thread.start(); 103 | } 104 | 105 | for (Thread thread : threads) { 106 | thread.join(); 107 | } 108 | 109 | assertEquals(0, exceptions.size()); 110 | assertEquals(100, readElements.size()); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/InMemoryConnectionTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | 4 | public class InMemoryConnectionTest extends AbstractConnectionTest { 5 | 6 | public InMemoryConnectionTest() { 7 | super(new InMemoryConnection()); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/MemoryZkClientTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import org.apache.zookeeper.CreateMode; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | public class MemoryZkClientTest extends AbstractBaseZkClientTest { 8 | 9 | @Override 10 | public void setUp() throws Exception { 11 | super.setUp(); 12 | _client = new ZkClient(new InMemoryConnection()); 13 | } 14 | 15 | @Override 16 | public void tearDown() throws Exception { 17 | super.tearDown(); 18 | _client.close(); 19 | } 20 | 21 | @Test 22 | public void testGetChildren() throws Exception { 23 | String path1 = "/a"; 24 | String path2 = "/a/a"; 25 | String path3 = "/a/a/a"; 26 | 27 | _client.create(path1, null, CreateMode.PERSISTENT); 28 | _client.create(path2, null, CreateMode.PERSISTENT); 29 | _client.create(path3, null, CreateMode.PERSISTENT); 30 | Assert.assertEquals(1, _client.getChildren(path1).size()); 31 | Assert.assertEquals(1, _client.getChildren(path2).size()); 32 | Assert.assertEquals(0, _client.getChildren(path3).size()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ServerZkClientTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import org.I0Itec.zkclient.exception.ZkBadVersionException; 4 | import org.I0Itec.zkclient.exception.ZkInterruptedException; 5 | import org.I0Itec.zkclient.exception.ZkNoNodeException; 6 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 7 | import org.apache.curator.test.TestingServer; 8 | import org.apache.zookeeper.Watcher.Event.KeeperState; 9 | import org.apache.zookeeper.data.Stat; 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.concurrent.Callable; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | import static org.junit.Assert.*; 19 | 20 | public class ServerZkClientTest extends AbstractBaseZkClientTest { 21 | @Override 22 | @Before 23 | public void setUp() throws Exception { 24 | super.setUp(); 25 | _zkServer = new TestingServer(4711); 26 | _client = ZkTestSystem.createZkClient("localhost:4711"); 27 | } 28 | 29 | @Override 30 | @After 31 | public void tearDown() throws Exception { 32 | super.tearDown(); 33 | _client.close(); 34 | _zkServer.close(); 35 | } 36 | 37 | @Test(timeout = 15000) 38 | public void testRetryUntilConnected() throws Exception { 39 | LOG.info("--- testRetryUntilConnected"); 40 | Gateway gateway = new Gateway(4712, 4711); 41 | gateway.start(); 42 | final IZkConnection zkConnection = ZkTestSystem.createZkConnection("localhost:4712"); 43 | final ZkClient zkClient = new ZkClient(zkConnection, 5000); 44 | 45 | gateway.stop(); 46 | 47 | // start server in 250ms 48 | new DeferredGatewayStarter(gateway, 250).start(); 49 | 50 | // this should work as soon as the connection is reestablished, if it 51 | // fails it throws a ConnectionLossException 52 | zkClient.retryUntilConnected(new Callable() { 53 | 54 | @Override 55 | public Object call() throws Exception { 56 | zkConnection.exists("/a", false); 57 | return null; 58 | } 59 | }); 60 | 61 | zkClient.close(); 62 | gateway.stop(); 63 | } 64 | 65 | @Test(timeout = 15000) 66 | public void testWaitUntilConnected() throws Exception { 67 | LOG.info("--- testWaitUntilConnected"); 68 | ZkClient _client = ZkTestSystem.createZkClient("localhost:4711"); 69 | 70 | _zkServer.close(); 71 | 72 | // the _client state should change to KeeperState.Disconnected 73 | assertTrue(_client.waitForKeeperState(KeeperState.Disconnected, 1, TimeUnit.SECONDS)); 74 | 75 | // connection should not be possible and timeout after 100ms 76 | assertFalse(_client.waitUntilConnected(100, TimeUnit.MILLISECONDS)); 77 | } 78 | 79 | /* 80 | JLZ - can't emulate 81 | 82 | @Test(timeout = 15000) 83 | public void testRetryUntilConnected_SessionExpiredException() { 84 | LOG.info("--- testRetryUntilConnected_SessionExpiredException"); 85 | 86 | // Use a tick time of 100ms, because the minimum session timeout is 2 x tick-time. 87 | // ZkServer zkServer = TestUtil.startZkServer("ZkClientTest-testSessionExpiredException", 4711, 100); 88 | Gateway gateway = new Gateway(4712, 4711); 89 | gateway.start(); 90 | 91 | // Use a session timeout of 200ms 92 | final ZkClient zkClient = ZkTestSystem.createZkClient("localhost:4712", 200, 5000); 93 | 94 | gateway.stop(); 95 | 96 | // Start server in 600ms, the session should have expired by then 97 | new DeferredGatewayStarter(gateway, 600).start(); 98 | 99 | // This should work as soon as a new session has been created (and the connection is reestablished), if it fails 100 | // it throws a SessionExpiredException 101 | zkClient.retryUntilConnected(new Callable() { 102 | 103 | @Override 104 | public Object call() throws Exception { 105 | zkClient.exists("/a"); 106 | return null; 107 | } 108 | }); 109 | 110 | zkClient.close(); 111 | // zkServer.shutdown(); 112 | gateway.stop(); 113 | } 114 | */ 115 | 116 | /* 117 | JLZ - can't emulate 118 | 119 | @Test(timeout = 15000) 120 | public void testChildListenerAfterSessionExpiredException() throws Exception { 121 | LOG.info("--- testChildListenerAfterSessionExpiredException"); 122 | 123 | int sessionTimeout = 200; 124 | ZkClient connectedClient = _zkServer.getZkClient(); 125 | connectedClient.createPersistent("/root"); 126 | 127 | Gateway gateway = new Gateway(4712, 4711); 128 | gateway.start(); 129 | 130 | final ZkClient disconnectedZkClient = new ZkClient("localhost:4712", sessionTimeout, 5000); 131 | final Holder> children = new Holder>(); 132 | disconnectedZkClient.subscribeChildChanges("/root", new IZkChildListener() { 133 | 134 | @Override 135 | public void handleChildChange(String parentPath, List currentChilds) throws Exception { 136 | children.set(currentChilds); 137 | } 138 | }); 139 | 140 | gateway.stop(); 141 | 142 | // The connected client now created a new child node 143 | connectedClient.createPersistent("/root/node"); 144 | 145 | // Wait for 3 x sessionTimeout, the session should have expired by then and start the gateway again 146 | Thread.sleep(sessionTimeout * 3); 147 | gateway.start(); 148 | 149 | Boolean hasOneChild = TestUtil.waitUntil(true, new Callable() { 150 | 151 | @Override 152 | public Boolean call() throws Exception { 153 | return children.get() != null && children.get().size() == 1; 154 | } 155 | }, TimeUnit.SECONDS, 5); 156 | 157 | assertTrue(hasOneChild); 158 | 159 | disconnectedZkClient.close(); 160 | gateway.stop(); 161 | } 162 | */ 163 | 164 | @Test(timeout = 10000) 165 | public void testZkClientConnectedToGatewayClosesQuickly() throws Exception { 166 | LOG.info("--- testZkClientConnectedToGatewayClosesQuickly"); 167 | final Gateway gateway = new Gateway(4712, 4711); 168 | gateway.start(); 169 | 170 | ZkClient zkClient = ZkTestSystem.createZkClient("localhost:4712"); 171 | zkClient.close(); 172 | 173 | gateway.stop(); 174 | } 175 | 176 | @Test 177 | public void testCountChildren() throws InterruptedException { 178 | assertEquals(0, _client.countChildren("/a")); 179 | _client.createPersistent("/a"); 180 | assertEquals(0, _client.countChildren("/a")); 181 | _client.createPersistent("/a/b"); 182 | assertEquals(1, _client.countChildren("/a")); 183 | 184 | // test concurrent access 185 | Thread thread = new Thread() { 186 | @Override 187 | public void run() { 188 | try { 189 | while (!isInterrupted()) { 190 | _client.createPersistent("/test"); 191 | _client.delete("/test"); 192 | } 193 | } catch (ZkInterruptedException e) { 194 | // ignore and finish 195 | } 196 | } 197 | }; 198 | 199 | thread.start(); 200 | for (int i = 0; i < 1000; i++) { 201 | assertEquals(0, _client.countChildren("/test")); 202 | } 203 | thread.interrupt(); 204 | thread.join(); 205 | } 206 | 207 | @Test 208 | public void testReadDataWithStat() { 209 | _client.createPersistent("/a", "data"); 210 | Stat stat = new Stat(); 211 | _client.readData("/a", stat); 212 | assertEquals(0, stat.getVersion()); 213 | assertTrue(stat.getDataLength() > 0); 214 | } 215 | 216 | @Test 217 | public void testWriteDataWithExpectedVersion() { 218 | _client.createPersistent("/a", "data"); 219 | _client.writeData("/a", "data2", 0); 220 | 221 | try { 222 | _client.writeData("/a", "data3", 0); 223 | fail("expected exception"); 224 | } catch (ZkBadVersionException e) { 225 | // expected 226 | } 227 | } 228 | 229 | @Test 230 | public void testCreateWithParentDirs() { 231 | String path = "/a/b"; 232 | try { 233 | _client.createPersistent(path, false); 234 | fail("should throw exception"); 235 | } catch (ZkNoNodeException e) { 236 | assertFalse(_client.exists(path)); 237 | } 238 | 239 | _client.createPersistent(path, true); 240 | assertTrue(_client.exists(path)); 241 | } 242 | 243 | @Test 244 | public void testUpdateSerialized() throws InterruptedException { 245 | _client.createPersistent("/a", 0); 246 | 247 | int numberOfThreads = 2; 248 | final int numberOfIncrementsPerThread = 100; 249 | 250 | List threads = new ArrayList(); 251 | for (int i = 0; i < numberOfThreads; i++) { 252 | Thread thread = new Thread() { 253 | @Override 254 | public void run() { 255 | for (int j = 0; j < numberOfIncrementsPerThread; j++) { 256 | _client.updateDataSerialized("/a", new DataUpdater() { 257 | 258 | @Override 259 | public Integer update(Integer integer) { 260 | return integer + 1; 261 | } 262 | }); 263 | } 264 | } 265 | }; 266 | thread.start(); 267 | threads.add(thread); 268 | } 269 | 270 | for (Thread thread : threads) { 271 | thread.join(); 272 | } 273 | 274 | Integer finalValue = _client.readData("/a"); 275 | assertEquals(numberOfIncrementsPerThread * numberOfThreads, finalValue.intValue()); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/TestUtil.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import org.mockito.exceptions.base.MockitoAssertionError; 4 | 5 | import java.util.concurrent.Callable; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | public class TestUtil { 9 | 10 | /** 11 | * This waits until the provided {@link Callable} returns an object that is equals to the given expected value or 12 | * the timeout has been reached. In both cases this method will return the return value of the latest 13 | * {@link Callable} execution. 14 | * 15 | * @param expectedValue 16 | * The expected value of the callable. 17 | * @param callable 18 | * The callable. 19 | * @param 20 | * The return type of the callable. 21 | * @param timeUnit 22 | * The timeout timeunit. 23 | * @param timeout 24 | * The timeout. 25 | * @return the return value of the latest {@link Callable} execution. 26 | * @throws Exception 27 | * @throws InterruptedException 28 | */ 29 | public static T waitUntil(T expectedValue, Callable callable, TimeUnit timeUnit, long timeout) throws Exception { 30 | long startTime = System.currentTimeMillis(); 31 | do { 32 | T actual = callable.call(); 33 | if (expectedValue.equals(actual)) { 34 | return actual; 35 | } 36 | if (System.currentTimeMillis() > startTime + timeUnit.toMillis(timeout)) { 37 | return actual; 38 | } 39 | Thread.sleep(50); 40 | } while (true); 41 | } 42 | 43 | /** 44 | * This waits until a mockito verification passed (which is provided in the runnable). This waits until the 45 | * virification passed or the timeout has been reached. If the timeout has been reached this method will rethrow the 46 | * {@link MockitoAssertionError} that comes from the mockito verification code. 47 | * 48 | * @param runnable 49 | * The runnable containing the mockito verification. 50 | * @param timeUnit 51 | * The timeout timeunit. 52 | * @param timeout 53 | * The timeout. 54 | * @throws InterruptedException 55 | */ 56 | public static void waitUntilVerified(Runnable runnable, TimeUnit timeUnit, int timeout) throws InterruptedException { 57 | long startTime = System.currentTimeMillis(); 58 | do { 59 | MockitoAssertionError exception = null; 60 | try { 61 | runnable.run(); 62 | } catch (MockitoAssertionError e) { 63 | exception = e; 64 | } 65 | if (exception == null) { 66 | return; 67 | } 68 | if (System.currentTimeMillis() > startTime + timeUnit.toMillis(timeout)) { 69 | throw exception; 70 | } 71 | Thread.sleep(50); 72 | } while (true); 73 | } 74 | } -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ZkClientSerializationTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | import static org.junit.Assert.assertEquals; 5 | 6 | import java.util.Random; 7 | 8 | import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer; 9 | import org.I0Itec.zkclient.serialize.SerializableSerializer; 10 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | 14 | public class ZkClientSerializationTest { 15 | 16 | @Rule 17 | public ZkTestSystem _zk = ZkTestSystem.getInstance(); 18 | 19 | @Test 20 | public void testBytes() throws Exception { 21 | ZkClient zkClient = ZkTestSystem.createZkClient(_zk.getZkServerAddress()); 22 | zkClient.setZkSerializer(new BytesPushThroughSerializer()); 23 | byte[] bytes = new byte[100]; 24 | new Random().nextBytes(bytes); 25 | zkClient.createPersistent("/a", bytes); 26 | byte[] readBytes = zkClient.readData("/a"); 27 | assertArrayEquals(bytes, readBytes); 28 | } 29 | 30 | @Test 31 | public void testSerializables() throws Exception { 32 | ZkClient zkClient = ZkTestSystem.createZkClient(_zk.getZkServerAddress()); 33 | zkClient.setZkSerializer(new SerializableSerializer()); 34 | String data = "hello world"; 35 | zkClient.createPersistent("/a", data); 36 | String readData = zkClient.readData("/a"); 37 | assertEquals(data, readData); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ZkConnectionTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 4 | import org.junit.Rule; 5 | 6 | public class ZkConnectionTest extends AbstractConnectionTest { 7 | 8 | @Rule 9 | public ZkTestSystem _zk = ZkTestSystem.getInstance(); 10 | 11 | public ZkConnectionTest() { 12 | super(establishConnection()); 13 | } 14 | 15 | private static IZkConnection establishConnection() { 16 | IZkConnection zkConnection = ZkTestSystem.createZkConnection("localhost:" + ZkTestSystem.getInstance().getZkServer().getPort()); 17 | new ZkClient(zkConnection);// connect 18 | return zkConnection; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/testutil/ZkPathUtil.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient.testutil; 2 | 3 | import org.I0Itec.zkclient.ZkClient; 4 | 5 | import java.util.List; 6 | 7 | // JLZ - copied from https://raw.github.com/sgroschupf/zkclient/master/src/main/java/org/I0Itec/zkclient/util/ZkPathUtil.java 8 | public class ZkPathUtil { 9 | 10 | public static String leadingZeros(long number, int numberOfLeadingZeros) { 11 | return String.format("%0" + numberOfLeadingZeros + "d", number); 12 | } 13 | 14 | public static String toString(ZkClient zkClient) { 15 | return toString(zkClient, "/", PathFilter.ALL); 16 | } 17 | 18 | public static String toString(ZkClient zkClient, String startPath, PathFilter pathFilter) { 19 | final int level = 1; 20 | final StringBuilder builder = new StringBuilder("+ (" + startPath + ")"); 21 | builder.append("\n"); 22 | addChildrenToStringBuilder(zkClient, pathFilter, level, builder, startPath); 23 | return builder.toString(); 24 | } 25 | 26 | private static void addChildrenToStringBuilder(ZkClient zkClient, PathFilter pathFilter, final int level, final StringBuilder builder, final String startPath) { 27 | final List children = zkClient.getChildren(startPath); 28 | for (final String node : children) { 29 | String nestedPath; 30 | if (startPath.endsWith("/")) { 31 | nestedPath = startPath + node; 32 | } else { 33 | nestedPath = startPath + "/" + node; 34 | } 35 | if (pathFilter.showChilds(nestedPath)) { 36 | builder.append(getSpaces(level - 1) + "'-" + "+" + node + "\n"); 37 | addChildrenToStringBuilder(zkClient, pathFilter, level + 1, builder, nestedPath); 38 | } else { 39 | builder.append(getSpaces(level - 1) + "'-" + "-" + node + " (contents hidden)\n"); 40 | } 41 | } 42 | } 43 | 44 | private static String getSpaces(final int level) { 45 | String s = ""; 46 | for (int i = 0; i < level; i++) { 47 | s += " "; 48 | } 49 | return s; 50 | } 51 | 52 | public static interface PathFilter { 53 | 54 | public static PathFilter ALL = new PathFilter() { 55 | 56 | @Override 57 | public boolean showChilds(String path) { 58 | return true; 59 | } 60 | }; 61 | 62 | boolean showChilds(String path); 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/testutil/ZkTestSystem.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient.testutil; 2 | 3 | import com.netflix.curator.x.zkclientbridge.CuratorZKClientBridge; 4 | import org.I0Itec.zkclient.IZkConnection; 5 | import org.I0Itec.zkclient.ZkClient; 6 | import org.apache.curator.framework.CuratorFramework; 7 | import org.apache.curator.framework.CuratorFrameworkFactory; 8 | import org.apache.curator.retry.RetryOneTime; 9 | import org.apache.curator.test.TestingServer; 10 | import org.apache.curator.test.Timing; 11 | import org.apache.log4j.Logger; 12 | import org.junit.rules.ExternalResource; 13 | import java.io.IOException; 14 | import java.util.List; 15 | 16 | public class ZkTestSystem extends ExternalResource { 17 | 18 | protected static final Logger LOG = Logger.getLogger(ZkTestSystem.class); 19 | 20 | private static int PORT = 10002; 21 | private static ZkTestSystem _instance; 22 | private TestingServer _zkServer; 23 | private ZkClient _zkClient; 24 | 25 | private ZkTestSystem() { 26 | LOG.info("~~~~~~~~~~~~~~~ starting zk system ~~~~~~~~~~~~~~~"); 27 | try { 28 | _zkServer = new TestingServer(PORT); 29 | _zkClient = ZkTestSystem.createZkClient(_zkServer.getConnectString()); 30 | } 31 | catch ( Exception e ) { 32 | throw new RuntimeException(e); 33 | } 34 | LOG.info("~~~~~~~~~~~~~~~ zk system started ~~~~~~~~~~~~~~~"); 35 | } 36 | 37 | @Override 38 | // executed before every test method 39 | protected void before() throws Throwable { 40 | cleanupZk(); 41 | } 42 | 43 | @Override 44 | // executed after every test method 45 | protected void after() { 46 | cleanupZk(); 47 | } 48 | 49 | private void cleanupZk() { 50 | LOG.info("cleanup zk namespace"); 51 | List children = getZkClient().getChildren("/"); 52 | for (String child : children) { 53 | if (!child.equals("zookeeper")) { 54 | getZkClient().deleteRecursive("/" + child); 55 | } 56 | } 57 | LOG.info("unsubscribing " + getZkClient().numberOfListeners() + " listeners"); 58 | getZkClient().unsubscribeAll(); 59 | } 60 | 61 | public static ZkTestSystem getInstance() { 62 | if (_instance == null) { 63 | _instance = new ZkTestSystem(); 64 | _instance.cleanupZk(); 65 | Runtime.getRuntime().addShutdownHook(new Thread() { 66 | @Override 67 | public void run() { 68 | LOG.info("shutting zk down"); 69 | try { 70 | getInstance().getZkClient().close(); 71 | getInstance().getZkServer().close(); 72 | } 73 | catch ( IOException e ) { 74 | throw new RuntimeException(e); 75 | } 76 | } 77 | }); 78 | } 79 | return _instance; 80 | } 81 | 82 | public TestingServer getZkServer() { 83 | return _zkServer; 84 | } 85 | 86 | public String getZkServerAddress() { 87 | return "localhost:" + getServerPort(); 88 | } 89 | 90 | public ZkClient getZkClient() { 91 | return _zkClient; 92 | } 93 | 94 | public int getServerPort() { 95 | return PORT; 96 | } 97 | 98 | public static IZkConnection createZkConnection(String connectString) { 99 | Timing timing = new Timing(); 100 | CuratorFramework client = CuratorFrameworkFactory.newClient(connectString, timing.session(), timing.connection(), new RetryOneTime(1)); 101 | client.start(); 102 | try 103 | { 104 | return new CuratorZKClientBridge(client); 105 | } 106 | catch ( Exception e ) 107 | { 108 | throw new RuntimeException(e); 109 | } 110 | } 111 | 112 | public static ZkClient createZkClient(String connectString) { 113 | try 114 | { 115 | Timing timing = new Timing(); 116 | return new ZkClient(createZkConnection(connectString), timing.connection()); 117 | } 118 | catch ( Exception e ) 119 | { 120 | throw new RuntimeException(e); 121 | } 122 | } 123 | 124 | public ZkClient createZkClient() { 125 | return createZkClient("localhost:" + PORT); 126 | } 127 | 128 | public void showStructure() { 129 | getZkClient().showFolders(System.out); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/util/ZkPathUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient.util; 2 | 3 | import junit.framework.TestCase; 4 | import org.I0Itec.zkclient.ZkClient; 5 | import org.I0Itec.zkclient.testutil.ZkPathUtil; 6 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 7 | import org.apache.curator.test.TestingServer; 8 | 9 | public class ZkPathUtilTest extends TestCase { 10 | 11 | protected TestingServer _zkServer; 12 | protected ZkClient _client; 13 | 14 | public void testToString() throws Exception { 15 | _zkServer = new TestingServer(4711); 16 | _client = ZkTestSystem.createZkClient("localhost:4711"); 17 | final String file1 = "/files/file1"; 18 | final String file2 = "/files/file2"; 19 | final String file3 = "/files/file2/file3"; 20 | _client.createPersistent(file1, true); 21 | _client.createPersistent(file2, true); 22 | _client.createPersistent(file3, true); 23 | 24 | String stringRepresentation = ZkPathUtil.toString(_client); 25 | System.out.println(stringRepresentation); 26 | System.out.println("-------------------------"); 27 | assertTrue(stringRepresentation.contains("file1")); 28 | assertTrue(stringRepresentation.contains("file2")); 29 | assertTrue(stringRepresentation.contains("file3")); 30 | 31 | // path filtering 32 | stringRepresentation = ZkPathUtil.toString(_client, "/", new ZkPathUtil.PathFilter() { 33 | @Override 34 | public boolean showChilds(String path) { 35 | return !file2.equals(path); 36 | } 37 | }); 38 | assertTrue(stringRepresentation.contains("file1")); 39 | assertTrue(stringRepresentation.contains("file2")); 40 | assertFalse(stringRepresentation.contains("file3")); 41 | 42 | // start path 43 | stringRepresentation = ZkPathUtil.toString(_client, file2, ZkPathUtil.PathFilter.ALL); 44 | assertFalse(stringRepresentation.contains("file1")); 45 | assertTrue(stringRepresentation.contains("file2")); 46 | assertTrue(stringRepresentation.contains("file3")); 47 | 48 | _zkServer.close(); 49 | } 50 | 51 | public void testLeadingZeros() throws Exception { 52 | assertEquals("0000000001", ZkPathUtil.leadingZeros(1, 10)); 53 | } 54 | } 55 | --------------------------------------------------------------------------------