├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── github │ └── zkclient │ ├── AbstractListener.java │ ├── IZkChildListener.java │ ├── IZkClient.java │ ├── IZkDataListener.java │ ├── IZkStateListener.java │ ├── ServerCnxnFactory.java │ ├── ZkClient.java │ ├── ZkClientUtils.java │ ├── ZkConnection.java │ ├── ZkEventThread.java │ ├── ZkLock.java │ ├── ZkServer.java │ └── exception │ ├── ZkBadVersionException.java │ ├── ZkException.java │ ├── ZkInterruptedException.java │ ├── ZkMarshallingError.java │ ├── ZkNoNodeException.java │ ├── ZkNodeExistsException.java │ └── ZkTimeoutException.java └── test ├── java └── com │ └── github │ └── zkclient │ ├── DeferredGatewayStarter.java │ ├── Gateway.java │ ├── GatewayThread.java │ ├── PortUtils.java │ ├── TestUtil.java │ ├── ZkClientTest.java │ ├── ZkClientUtilsTest.java │ └── ZkServerDemo.java └── resources └── log4j.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .classpath 3 | .project 4 | .settings 5 | build-eclipse 6 | build 7 | junit* 8 | target 9 | pom-*.xml 10 | .idea/ 11 | *.iml 12 | doc -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2009 Stefan Groschupf 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## A simple and effective Java client for [zookeeper](http://zookeeper.apache.org). 3 | 4 | [![Build Status](https://travis-ci.org/adyliu/zkclient.png?branch=master)](https://travis-ci.org/adyliu/zkclient) 5 | 6 | 7 | ## Maven Dependency 8 | 9 | The latest version has been synchronized to [Maven Central Repository](http://repo1.maven.org/maven2/com/github/adyliu/zkclient/). 10 | 11 | 12 | com.github.adyliu 13 | zkclient 14 | 2.2.0 15 | 16 | 17 | 18 | * The version 2.x supports both zookeeper 3.3.x and 3.4.x+. 19 | * The version [1.x](http://repo1.maven.org/maven2/com/github/adyliu/zkclient/) needs zookeeper 3.3.x. 20 | 21 | ## Wiki & Usage 22 | 23 | The wiki links: [https://github.com/adyliu/zkclient/wiki](https://github.com/adyliu/zkclient/wiki) 24 | 25 | ## Contributors 26 | 27 | * [Ady Liu](https://github.com/adyliu) 28 | * [YANGLiiN](https://github.com/yangl) 29 | * [Peter Voss](https://github.com/sgroschupf/zkclient) (origin author) 30 | 31 | [Zookeeper client for java] -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.github.adyliu 6 | zkclient 7 | zookeeper client wrapper 8 | 2.2.0 9 | jar 10 | A zookeeper client wrapper. 11 | https://github.com/adyliu/zkclient 12 | 13 | 14 | The Apache Software License, Version 2.0 15 | http://www.apache.org/licenses/LICENSE-2.0.txt 16 | repo 17 | 18 | 19 | 20 | scm:git:git@github.com:adyliu/zkclient.git 21 | scm:git:git@github.com:adyliu/zkclient.git 22 | git@github.com:adyliu/zkclient.git 23 | 24 | 25 | 26 | imxylz 27 | Ady Liu 28 | imxylz@gmail.com 29 | https://github.com/adyliu 30 | 31 | developer 32 | 33 | 34 | 35 | pvoss 36 | Peter Voss 37 | info@petervoss.org 38 | https://github.com/sgroschupf/zkclient 39 | 40 | original author 41 | 42 | 43 | 44 | 45 | 46 | org.apache.zookeeper 47 | zookeeper 48 | [3.4.9,3.4.14] 49 | 50 | 51 | jline 52 | jline 53 | 54 | 55 | netty 56 | io.netty 57 | 58 | 59 | 60 | 61 | log4j 62 | log4j 63 | 1.2.17 64 | runtime 65 | 66 | 67 | org.slf4j 68 | slf4j-api 69 | 1.7.5 70 | 71 | 72 | org.slf4j 73 | slf4j-log4j12 74 | 1.7.5 75 | 76 | 77 | junit 78 | junit 79 | 4.11 80 | test 81 | 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-compiler-plugin 88 | 3.0 89 | 90 | 1.6 91 | 1.6 92 | UTF-8 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-resources-plugin 98 | 2.6 99 | 100 | UTF-8 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-source-plugin 106 | 2.2.1 107 | 108 | 109 | attach-sources 110 | verify 111 | 112 | jar-no-fork 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-javadoc-plugin 120 | 2.9.1 121 | 122 | 1.6 123 | UTF-8 124 | 125 | 126 | 127 | attach-javadocs 128 | 129 | jar 130 | 131 | 132 | 133 | 134 | 135 | org.sonatype.plugins 136 | nexus-staging-maven-plugin 137 | 1.6.7 138 | true 139 | 140 | ossrh 141 | https://oss.sonatype.org/ 142 | true 143 | 144 | 145 | 146 | org.apache.maven.plugins 147 | maven-gpg-plugin 148 | 1.5 149 | 150 | true 151 | 152 | 153 | 154 | sign-artifacts 155 | 156 | sign 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | ossrh 166 | https://oss.sonatype.org/content/repositories/snapshots 167 | 168 | 169 | ossrh 170 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/AbstractListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.github.zkclient; 5 | 6 | import java.util.List; 7 | 8 | import org.apache.zookeeper.Watcher.Event.KeeperState; 9 | 10 | /** 11 | * An abstract class for zookeeper listner 12 | * @author adyliu (imxylz@gmail.com) 13 | * @since 2012-12-4 14 | * @see IZkChildListener 15 | * @see IZkDataListener 16 | * @see IZkStateListener 17 | */ 18 | public abstract class AbstractListener implements IZkChildListener, IZkDataListener, IZkStateListener { 19 | 20 | @Override 21 | public void handleStateChanged(KeeperState state) throws Exception { 22 | } 23 | 24 | @Override 25 | public void handleNewSession() throws Exception { 26 | } 27 | 28 | @Override 29 | public void handleDataChange(String dataPath, byte[] data) throws Exception { 30 | } 31 | 32 | @Override 33 | public void handleDataDeleted(String dataPath) throws Exception { 34 | } 35 | 36 | @Override 37 | public void handleChildChange(String parentPath, List currentChildren) throws Exception { 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/IZkChildListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * An {@link IZkChildListener} can be registered at a {@link ZkClient} for listening on zk child changes for a given 22 | * path. 23 | *

24 | * Node: Also this listener re-subscribes it watch for the path on each zk event (zk watches are one-timers) is is not 25 | * guaranteed that events on the path are missing (see http://zookeeper.apache.org/doc/current/zookeeperProgrammers.html#ch_zkWatches). An 26 | * implementation of this class should take that into account. 27 | */ 28 | public interface IZkChildListener { 29 | 30 | /** 31 | * Called when the children of the given path changed. 32 | *

33 | * NOTICE: if subscribing a node(not exists), the event will be triggered while the node(parent) were created. 34 | *

35 | * 36 | * @param parentPath The parent path 37 | * @param currentChildren The children or null if the root node (parent path) was deleted. 38 | * @throws Exception any exception 39 | */ 40 | public void handleChildChange(String parentPath, List currentChildren) throws Exception; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/IZkClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.github.zkclient; 19 | 20 | import com.github.zkclient.exception.ZkException; 21 | import com.github.zkclient.exception.ZkInterruptedException; 22 | import com.github.zkclient.exception.ZkNoNodeException; 23 | import com.github.zkclient.exception.ZkNodeExistsException; 24 | import com.github.zkclient.exception.ZkTimeoutException; 25 | import org.apache.zookeeper.CreateMode; 26 | import org.apache.zookeeper.Watcher; 27 | import org.apache.zookeeper.Watcher.Event.KeeperState; 28 | import org.apache.zookeeper.ZooKeeper; 29 | import org.apache.zookeeper.data.Stat; 30 | 31 | import java.io.Closeable; 32 | import java.util.List; 33 | import java.util.concurrent.TimeUnit; 34 | 35 | /** 36 | * zookeeper client wrapper 37 | * 38 | * @author adyliu (imxylz@gmail.com) 39 | * @since 2.0 40 | */ 41 | public interface IZkClient extends Closeable { 42 | 43 | int DEFAULT_CONNECTION_TIMEOUT = 10000; 44 | 45 | int DEFAULT_SESSION_TIMEOUT = 30000; 46 | 47 | /** 48 | * Close the client. 49 | * 50 | * @throws ZkInterruptedException if interrupted while closing 51 | */ 52 | void close(); 53 | 54 | /** 55 | * Connect to ZooKeeper. 56 | * 57 | * @param timeout max waiting time(ms) until connected 58 | * @param watcher default watcher 59 | * @throws ZkInterruptedException if the connection timed out due to thread interruption 60 | * @throws ZkTimeoutException if the connection timed out 61 | * @throws IllegalStateException if the connection timed out due to thread interruption 62 | */ 63 | void connect(final long timeout, Watcher watcher); 64 | 65 | /** 66 | * Counts number of children for the given path. 67 | * 68 | * @param path zk path 69 | * @return number of children or 0 if path does not exist. 70 | */ 71 | int countChildren(String path); 72 | 73 | /** 74 | * Create a node. 75 | * 76 | * @param path zk path 77 | * @param data node data 78 | * @param mode create mode {@link CreateMode} 79 | * @return created path 80 | * @throws IllegalArgumentException if called from anything except the ZooKeeper event 81 | * thread 82 | * @throws ZkException if any ZooKeeper exception occurred 83 | */ 84 | String create(final String path, byte[] data, final CreateMode mode); 85 | 86 | /** 87 | * Create an ephemeral node with empty data 88 | * 89 | * @param path zk path 90 | * @throws ZkInterruptedException if operation was interrupted, or a required reconnection 91 | * got interrupted 92 | * @throws IllegalArgumentException if called from anything except the ZooKeeper event 93 | * thread 94 | * @throws ZkException if any ZooKeeper exception occurred 95 | */ 96 | void createEphemeral(final String path); 97 | 98 | /** 99 | * Create an ephemeral node. 100 | * 101 | * @param path the path for the node 102 | * @param data node data 103 | * @throws ZkInterruptedException if operation was interrupted, or a required reconnection 104 | * got interrupted 105 | * @throws IllegalArgumentException if called from anything except the ZooKeeper event 106 | * thread 107 | * @throws ZkException if any ZooKeeper exception occurred 108 | */ 109 | void createEphemeral(final String path, final byte[] data); 110 | 111 | /** 112 | * Create an ephemeral, sequential node. 113 | * 114 | * @param path the path for the node 115 | * @param data the data for the node 116 | * @return created path 117 | * @throws ZkInterruptedException if operation was interrupted, or a required reconnection 118 | * got interrupted 119 | * @throws IllegalArgumentException if called from anything except the ZooKeeper event 120 | * thread 121 | * @throws ZkException if any ZooKeeper exception occurred 122 | */ 123 | String createEphemeralSequential(final String path, final byte[] data); 124 | 125 | /** 126 | * Create a persistent node with empty data (null) 127 | * 128 | * @param path the path for the node 129 | * @throws ZkNodeExistsException if the node exists 130 | * @throws ZkNoNodeException if the parent node not exists 131 | * @throws ZkInterruptedException if operation was interrupted, or a required reconnection 132 | * got interrupted 133 | * @throws IllegalArgumentException if called from anything except the ZooKeeper event 134 | * thread 135 | * @throws ZkException if any ZooKeeper exception occurred 136 | * @throws RuntimeException any other exception 137 | * @see #createPersistent(String, boolean) 138 | */ 139 | void createPersistent(String path); 140 | 141 | /** 142 | * Create a persistent node with empty data (null) 143 | *

144 | * If the createParents is true, neither {@link ZkNodeExistsException} nor {@link com.github.zkclient.exception.ZkNoNodeException} were throwed. 145 | *

146 | * 147 | * @param path the path for the node 148 | * @param createParents if true all parent dirs are created as well and no 149 | * {@link ZkNodeExistsException} is thrown in case the path already exists 150 | * @throws ZkInterruptedException if operation was interrupted, or a required reconnection 151 | * got interrupted 152 | * @throws IllegalArgumentException if called from anything except the ZooKeeper event 153 | * thread 154 | * @throws ZkException if any ZooKeeper exception occurred 155 | * @throws RuntimeException any other exception 156 | */ 157 | void createPersistent(String path, boolean createParents); 158 | 159 | /** 160 | * Create a persistent node. 161 | * 162 | * @param path the path for the node 163 | * @param data node data 164 | * @throws ZkInterruptedException if operation was interrupted, or a required reconnection 165 | * got interrupted 166 | * @throws IllegalArgumentException if called from anything except the ZooKeeper event 167 | * thread 168 | * @throws ZkException if any ZooKeeper exception occurred 169 | * @throws RuntimeException any other exception 170 | */ 171 | void createPersistent(String path, byte[] data); 172 | 173 | /** 174 | * Create a persistent, sequental node. 175 | * 176 | * @param path the path for the node 177 | * @param data node data 178 | * @return create node's path 179 | * @throws ZkInterruptedException if operation was interrupted, or a required reconnection 180 | * got interrupted 181 | * @throws IllegalArgumentException if called from anything except the ZooKeeper event 182 | * thread 183 | * @throws ZkException if any ZooKeeper exception occurred 184 | * @throws RuntimeException any other exception 185 | */ 186 | String createPersistentSequential(String path, byte[] data); 187 | 188 | /** 189 | * delete a node 190 | * 191 | * @param path the path for the node 192 | * @return true if deleted; otherwise false 193 | */ 194 | boolean delete(final String path); 195 | 196 | /** 197 | * delete a node with all children 198 | * 199 | * @param path the path for the node 200 | * @return true if all deleted; otherwise false 201 | */ 202 | boolean deleteRecursive(String path); 203 | 204 | /** 205 | * check the node exists 206 | * 207 | * @param path the path for the node 208 | * @return true if the node exists 209 | */ 210 | boolean exists(final String path); 211 | 212 | /** 213 | * get the children for the node 214 | * 215 | * @param path the path for the node 216 | * @return the children node names or null (then node not exists) 217 | */ 218 | List getChildren(String path); 219 | 220 | /** 221 | * get the node creation time (unix milliseconds) 222 | * 223 | * @param path the path for the node 224 | * @return the unix milliseconds or -1 if node not exists 225 | */ 226 | long getCreationTime(String path); 227 | 228 | /** 229 | * all watcher number in this connection 230 | * 231 | * @return watcher number 232 | */ 233 | int numberOfListeners(); 234 | 235 | /** 236 | * read the data for the node 237 | * 238 | * @param path the path for the node 239 | * @return the data for the node 240 | * @throws com.github.zkclient.exception.ZkNoNodeException if the node not exists 241 | * @see #readData(String, boolean) 242 | */ 243 | byte[] readData(String path); 244 | 245 | /** 246 | * read the data for the node 247 | * 248 | * @param path the path for the node 249 | * @param returnNullIfPathNotExists if true no {@link com.github.zkclient.exception.ZkNoNodeException} thrown 250 | * @return the data for the node 251 | */ 252 | byte[] readData(String path, boolean returnNullIfPathNotExists); 253 | 254 | /** 255 | * read the data and stat for the node 256 | * 257 | * @param path the path for the node 258 | * @param stat the stat for the node 259 | * @return the data for the node 260 | * @see #readData(String, boolean) 261 | */ 262 | byte[] readData(String path, Stat stat); 263 | 264 | /** 265 | * subscribe the changing for children 266 | * 267 | * @param path the path for the node 268 | * @param listener the listener 269 | * @return the children list or null if the node not exists 270 | * @see IZkChildListener 271 | */ 272 | List subscribeChildChanges(String path, IZkChildListener listener); 273 | 274 | /** 275 | * subscribe the data changing for the node 276 | * 277 | * @param path the path for the node 278 | * @param listener the data changing listener 279 | * @see IZkDataListener 280 | */ 281 | void subscribeDataChanges(String path, IZkDataListener listener); 282 | 283 | /** 284 | * subscribe the connection state 285 | * 286 | * @param listener the connection listener 287 | * @see IZkStateListener 288 | */ 289 | void subscribeStateChanges(IZkStateListener listener); 290 | 291 | /** 292 | * unsubscribe all listeners for all path and connection state 293 | */ 294 | void unsubscribeAll(); 295 | 296 | /** 297 | * unsubscribe the child listener 298 | * 299 | * @param path the path for the node 300 | * @param childListener the listener 301 | */ 302 | void unsubscribeChildChanges(String path, IZkChildListener childListener); 303 | 304 | /** 305 | * unsubscribe the data changing for the node 306 | * 307 | * @param path the path for the node 308 | * @param dataListener the data changing listener 309 | */ 310 | void unsubscribeDataChanges(String path, IZkDataListener dataListener); 311 | 312 | /** 313 | * unsubscribe the connection state 314 | * 315 | * @param stateListener the connection listener 316 | */ 317 | void unsubscribeStateChanges(IZkStateListener stateListener); 318 | 319 | /** 320 | * Updates data of an existing znode. The current content of the znode is passed to the 321 | * {@link DataUpdater} that is passed into this method, which returns the new content. The 322 | * new content is only written back to ZooKeeper if nobody has modified the given znode in 323 | * between. If a concurrent change has been detected the new data of the znode is passed to 324 | * the updater once again until the new contents can be successfully written back to 325 | * ZooKeeper. 326 | * 327 | * @param path the path for the node 328 | * @param updater Updater that creates the new contents. 329 | */ 330 | void cas(String path, DataUpdater updater); 331 | 332 | /** 333 | * wait some time for the state 334 | * 335 | * @param keeperState the state 336 | * @param time some time 337 | * @param timeUnit the time unit 338 | * @return true if the connection state is the keeperState before the end time 339 | */ 340 | boolean waitForKeeperState(KeeperState keeperState, long time, TimeUnit timeUnit); 341 | 342 | /** 343 | * wait for the connected state. 344 | *
345 |      *     waitForKeeperState(KeeperState.SyncConnected, Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
346 |      * 
347 | * 348 | * @return true if the client connects the server 349 | * @throws ZkInterruptedException the thread was interrupted 350 | * @see #waitForKeeperState(org.apache.zookeeper.Watcher.Event.KeeperState, long, java.util.concurrent.TimeUnit) 351 | */ 352 | boolean waitUntilConnected() throws ZkInterruptedException; 353 | 354 | /** 355 | * wait for the connected state 356 | * 357 | * @param time soem time 358 | * @param timeUnit the time unit 359 | * @return true if the client connects the server before the end time 360 | */ 361 | boolean waitUntilConnected(long time, TimeUnit timeUnit); 362 | 363 | /** 364 | * wait some unit until the node exists 365 | * 366 | * @param path the path for the node 367 | * @param timeUnit the time unit 368 | * @param time some time 369 | * @return true if the node exists 370 | */ 371 | boolean waitUntilExists(String path, TimeUnit timeUnit, long time); 372 | 373 | /** 374 | * write the data for the node 375 | * 376 | * @param path the path for the node 377 | * @param data the data for the node 378 | * @return the stat for the node 379 | */ 380 | Stat writeData(String path, byte[] data); 381 | 382 | /** 383 | * write the data for the node 384 | * 385 | * @param path the path for the node 386 | * @param data the data for the node 387 | * @param expectedVersion the version for the node 388 | * @return the stat for the node 389 | * @see #cas(String, com.github.zkclient.IZkClient.DataUpdater) 390 | */ 391 | Stat writeData(String path, byte[] data, int expectedVersion); 392 | 393 | /** 394 | * multi operation for zookeeper 3.4.x 395 | * 396 | * @param ops operations 397 | * @return op result 398 | * @see org.apache.zookeeper.ZooKeeper#multi(Iterable) 399 | * @see org.apache.zookeeper.Op 400 | * @see org.apache.zookeeper.OpResult 401 | */ 402 | List multi(Iterable ops); 403 | 404 | /** 405 | * get the inner zookeeper client 406 | * 407 | * @return the inner zookeeper client 408 | */ 409 | ZooKeeper getZooKeeper(); 410 | 411 | /** 412 | * check the connecting state of zookeeper client 413 | * @return true if connected 414 | */ 415 | boolean isConnected(); 416 | 417 | /** 418 | * A CAS operation 419 | */ 420 | interface DataUpdater { 421 | 422 | /** 423 | * Updates the current data of a znode. 424 | * 425 | * @param currentData The current contents. 426 | * @return the new data that should be written back to ZooKeeper. 427 | */ 428 | public byte[] update(byte[] currentData); 429 | 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/IZkDataListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | /** 19 | * An {@link IZkDataListener} can be registered at a {@link ZkClient} for listening on zk data changes for a given path. 20 | *

21 | * Node: Also this listener re-subscribes it watch for the path on each zk event (zk watches are one-timers) is is not 22 | * guaranteed that events on the path are missing (see http://zookeeper.apache.org/doc/current/zookeeperProgrammers.html#ch_zkWatches). An 23 | * implementation of this class should take that into account. 24 | */ 25 | public interface IZkDataListener { 26 | 27 | public void handleDataChange(String dataPath, byte[] data) throws Exception; 28 | 29 | public void handleDataDeleted(String dataPath) throws Exception; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/IZkStateListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | import org.apache.zookeeper.Watcher.Event.KeeperState; 19 | 20 | public interface IZkStateListener { 21 | 22 | /** 23 | * Called when the zookeeper connection state has changed. 24 | * 25 | * @param state The new state. 26 | * @throws Exception On any error. 27 | */ 28 | public void handleStateChanged(KeeperState state) throws Exception; 29 | 30 | /** 31 | * Called after the zookeeper session has expired and a new session has been created. You would have to re-create 32 | * any ephemeral nodes here. 33 | * 34 | * @throws Exception On any error. 35 | */ 36 | public void handleNewSession() throws Exception; 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/ServerCnxnFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.github.zkclient; 5 | 6 | import java.lang.reflect.InvocationTargetException; 7 | import java.lang.reflect.Method; 8 | import java.net.InetSocketAddress; 9 | 10 | import org.apache.zookeeper.server.ZooKeeperServer; 11 | 12 | import com.github.zkclient.ZkClientUtils.ZkVersion; 13 | 14 | /** 15 | * Adapter for zookeeper 3.3.x/3.4.x 16 | * 17 | * @author adyliu (imxylz@gmail.com) 18 | * @since 2012-11-27 19 | */ 20 | public class ServerCnxnFactory { 21 | 22 | private static final String zk33Factory = "org.apache.zookeeper.server.NIOServerCnxn$Factory"; 23 | private static final String zk34Factory = "org.apache.zookeeper.server.ServerCnxnFactory"; 24 | 25 | private Object target = null; 26 | private Method shutdownMethod = null; 27 | private Method joinMethod = null; 28 | private Method startupMethod = null; 29 | 30 | public void startup(ZooKeeperServer server) throws InterruptedException { 31 | try { 32 | startupMethod.invoke(target, server); 33 | } catch (IllegalArgumentException e) { 34 | throw e; 35 | } catch (IllegalAccessException e) { 36 | throw new RuntimeException(e); 37 | } catch (InvocationTargetException e) { 38 | Throwable t = e.getTargetException(); 39 | if (t instanceof InterruptedException) { 40 | throw (InterruptedException) t; 41 | } 42 | throw new RuntimeException(t); 43 | } 44 | } 45 | 46 | private static ServerCnxnFactory createFactory(final String hostname, int port, int maxcc) { 47 | ServerCnxnFactory factory = new ServerCnxnFactory(); 48 | 49 | try { 50 | if (ZkClientUtils.zkVersion == ZkVersion.V33) { 51 | Class clazz = Class.forName(zk33Factory); 52 | factory.target = clazz.getDeclaredConstructor(InetSocketAddress.class).newInstance(new InetSocketAddress(port)); 53 | factory.shutdownMethod = clazz.getDeclaredMethod("shutdown", new Class[0]); 54 | factory.joinMethod = clazz.getMethod("join", new Class[0]); 55 | factory.startupMethod = clazz.getDeclaredMethod("startup", ZooKeeperServer.class); 56 | } else { 57 | Class clazz = Class.forName(zk34Factory); 58 | factory.target = clazz.getMethod("createFactory", int.class, int.class).invoke(null, port, maxcc); 59 | factory.shutdownMethod = clazz.getDeclaredMethod("shutdown", new Class[0]); 60 | factory.joinMethod = clazz.getDeclaredMethod("join", new Class[0]); 61 | factory.startupMethod = clazz.getMethod("startup", ZooKeeperServer.class); 62 | } 63 | 64 | } catch (IllegalArgumentException e) { 65 | throw new RuntimeException("unsupported zookeeper version", e); 66 | } catch (SecurityException e) { 67 | throw new RuntimeException(e.getMessage(), e); 68 | } catch (InstantiationException e) { 69 | throw new RuntimeException(e.getMessage(), e); 70 | } catch (IllegalAccessException e) { 71 | throw new RuntimeException(e.getMessage(), e); 72 | } catch (InvocationTargetException e) { 73 | throw new RuntimeException(e.getMessage(), e); 74 | } catch (NoSuchMethodException e) { 75 | throw new RuntimeException("unsupported zookeeper version", e); 76 | } catch (ClassNotFoundException e) { 77 | throw new RuntimeException("unsupported zookeeper version", e); 78 | } 79 | 80 | return factory; 81 | } 82 | 83 | public static ServerCnxnFactory createFactory(int port, int maxcc) { 84 | return createFactory(null, port, maxcc); 85 | } 86 | 87 | public void shutdown() { 88 | try { 89 | shutdownMethod.invoke(target, new Object[0]); 90 | } catch (IllegalArgumentException e) { 91 | throw e; 92 | } catch (IllegalAccessException e) { 93 | throw new RuntimeException(e); 94 | } catch (InvocationTargetException e) { 95 | Throwable t = e.getTargetException(); 96 | throw new RuntimeException(t); 97 | } 98 | } 99 | 100 | public void join() throws InterruptedException { 101 | try { 102 | joinMethod.invoke(target, new Object[0]); 103 | } catch (IllegalArgumentException e) { 104 | throw e; 105 | } catch (IllegalAccessException e) { 106 | throw new RuntimeException(e); 107 | } catch (InvocationTargetException e) { 108 | Throwable t = e.getTargetException(); 109 | if (t instanceof InterruptedException) { 110 | throw (InterruptedException) t; 111 | } 112 | throw new RuntimeException(t); 113 | } 114 | } 115 | 116 | public static void main(String[] args) throws Exception { 117 | ServerCnxnFactory factory = ServerCnxnFactory.createFactory(8123, 60); 118 | factory.shutdown(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/ZkClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | import com.github.zkclient.ZkEventThread.ZkEvent; 19 | import com.github.zkclient.exception.ZkBadVersionException; 20 | import com.github.zkclient.exception.ZkException; 21 | import com.github.zkclient.exception.ZkInterruptedException; 22 | import com.github.zkclient.exception.ZkNoNodeException; 23 | import com.github.zkclient.exception.ZkNodeExistsException; 24 | import com.github.zkclient.exception.ZkTimeoutException; 25 | import org.apache.zookeeper.CreateMode; 26 | import org.apache.zookeeper.KeeperException; 27 | import org.apache.zookeeper.KeeperException.ConnectionLossException; 28 | import org.apache.zookeeper.KeeperException.SessionExpiredException; 29 | import org.apache.zookeeper.WatchedEvent; 30 | import org.apache.zookeeper.Watcher; 31 | import org.apache.zookeeper.Watcher.Event.EventType; 32 | import org.apache.zookeeper.Watcher.Event.KeeperState; 33 | import org.apache.zookeeper.ZooKeeper; 34 | import org.apache.zookeeper.ZooKeeper.States; 35 | import org.apache.zookeeper.data.Stat; 36 | import org.slf4j.Logger; 37 | import org.slf4j.LoggerFactory; 38 | 39 | import java.util.Date; 40 | import java.util.List; 41 | import java.util.Map; 42 | import java.util.Map.Entry; 43 | import java.util.Set; 44 | import java.util.concurrent.Callable; 45 | import java.util.concurrent.ConcurrentHashMap; 46 | import java.util.concurrent.CopyOnWriteArraySet; 47 | import java.util.concurrent.TimeUnit; 48 | 49 | /** 50 | * Zookeeper client 51 | *

52 | * The client is thread-safety 53 | *

54 | */ 55 | public class ZkClient implements Watcher, IZkClient { 56 | 57 | private final static Logger LOG = LoggerFactory.getLogger(ZkClient.class); 58 | 59 | protected ZkConnection _connection; 60 | 61 | private final Map> _childListener = new ConcurrentHashMap>(); 62 | 63 | private final Map> _dataListener = new ConcurrentHashMap>(); 64 | 65 | private final Set _stateListener = new CopyOnWriteArraySet(); 66 | 67 | private volatile KeeperState _currentState; 68 | 69 | private final ZkLock _zkEventLock = new ZkLock(); 70 | 71 | private volatile boolean _shutdownTriggered; 72 | 73 | private ZkEventThread _eventThread; 74 | 75 | private Thread _zookeeperEventThread; 76 | 77 | /** 78 | * Create a client with default connection timeout and default session timeout 79 | * 80 | * @param connectString zookeeper connection string 81 | * comma separated host:port pairs, each corresponding to a zk 82 | * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002" If 83 | * the optional chroot suffix is used the example would look 84 | * like: "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a" 85 | * where the client would be rooted at "/app/a" and all paths 86 | * would be relative to this root - ie getting/setting/etc... 87 | * "/foo/bar" would result in operations being run on 88 | * "/app/a/foo/bar" (from the server perspective). 89 | * @see IZkClient#DEFAULT_CONNECTION_TIMEOUT 90 | * @see IZkClient#DEFAULT_SESSION_TIMEOUT 91 | */ 92 | public ZkClient(String connectString) { 93 | this(connectString, DEFAULT_CONNECTION_TIMEOUT); 94 | } 95 | 96 | /** 97 | * Create a client 98 | * 99 | * @param connectString zookeeper connection string 100 | * comma separated host:port pairs, each corresponding to a zk 101 | * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002" If 102 | * the optional chroot suffix is used the example would look 103 | * like: "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a" 104 | * where the client would be rooted at "/app/a" and all paths 105 | * would be relative to this root - ie getting/setting/etc... 106 | * "/foo/bar" would result in operations being run on 107 | * "/app/a/foo/bar" (from the server perspective). 108 | * @param connectionTimeout connection timeout in milliseconds 109 | */ 110 | public ZkClient(String connectString, int connectionTimeout) { 111 | this(connectString, DEFAULT_SESSION_TIMEOUT, connectionTimeout); 112 | } 113 | 114 | /** 115 | * Create a client 116 | * 117 | * @param connectString zookeeper connection string 118 | * comma separated host:port pairs, each corresponding to a zk 119 | * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002" If 120 | * the optional chroot suffix is used the example would look 121 | * like: "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a" 122 | * where the client would be rooted at "/app/a" and all paths 123 | * would be relative to this root - ie getting/setting/etc... 124 | * "/foo/bar" would result in operations being run on 125 | * "/app/a/foo/bar" (from the server perspective). 126 | * @param sessionTimeout session timeout in milliseconds 127 | * @param connectionTimeout connection timeout in milliseconds 128 | */ 129 | public ZkClient(String connectString, int sessionTimeout, int connectionTimeout) { 130 | this(new ZkConnection(connectString, sessionTimeout), connectionTimeout); 131 | } 132 | 133 | /** 134 | * Create a client with special implementation 135 | * 136 | * @param zkConnection special client 137 | * @param connectionTimeout connection timeout in milliseconds 138 | */ 139 | public ZkClient(ZkConnection zkConnection, int connectionTimeout) { 140 | _connection = zkConnection; 141 | connect(connectionTimeout, this); 142 | } 143 | 144 | 145 | public List subscribeChildChanges(String path, IZkChildListener listener) { 146 | synchronized (_childListener) { 147 | Set listeners = _childListener.get(path); 148 | if (listeners == null) { 149 | listeners = new CopyOnWriteArraySet(); 150 | _childListener.put(path, listeners); 151 | } 152 | listeners.add(listener); 153 | } 154 | return watchForChilds(path); 155 | } 156 | 157 | public void unsubscribeChildChanges(String path, IZkChildListener childListener) { 158 | synchronized (_childListener) { 159 | final Set listeners = _childListener.get(path); 160 | if (listeners != null) { 161 | listeners.remove(childListener); 162 | } 163 | } 164 | } 165 | 166 | public void subscribeDataChanges(String path, IZkDataListener listener) { 167 | Set listeners; 168 | synchronized (_dataListener) { 169 | listeners = _dataListener.get(path); 170 | if (listeners == null) { 171 | listeners = new CopyOnWriteArraySet(); 172 | _dataListener.put(path, listeners); 173 | } 174 | listeners.add(listener); 175 | } 176 | watchForData(path); 177 | LOG.debug("Subscribed data changes for " + path); 178 | } 179 | 180 | public void unsubscribeDataChanges(String path, IZkDataListener dataListener) { 181 | synchronized (_dataListener) { 182 | final Set listeners = _dataListener.get(path); 183 | if (listeners != null) { 184 | listeners.remove(dataListener); 185 | } 186 | if (listeners == null || listeners.isEmpty()) { 187 | _dataListener.remove(path); 188 | } 189 | } 190 | } 191 | 192 | public void subscribeStateChanges(final IZkStateListener listener) { 193 | synchronized (_stateListener) { 194 | _stateListener.add(listener); 195 | } 196 | } 197 | 198 | public void unsubscribeStateChanges(IZkStateListener stateListener) { 199 | synchronized (_stateListener) { 200 | _stateListener.remove(stateListener); 201 | } 202 | } 203 | 204 | public void unsubscribeAll() { 205 | synchronized (_childListener) { 206 | _childListener.clear(); 207 | } 208 | synchronized (_dataListener) { 209 | _dataListener.clear(); 210 | } 211 | synchronized (_stateListener) { 212 | _stateListener.clear(); 213 | } 214 | } 215 | 216 | 217 | public void createPersistent(String path) { 218 | createPersistent(path, false); 219 | } 220 | 221 | 222 | public void createPersistent(String path, boolean createParents) { 223 | try { 224 | create(path, null, CreateMode.PERSISTENT); 225 | } catch (ZkNodeExistsException e) { 226 | if (!createParents) { 227 | throw e; 228 | } 229 | } catch (ZkNoNodeException e) { 230 | if (!createParents) { 231 | throw e; 232 | } 233 | String parentDir = path.substring(0, path.lastIndexOf('/')); 234 | createPersistent(parentDir, createParents); 235 | createPersistent(path, createParents); 236 | } 237 | } 238 | 239 | 240 | public void createPersistent(String path, byte[] data) { 241 | create(path, data, CreateMode.PERSISTENT); 242 | } 243 | 244 | 245 | public String createPersistentSequential(String path, byte[] data) { 246 | return create(path, data, CreateMode.PERSISTENT_SEQUENTIAL); 247 | } 248 | 249 | 250 | public void createEphemeral(final String path) { 251 | create(path, null, CreateMode.EPHEMERAL); 252 | } 253 | 254 | public String create(final String path, byte[] data, final CreateMode mode) { 255 | if (path == null) { 256 | throw new NullPointerException("path must not be null."); 257 | } 258 | final byte[] bytes = data; 259 | 260 | return retryUntilConnected(new Callable() { 261 | 262 | @Override 263 | public String call() throws Exception { 264 | return _connection.create(path, bytes, mode); 265 | } 266 | }); 267 | } 268 | 269 | public void createEphemeral(final String path, final byte[] data) { 270 | create(path, data, CreateMode.EPHEMERAL); 271 | } 272 | 273 | public String createEphemeralSequential(final String path, final byte[] data) { 274 | return create(path, data, CreateMode.EPHEMERAL_SEQUENTIAL); 275 | } 276 | 277 | public void process(WatchedEvent event) { 278 | LOG.debug("Received event: " + event); 279 | _zookeeperEventThread = Thread.currentThread(); 280 | 281 | boolean stateChanged = event.getPath() == null; 282 | boolean znodeChanged = event.getPath() != null; 283 | boolean dataChanged = event.getType() == EventType.NodeDataChanged || // 284 | event.getType() == EventType.NodeDeleted || 285 | event.getType() == EventType.NodeCreated || // 286 | event.getType() == EventType.NodeChildrenChanged; 287 | 288 | getEventLock().lock(); 289 | try { 290 | 291 | // We might have to install child change event listener if a new node was created 292 | if (getShutdownTrigger()) { 293 | LOG.debug("ignoring event '{" + event.getType() + " | " + event.getPath() + "}' since shutdown triggered"); 294 | return; 295 | } 296 | if (stateChanged) { 297 | processStateChanged(event); 298 | } 299 | if (dataChanged) { 300 | processDataOrChildChange(event); 301 | } 302 | } finally { 303 | if (stateChanged) { 304 | getEventLock().getStateChangedCondition().signalAll(); 305 | 306 | // If the session expired we have to signal all conditions, because watches might have been removed and 307 | // there is no guarantee that those 308 | // conditions will be signaled at all after an Expired event 309 | if (event.getState() == KeeperState.Expired) { 310 | getEventLock().getZNodeEventCondition().signalAll(); 311 | getEventLock().getDataChangedCondition().signalAll(); 312 | // We also have to notify all listeners that something might have changed 313 | fireAllEvents(); 314 | } 315 | } 316 | if (znodeChanged) { 317 | getEventLock().getZNodeEventCondition().signalAll(); 318 | } 319 | if (dataChanged) { 320 | getEventLock().getDataChangedCondition().signalAll(); 321 | } 322 | getEventLock().unlock(); 323 | LOG.debug("Leaving process event"); 324 | } 325 | } 326 | 327 | private void fireAllEvents() { 328 | for (Entry> entry : _childListener.entrySet()) { 329 | fireChildChangedEvents(entry.getKey(), entry.getValue()); 330 | } 331 | for (Entry> entry : _dataListener.entrySet()) { 332 | fireDataChangedEvents(entry.getKey(), entry.getValue()); 333 | } 334 | } 335 | 336 | public List getChildren(String path) { 337 | return getChildren(path, hasListeners(path)); 338 | } 339 | 340 | protected List getChildren(final String path, final boolean watch) { 341 | try { 342 | return retryUntilConnected(new Callable>() { 343 | 344 | @Override 345 | public List call() throws Exception { 346 | return _connection.getChildren(path, watch); 347 | } 348 | }); 349 | } catch (ZkNoNodeException e) { 350 | return null; 351 | } 352 | } 353 | 354 | 355 | public int countChildren(String path) { 356 | try { 357 | Stat stat = new Stat(); 358 | this.readData(path, stat); 359 | return stat.getNumChildren(); 360 | //return getChildren(path).size(); 361 | } catch (ZkNoNodeException e) { 362 | return -1; 363 | } 364 | } 365 | 366 | protected boolean exists(final String path, final boolean watch) { 367 | return retryUntilConnected(new Callable() { 368 | 369 | @Override 370 | public Boolean call() throws Exception { 371 | return _connection.exists(path, watch); 372 | } 373 | }); 374 | } 375 | 376 | public boolean exists(final String path) { 377 | return exists(path, hasListeners(path)); 378 | } 379 | 380 | private void processStateChanged(WatchedEvent event) { 381 | LOG.info("zookeeper state changed (" + event.getState() + ")"); 382 | setCurrentState(event.getState()); 383 | if (getShutdownTrigger()) { 384 | return; 385 | } 386 | try { 387 | fireStateChangedEvent(event.getState()); 388 | 389 | if (event.getState() == KeeperState.Expired) { 390 | reconnect(); 391 | fireNewSessionEvents(); 392 | } 393 | } catch (final Exception e) { 394 | throw new RuntimeException("Exception while restarting zk client", e); 395 | } 396 | } 397 | 398 | private void fireNewSessionEvents() { 399 | for (final IZkStateListener stateListener : _stateListener) { 400 | _eventThread.send(new ZkEvent("New session event sent to " + stateListener) { 401 | 402 | @Override 403 | public void run() throws Exception { 404 | stateListener.handleNewSession(); 405 | } 406 | }); 407 | } 408 | } 409 | 410 | private void fireStateChangedEvent(final KeeperState state) { 411 | for (final IZkStateListener stateListener : _stateListener) { 412 | _eventThread.send(new ZkEvent("State changed to " + state + " sent to " + stateListener) { 413 | 414 | @Override 415 | public void run() throws Exception { 416 | stateListener.handleStateChanged(state); 417 | } 418 | }); 419 | } 420 | } 421 | 422 | private boolean hasListeners(String path) { 423 | Set dataListeners = _dataListener.get(path); 424 | if (dataListeners != null && dataListeners.size() > 0) { 425 | return true; 426 | } 427 | Set childListeners = _childListener.get(path); 428 | if (childListeners != null && childListeners.size() > 0) { 429 | return true; 430 | } 431 | return false; 432 | } 433 | 434 | public boolean deleteRecursive(String path) { 435 | List children; 436 | try { 437 | children = getChildren(path, false); 438 | } catch (ZkNoNodeException e) { 439 | return true; 440 | } 441 | if (children != null){ 442 | for (String subPath : children) { 443 | if (!deleteRecursive(path + "/" + subPath)) { 444 | return false; 445 | } 446 | } 447 | } 448 | 449 | return delete(path); 450 | } 451 | 452 | private void processDataOrChildChange(WatchedEvent event) { 453 | final String path = event.getPath(); 454 | 455 | if (event.getType() == EventType.NodeChildrenChanged || 456 | event.getType() == EventType.NodeCreated || 457 | event.getType() == EventType.NodeDeleted) { 458 | Set childListeners = _childListener.get(path); 459 | if (childListeners != null && !childListeners.isEmpty()) { 460 | fireChildChangedEvents(path, childListeners); 461 | } 462 | } 463 | 464 | if (event.getType() == EventType.NodeDataChanged || 465 | event.getType() == EventType.NodeDeleted || 466 | event.getType() == EventType.NodeCreated) { 467 | Set listeners = _dataListener.get(path); 468 | if (listeners != null && !listeners.isEmpty()) { 469 | fireDataChangedEvents(event.getPath(), listeners); 470 | } 471 | } 472 | } 473 | 474 | private void fireDataChangedEvents(final String path, Set listeners) { 475 | for (final IZkDataListener listener : listeners) { 476 | _eventThread.send(new ZkEvent("Data of " + path + " changed sent to " + listener) { 477 | 478 | @Override 479 | public void run() throws Exception { 480 | // reinstall watch 481 | exists(path, true); 482 | try { 483 | byte[] data = readData(path, null, true); 484 | listener.handleDataChange(path, data); 485 | } catch (ZkNoNodeException e) { 486 | listener.handleDataDeleted(path); 487 | } 488 | } 489 | }); 490 | } 491 | } 492 | 493 | private void fireChildChangedEvents(final String path, Set childListeners) { 494 | try { 495 | // reinstall the watch 496 | for (final IZkChildListener listener : childListeners) { 497 | _eventThread.send(new ZkEvent("Children of " + path + " changed sent to " + listener) { 498 | 499 | @Override 500 | public void run() throws Exception { 501 | try { 502 | // if the node doesn't exist we should listen for the root node to reappear 503 | exists(path); 504 | List children = getChildren(path); 505 | listener.handleChildChange(path, children); 506 | } catch (ZkNoNodeException e) { 507 | listener.handleChildChange(path, null); 508 | } 509 | } 510 | }); 511 | } 512 | } catch (Exception e) { 513 | LOG.error("Failed to fire child changed event. Unable to getChildren. ", e); 514 | } 515 | } 516 | 517 | public boolean waitUntilExists(String path, TimeUnit timeUnit, long time) throws ZkInterruptedException { 518 | Date timeout = new Date(System.currentTimeMillis() + timeUnit.toMillis(time)); 519 | LOG.debug("Waiting until znode '" + path + "' becomes available."); 520 | if (exists(path)) { 521 | return true; 522 | } 523 | acquireEventLock(); 524 | try { 525 | while (!exists(path, true)) { 526 | boolean gotSignal = getEventLock().getZNodeEventCondition().awaitUntil(timeout); 527 | if (!gotSignal) { 528 | return false; 529 | } 530 | } 531 | return true; 532 | } catch (InterruptedException e) { 533 | throw new ZkInterruptedException(e); 534 | } finally { 535 | getEventLock().unlock(); 536 | } 537 | } 538 | 539 | public boolean waitUntilConnected() throws ZkInterruptedException { 540 | return waitUntilConnected(Integer.MAX_VALUE, TimeUnit.MILLISECONDS); 541 | } 542 | 543 | public boolean waitUntilConnected(long time, TimeUnit timeUnit) throws ZkInterruptedException { 544 | return waitForKeeperState(KeeperState.SyncConnected, time, timeUnit); 545 | } 546 | 547 | public boolean waitForKeeperState(KeeperState keeperState, long time, TimeUnit timeUnit) 548 | throws ZkInterruptedException { 549 | if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) { 550 | throw new IllegalArgumentException("Must not be done in the zookeeper event thread."); 551 | } 552 | Date timeout = new Date(System.currentTimeMillis() + timeUnit.toMillis(time)); 553 | 554 | LOG.debug("Waiting for keeper state " + keeperState); 555 | acquireEventLock(); 556 | try { 557 | boolean stillWaiting = true; 558 | while (_currentState != keeperState) { 559 | if (!stillWaiting) { 560 | return false; 561 | } 562 | stillWaiting = getEventLock().getStateChangedCondition().awaitUntil(timeout); 563 | } 564 | LOG.debug("State is " + _currentState); 565 | return true; 566 | } catch (InterruptedException e) { 567 | throw new ZkInterruptedException(e); 568 | } finally { 569 | getEventLock().unlock(); 570 | } 571 | } 572 | 573 | private void acquireEventLock() { 574 | try { 575 | getEventLock().lockInterruptibly(); 576 | } catch (InterruptedException e) { 577 | throw new ZkInterruptedException(e); 578 | } 579 | } 580 | 581 | /** 582 | * @param callable the callable object 583 | * @param the runtime type of result 584 | * @return result of Callable 585 | * @throws ZkInterruptedException if operation was interrupted, or a required reconnection 586 | * got interrupted 587 | * @throws IllegalArgumentException if called from anything except the ZooKeeper event 588 | * thread 589 | * @throws ZkException if any ZooKeeper exception occurred 590 | * @throws RuntimeException if any other exception occurs from invoking the Callable 591 | */ 592 | public E retryUntilConnected(Callable callable) { 593 | if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) { 594 | throw new IllegalArgumentException("Must not be done in the zookeeper event thread."); 595 | } 596 | while (true) { 597 | try { 598 | return callable.call(); 599 | } catch (ConnectionLossException e) { 600 | // we give the event thread some time to update the status to 'Disconnected' 601 | Thread.yield(); 602 | waitUntilConnected(); 603 | } catch (SessionExpiredException e) { 604 | // we give the event thread some time to update the status to 'Expired' 605 | Thread.yield(); 606 | waitUntilConnected(); 607 | } catch (KeeperException e) { 608 | throw ZkException.create(e); 609 | } catch (InterruptedException e) { 610 | throw new ZkInterruptedException(e); 611 | } catch (Exception e) { 612 | throw ZkClientUtils.convertToRuntimeException(e); 613 | } 614 | } 615 | } 616 | 617 | public void setCurrentState(KeeperState currentState) { 618 | getEventLock().lock(); 619 | try { 620 | _currentState = currentState; 621 | } finally { 622 | getEventLock().unlock(); 623 | } 624 | } 625 | 626 | /** 627 | * Returns a mutex all zookeeper events are synchronized aginst. So in case you need to do 628 | * something without getting any zookeeper event interruption synchronize against this 629 | * mutex. Also all threads waiting on this mutex object will be notified on an event. 630 | * 631 | * @return the mutex. 632 | */ 633 | public ZkLock getEventLock() { 634 | return _zkEventLock; 635 | } 636 | 637 | public boolean delete(final String path) { 638 | try { 639 | retryUntilConnected(new Callable() { 640 | 641 | @Override 642 | public byte[] call() throws Exception { 643 | _connection.delete(path); 644 | return null; 645 | } 646 | }); 647 | 648 | return true; 649 | } catch (ZkNoNodeException e) { 650 | return false; 651 | } 652 | } 653 | 654 | public byte[] readData(String path) { 655 | return readData(path, false); 656 | } 657 | 658 | public byte[] readData(String path, boolean returnNullIfPathNotExists) { 659 | byte[] data = null; 660 | try { 661 | data = readData(path, null); 662 | } catch (ZkNoNodeException e) { 663 | if (!returnNullIfPathNotExists) { 664 | throw e; 665 | } 666 | } 667 | return data; 668 | } 669 | 670 | public byte[] readData(String path, Stat stat) { 671 | return readData(path, stat, hasListeners(path)); 672 | } 673 | 674 | protected byte[] readData(final String path, final Stat stat, final boolean watch) { 675 | byte[] data = retryUntilConnected(new Callable() { 676 | 677 | @Override 678 | public byte[] call() throws Exception { 679 | return _connection.readData(path, stat, watch); 680 | } 681 | }); 682 | return data; 683 | } 684 | 685 | public Stat writeData(String path, byte[] object) { 686 | return writeData(path, object, -1); 687 | } 688 | 689 | public void cas(String path, DataUpdater updater) { 690 | Stat stat = new Stat(); 691 | boolean retry; 692 | do { 693 | retry = false; 694 | try { 695 | byte[] oldData = readData(path, stat); 696 | byte[] newData = updater.update(oldData); 697 | writeData(path, newData, stat.getVersion()); 698 | } catch (ZkBadVersionException e) { 699 | retry = true; 700 | } 701 | } while (retry); 702 | } 703 | 704 | public Stat writeData(final String path, final byte[] data, final int expectedVersion) { 705 | return retryUntilConnected(new Callable() { 706 | 707 | @Override 708 | public Stat call() throws Exception { 709 | return _connection.writeData(path, data, expectedVersion); 710 | } 711 | }); 712 | } 713 | 714 | public void watchForData(final String path) { 715 | retryUntilConnected(new Callable() { 716 | 717 | @Override 718 | public Object call() throws Exception { 719 | _connection.exists(path, true); 720 | return null; 721 | } 722 | }); 723 | } 724 | 725 | public List watchForChilds(final String path) { 726 | if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) { 727 | throw new IllegalArgumentException("Must not be done in the zookeeper event thread."); 728 | } 729 | return retryUntilConnected(new Callable>() { 730 | 731 | @Override 732 | public List call() throws Exception { 733 | exists(path, true); 734 | try { 735 | return getChildren(path, true); 736 | } catch (ZkNoNodeException e) { 737 | // ignore, the "exists" watch will listen for the parent node to appear 738 | } 739 | return null; 740 | } 741 | }); 742 | } 743 | 744 | 745 | public synchronized void connect(final long maxMsToWaitUntilConnected, Watcher watcher) { 746 | if (_eventThread != null) { 747 | return; 748 | } 749 | boolean started = false; 750 | try { 751 | getEventLock().lockInterruptibly(); 752 | setShutdownTrigger(false); 753 | _eventThread = new ZkEventThread(_connection.getServers()); 754 | _eventThread.start(); 755 | _connection.connect(watcher); 756 | 757 | LOG.debug("Awaiting connection to Zookeeper server: " + maxMsToWaitUntilConnected); 758 | if (!waitUntilConnected(maxMsToWaitUntilConnected, TimeUnit.MILLISECONDS)) { 759 | throw new ZkTimeoutException(String.format( 760 | "Unable to connect to zookeeper server[%s] within timeout %dms", _connection.getServers(), maxMsToWaitUntilConnected)); 761 | } 762 | started = true; 763 | } catch (InterruptedException e) { 764 | States state = _connection.getZookeeperState(); 765 | throw new IllegalStateException("Not connected with zookeeper server yet. Current state is " + state); 766 | } finally { 767 | getEventLock().unlock(); 768 | 769 | // we should close the zookeeper instance, otherwise it would keep 770 | // on trying to connect 771 | if (!started) { 772 | close(); 773 | } 774 | } 775 | } 776 | 777 | public long getCreationTime(String path) { 778 | try { 779 | getEventLock().lockInterruptibly(); 780 | return _connection.getCreateTime(path); 781 | } catch (KeeperException e) { 782 | throw ZkException.create(e); 783 | } catch (InterruptedException e) { 784 | throw new ZkInterruptedException(e); 785 | } finally { 786 | getEventLock().unlock(); 787 | } 788 | } 789 | 790 | public synchronized void close() throws ZkInterruptedException { 791 | if (_eventThread == null) { 792 | return; 793 | } 794 | LOG.debug("Closing ZkClient..."); 795 | getEventLock().lock(); 796 | try { 797 | setShutdownTrigger(true); 798 | _currentState = null; 799 | _eventThread.interrupt(); 800 | _eventThread.join(2000); 801 | _connection.close(); 802 | _eventThread = null; 803 | } catch (InterruptedException e) { 804 | throw new ZkInterruptedException(e); 805 | } finally { 806 | getEventLock().unlock(); 807 | } 808 | LOG.debug("Closing ZkClient...done"); 809 | } 810 | 811 | private void reconnect() { 812 | getEventLock().lock(); 813 | try { 814 | _connection.close(); 815 | _connection.connect(this); 816 | } catch (InterruptedException e) { 817 | throw new ZkInterruptedException(e); 818 | } finally { 819 | getEventLock().unlock(); 820 | } 821 | } 822 | 823 | private void setShutdownTrigger(boolean triggerState) { 824 | _shutdownTriggered = triggerState; 825 | } 826 | 827 | private boolean getShutdownTrigger() { 828 | return _shutdownTriggered; 829 | } 830 | 831 | public int numberOfListeners() { 832 | int listeners = 0; 833 | for (Set childListeners : _childListener.values()) { 834 | listeners += childListeners.size(); 835 | } 836 | for (Set dataListeners : _dataListener.values()) { 837 | listeners += dataListeners.size(); 838 | } 839 | listeners += _stateListener.size(); 840 | 841 | return listeners; 842 | } 843 | 844 | @Override 845 | public List multi(final Iterable ops) { 846 | return retryUntilConnected(new Callable>() { 847 | @Override 848 | public List call() throws Exception { 849 | return _connection.multi(ops); 850 | } 851 | }); 852 | } 853 | 854 | @Override 855 | public ZooKeeper getZooKeeper() { 856 | return _connection != null ? _connection.getZooKeeper() : null; 857 | } 858 | 859 | @Override 860 | public boolean isConnected() { 861 | return _currentState == KeeperState.SyncConnected; 862 | } 863 | } 864 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/ZkClientUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * 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, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.github.zkclient; 17 | 18 | import java.io.IOException; 19 | import java.net.*; 20 | 21 | import com.github.zkclient.exception.ZkInterruptedException; 22 | 23 | public class ZkClientUtils { 24 | 25 | private ZkClientUtils() {} 26 | 27 | public static enum ZkVersion { 28 | V33, V34 29 | } 30 | 31 | public static final ZkVersion zkVersion; 32 | 33 | static { 34 | ZkVersion version = null; 35 | try { 36 | Class.forName("org.apache.zookeeper.OpResult"); 37 | version = ZkVersion.V34; 38 | } 39 | catch (ClassNotFoundException e) { 40 | version = ZkVersion.V33; 41 | } 42 | finally { 43 | zkVersion = version; 44 | } 45 | 46 | } 47 | 48 | public static RuntimeException convertToRuntimeException(Throwable e) { 49 | if (e instanceof RuntimeException) { 50 | return (RuntimeException) e; 51 | } 52 | retainInterruptFlag(e); 53 | return new RuntimeException(e); 54 | } 55 | 56 | /** 57 | * This sets the interrupt flag if the catched exception was an 58 | * {@link InterruptedException}. Catching such an exception always clears 59 | * the interrupt flag. 60 | * 61 | * @param catchedException 62 | * The catched exception. 63 | */ 64 | public static void retainInterruptFlag(Throwable catchedException) { 65 | if (catchedException instanceof InterruptedException) { 66 | Thread.currentThread().interrupt(); 67 | } 68 | } 69 | 70 | public static void rethrowInterruptedException(Throwable e) throws InterruptedException { 71 | if (e instanceof InterruptedException) { 72 | throw (InterruptedException) e; 73 | } 74 | if (e instanceof ZkInterruptedException) { 75 | throw (ZkInterruptedException) e; 76 | } 77 | } 78 | 79 | public static String leadingZeros(long number, int numberOfLeadingZeros) { 80 | return String.format("%0" + numberOfLeadingZeros + "d", number); 81 | } 82 | 83 | public final static String OVERWRITE_HOSTNAME_SYSTEM_PROPERTY = "zkclient.hostname.overwritten"; 84 | 85 | public static boolean isPortFree(int port) { 86 | try { 87 | Socket socket = new Socket(); 88 | socket.connect(new InetSocketAddress("localhost", port), 200); 89 | socket.close(); 90 | return false; 91 | } 92 | catch (SocketTimeoutException e) { 93 | return true; 94 | } 95 | catch (ConnectException e) { 96 | return true; 97 | } 98 | catch (SocketException e) { 99 | if (e.getMessage().equals("Connection reset by peer")) { 100 | return true; 101 | } 102 | throw new RuntimeException(e); 103 | } 104 | catch (UnknownHostException e) { 105 | throw new RuntimeException(e); 106 | } 107 | catch (IOException e) { 108 | throw new RuntimeException(e); 109 | } 110 | } 111 | 112 | public static String getLocalhostName() { 113 | String property = System.getProperty(OVERWRITE_HOSTNAME_SYSTEM_PROPERTY); 114 | if (property != null && property.trim().length() > 0) { 115 | return property; 116 | } 117 | try { 118 | return InetAddress.getLocalHost().getHostName(); 119 | } 120 | catch (final UnknownHostException e) { 121 | throw new RuntimeException("unable to retrieve localhost name"); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/ZkConnection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | import java.io.IOException; 19 | import java.lang.reflect.InvocationTargetException; 20 | import java.lang.reflect.Method; 21 | import java.util.List; 22 | import java.util.concurrent.locks.Lock; 23 | import java.util.concurrent.locks.ReentrantLock; 24 | 25 | import org.apache.zookeeper.CreateMode; 26 | import org.apache.zookeeper.KeeperException; 27 | import org.apache.zookeeper.Watcher; 28 | import org.apache.zookeeper.ZooKeeper; 29 | import org.apache.zookeeper.ZooDefs.Ids; 30 | import org.apache.zookeeper.ZooKeeper.States; 31 | import org.apache.zookeeper.data.Stat; 32 | 33 | import com.github.zkclient.exception.ZkException; 34 | import org.slf4j.Logger; 35 | import org.slf4j.LoggerFactory; 36 | 37 | public class ZkConnection { 38 | 39 | private static final Logger LOG = LoggerFactory.getLogger(ZkConnection.class); 40 | 41 | private ZooKeeper _zk = null; 42 | private final Lock _zookeeperLock = new ReentrantLock(); 43 | 44 | private final String _servers; 45 | private final int _sessionTimeOut; 46 | 47 | private static final Method method; 48 | 49 | static { 50 | Method[] methods = ZooKeeper.class.getDeclaredMethods(); 51 | Method m = null; 52 | for (Method method : methods) { 53 | if (method.getName().equals("multi")) { 54 | m = method; 55 | break; 56 | } 57 | } 58 | method = m; 59 | } 60 | 61 | /** 62 | * build a zookeeper connection 63 | * @param zkServers zookeeper connection string 64 | * @param sessionTimeOut session timeout in milliseconds 65 | */ 66 | public ZkConnection(String zkServers, int sessionTimeOut) { 67 | _servers = zkServers; 68 | _sessionTimeOut = sessionTimeOut; 69 | } 70 | 71 | public void connect(Watcher watcher) { 72 | _zookeeperLock.lock(); 73 | try { 74 | if (_zk != null) { 75 | throw new IllegalStateException("zk client has already been started"); 76 | } 77 | try { 78 | LOG.debug("Creating new ZookKeeper instance to connect to " + _servers + "."); 79 | _zk = new ZooKeeper(_servers, _sessionTimeOut, watcher); 80 | } catch (IOException e) { 81 | throw new ZkException("Unable to connect to " + _servers, e); 82 | } 83 | } finally { 84 | _zookeeperLock.unlock(); 85 | } 86 | } 87 | 88 | public void close() throws InterruptedException { 89 | _zookeeperLock.lock(); 90 | try { 91 | if (_zk != null) { 92 | LOG.debug("Closing ZooKeeper connected to " + _servers); 93 | _zk.close(); 94 | _zk = null; 95 | } 96 | } finally { 97 | _zookeeperLock.unlock(); 98 | } 99 | } 100 | 101 | public String create(String path, byte[] data, CreateMode mode) throws KeeperException, InterruptedException { 102 | return _zk.create(path, data, Ids.OPEN_ACL_UNSAFE, mode); 103 | } 104 | 105 | public void delete(String path) throws InterruptedException, KeeperException { 106 | _zk.delete(path, -1); 107 | } 108 | 109 | public boolean exists(String path, boolean watch) throws KeeperException, InterruptedException { 110 | return _zk.exists(path, watch) != null; 111 | } 112 | 113 | public List getChildren(final String path, final boolean watch) throws KeeperException, InterruptedException { 114 | return _zk.getChildren(path, watch); 115 | } 116 | 117 | public byte[] readData(String path, Stat stat, boolean watch) throws KeeperException, InterruptedException { 118 | return _zk.getData(path, watch, stat); 119 | } 120 | 121 | /** 122 | * wrapper for 3.3.x/3.4.x 123 | * 124 | * @param ops multi operations 125 | * @return OpResult list 126 | */ 127 | @SuppressWarnings("unchecked") 128 | public List multi(Iterable ops) { 129 | if (method == null) throw new UnsupportedOperationException("multi operation must use zookeeper 3.4+"); 130 | try { 131 | return (List) method.invoke(_zk, ops); 132 | } catch (IllegalArgumentException e) { 133 | throw new UnsupportedOperationException("ops must be 'org.apache.zookeeper.Op'"); 134 | } catch (IllegalAccessException e) { 135 | throw new UnsupportedOperationException(e); 136 | } catch (InvocationTargetException e) { 137 | throw new RuntimeException(e); 138 | } 139 | } 140 | 141 | public Stat writeData(String path, byte[] data, int version) throws KeeperException, InterruptedException { 142 | return _zk.setData(path, data, version); 143 | } 144 | 145 | public States getZookeeperState() { 146 | return _zk != null ? _zk.getState() : null; 147 | } 148 | 149 | public long getCreateTime(String path) throws KeeperException, InterruptedException { 150 | Stat stat = _zk.exists(path, false); 151 | if (stat != null) { 152 | return stat.getCtime(); 153 | } 154 | return -1; 155 | } 156 | 157 | public String getServers() { 158 | return _servers; 159 | } 160 | 161 | public ZooKeeper getZooKeeper() { 162 | return _zk; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/ZkEventThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | import java.util.concurrent.BlockingQueue; 19 | import java.util.concurrent.LinkedBlockingQueue; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | 22 | 23 | import com.github.zkclient.exception.ZkInterruptedException; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | /** 28 | * All listeners registered at the {@link ZkClient} will be notified from this event thread. 29 | * This is to prevent dead-lock situations. The {@link ZkClient} pulls some information out of 30 | * the {@link org.apache.zookeeper.ZooKeeper} events to signal {@link ZkLock} conditions. Re-using the 31 | * {@link org.apache.zookeeper.ZooKeeper} event thread to also notify {@link ZkClient} listeners, would stop the 32 | * ZkClient from receiving events from {@link org.apache.zookeeper.ZooKeeper} as soon as one of the listeners blocks 33 | * (because it is waiting for something). {@link ZkClient} would then for instance not be able 34 | * to maintain it's connection state anymore. 35 | */ 36 | class ZkEventThread extends Thread { 37 | 38 | private static final Logger LOG = LoggerFactory.getLogger(ZkEventThread.class); 39 | 40 | private final BlockingQueue _events = new LinkedBlockingQueue(); 41 | 42 | private static final AtomicInteger _eventId = new AtomicInteger(0); 43 | 44 | private volatile boolean shutdown = false; 45 | 46 | static abstract class ZkEvent { 47 | 48 | private final String _description; 49 | 50 | public ZkEvent(String description) { 51 | _description = description; 52 | } 53 | 54 | public abstract void run() throws Exception; 55 | 56 | @Override 57 | public String toString() { 58 | return "ZkEvent[" + _description + "]"; 59 | } 60 | } 61 | 62 | ZkEventThread(String name) { 63 | setDaemon(true); 64 | setName("ZkClient-EventThread-" + getId() + "-" + name); 65 | } 66 | 67 | @Override 68 | public void run() { 69 | LOG.info("Starting ZkClient event thread."); 70 | try { 71 | while (!isShutdown()) { 72 | ZkEvent zkEvent = _events.take(); 73 | int eventId = _eventId.incrementAndGet(); 74 | LOG.debug("Delivering event #" + eventId + " " + zkEvent); 75 | try { 76 | zkEvent.run(); 77 | } catch (InterruptedException e) { 78 | shutdown(); 79 | } catch (ZkInterruptedException e) { 80 | shutdown(); 81 | } catch (Throwable e) { 82 | LOG.error("Error handling event " + zkEvent, e); 83 | } 84 | LOG.debug("Delivering event #" + eventId + " done"); 85 | } 86 | } catch (InterruptedException e) { 87 | LOG.info("Terminate ZkClient event thread."); 88 | } 89 | } 90 | 91 | /** 92 | * @return the shutdown 93 | */ 94 | public boolean isShutdown() { 95 | return shutdown || isInterrupted(); 96 | } 97 | 98 | public void shutdown() { 99 | this.shutdown = true; 100 | this.interrupt(); 101 | } 102 | 103 | public void send(ZkEvent event) { 104 | if (!isShutdown()) { 105 | LOG.debug("New event: " + event); 106 | _events.add(event); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/ZkLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | import java.util.concurrent.locks.Condition; 19 | import java.util.concurrent.locks.ReentrantLock; 20 | 21 | public class ZkLock extends ReentrantLock { 22 | 23 | private static final long serialVersionUID = 1L; 24 | 25 | private final Condition _dataChangedCondition = newCondition(); 26 | private final Condition _stateChangedCondition = newCondition(); 27 | private final Condition _zNodeEventCondition = newCondition(); 28 | 29 | /** 30 | * This condition will be signaled if a zookeeper event was processed and the event contains a data/child change. 31 | * 32 | * @return the condition. 33 | */ 34 | public Condition getDataChangedCondition() { 35 | return _dataChangedCondition; 36 | } 37 | 38 | /** 39 | * This condition will be signaled if a zookeeper event was processed and the event contains a state change 40 | * (connected, disconnected, session expired, etc ...). 41 | * 42 | * @return the condition. 43 | */ 44 | public Condition getStateChangedCondition() { 45 | return _stateChangedCondition; 46 | } 47 | 48 | /** 49 | * This condition will be signaled if any znode related zookeeper event was received. 50 | * 51 | * @return the condition. 52 | */ 53 | public Condition getZNodeEventCondition() { 54 | return _zNodeEventCondition; 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/ZkServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | import com.github.zkclient.exception.ZkException; 19 | import org.apache.zookeeper.client.FourLetterWordMain; 20 | import org.apache.zookeeper.server.ServerConfig; 21 | import org.apache.zookeeper.server.ZooKeeperServerMain; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import javax.annotation.PostConstruct; 26 | import javax.annotation.PreDestroy; 27 | import java.io.File; 28 | import java.net.ConnectException; 29 | 30 | public class ZkServer extends ZooKeeperServerMain { 31 | 32 | private final static Logger LOG = LoggerFactory.getLogger(ZkServer.class); 33 | ; 34 | 35 | public static final int DEFAULT_PORT = 2181; 36 | 37 | public static final int DEFAULT_TICK_TIME = 5000; 38 | 39 | public static final int DEFAULT_MIN_SESSION_TIMEOUT = 2 * DEFAULT_TICK_TIME; 40 | 41 | private final String _dataDir; 42 | 43 | private final String _logDir; 44 | 45 | private ZkClient _zkClient; 46 | 47 | private final int _port; 48 | 49 | private final int _tickTime; 50 | 51 | private final int _minSessionTimeout; 52 | 53 | private volatile boolean shutdown = false; 54 | 55 | private boolean daemon = true; 56 | 57 | public ZkServer(String dataDir, String logDir) { 58 | this(dataDir, logDir, DEFAULT_PORT); 59 | } 60 | 61 | public ZkServer(String dataDir, String logDir, int port) { 62 | this(dataDir, logDir, port, DEFAULT_TICK_TIME); 63 | } 64 | 65 | public ZkServer(String dataDir, String logDir, int port, int tickTime) { 66 | this(dataDir, logDir, port, tickTime, DEFAULT_MIN_SESSION_TIMEOUT); 67 | } 68 | 69 | public ZkServer(String dataDir, String logDir, int port, int tickTime, int minSessionTimeout) { 70 | _dataDir = dataDir; 71 | _logDir = logDir; 72 | _port = port; 73 | _tickTime = tickTime; 74 | _minSessionTimeout = minSessionTimeout; 75 | } 76 | 77 | public int getPort() { 78 | return _port; 79 | } 80 | 81 | @PostConstruct 82 | public void start() { 83 | shutdown = false; 84 | startZkServer(); 85 | _zkClient = new ZkClient("localhost:" + _port, 10000); 86 | } 87 | 88 | private void startZkServer() { 89 | final int port = _port; 90 | if (ZkClientUtils.isPortFree(port)) { 91 | final File dataDir = new File(_dataDir); 92 | final File dataLogDir = new File(_logDir); 93 | dataDir.mkdirs(); 94 | 95 | // single zk server 96 | LOG.info("Start single zookeeper server, port={} data={} ", port, dataDir.getAbsolutePath()); 97 | // 98 | final ZooKeeperServerMain serverMain = this; 99 | final InnerServerConfig config = new InnerServerConfig(); 100 | config.parse(new String[]{"" + port, dataDir.getAbsolutePath(), "" + _tickTime, "60"}); 101 | config.setMinSessionTimeout(_minSessionTimeout); 102 | // 103 | final String threadName = "inner-zkserver-" + port; 104 | final Thread innerThread = new Thread(new Runnable() { 105 | @Override 106 | public void run() { 107 | try { 108 | serverMain.runFromConfig(config); 109 | } catch (Exception e) { 110 | throw new ZkException("Unable to start single ZooKeeper server.", e); 111 | } 112 | } 113 | }, threadName); 114 | innerThread.setDaemon(daemon); 115 | innerThread.start(); 116 | // 117 | waitForServerUp(port, 30000, false); 118 | 119 | } else { 120 | throw new IllegalStateException("Zookeeper port " + port + " was already in use. Running in single machine mode?"); 121 | } 122 | } 123 | 124 | @PreDestroy 125 | public void shutdown() { 126 | if (!shutdown) { 127 | shutdown = true; 128 | LOG.info("Shutting down ZkServer port={}...", _port); 129 | if (_zkClient != null) { 130 | try { 131 | _zkClient.close(); 132 | } catch (ZkException e) { 133 | LOG.warn("Error on closing zkclient: " + e.getClass().getName()); 134 | } 135 | _zkClient = null; 136 | } 137 | super.shutdown(); 138 | waitForServerDown(_port, 30000, false); 139 | LOG.info("Shutting down ZkServer port={}...done", _port); 140 | } 141 | } 142 | 143 | 144 | public ZkClient getZkClient() { 145 | return _zkClient; 146 | } 147 | 148 | class InnerServerConfig extends ServerConfig { 149 | public void setMinSessionTimeout(int minSessionTimeout) { 150 | this.minSessionTimeout = minSessionTimeout; 151 | } 152 | } 153 | 154 | public static boolean waitForServerUp(int port, long timeout, boolean secure) { 155 | long start = System.currentTimeMillis(); 156 | while (true) { 157 | try { 158 | // if there are multiple hostports, just take the first one 159 | String result = FourLetterWordMain.send4LetterWord("127.0.0.1", port, "stat"); 160 | if (result.startsWith("Zookeeper version:") && 161 | !result.contains("READ-ONLY")) { 162 | return true; 163 | } 164 | } catch (ConnectException e) { 165 | // ignore as this is expected, do not log stacktrace 166 | LOG.debug("server {} not up: {}", port, e.toString()); 167 | } catch (Exception e) { 168 | // ignore as this is expected 169 | LOG.info("server {} not up", port, e); 170 | } 171 | 172 | if (System.currentTimeMillis() > start + timeout) { 173 | break; 174 | } 175 | try { 176 | Thread.sleep(250); 177 | } catch (InterruptedException e) { 178 | // ignore 179 | } 180 | } 181 | return false; 182 | } 183 | 184 | public static boolean waitForServerDown(int port, long timeout, boolean secure) { 185 | long start = System.currentTimeMillis(); 186 | while (true) { 187 | try { 188 | FourLetterWordMain.send4LetterWord("127.0.0.1", port, "stat"); 189 | } catch (Exception e) { 190 | return true; 191 | } 192 | 193 | if (System.currentTimeMillis() > start + timeout) { 194 | break; 195 | } 196 | try { 197 | Thread.sleep(250); 198 | } catch (InterruptedException e) { 199 | // ignore 200 | } 201 | } 202 | return false; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/exception/ZkBadVersionException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient.exception; 17 | 18 | import org.apache.zookeeper.KeeperException; 19 | 20 | public class ZkBadVersionException extends ZkException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public ZkBadVersionException() { 25 | super(); 26 | } 27 | 28 | public ZkBadVersionException(KeeperException cause) { 29 | super(cause); 30 | } 31 | 32 | public ZkBadVersionException(String message, KeeperException cause) { 33 | super(message, cause); 34 | } 35 | 36 | public ZkBadVersionException(String message) { 37 | super(message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/exception/ZkException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient.exception; 17 | 18 | import org.apache.zookeeper.KeeperException; 19 | 20 | public class ZkException extends RuntimeException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public ZkException() { 25 | super(); 26 | } 27 | 28 | public ZkException(String message, Throwable cause) { 29 | super(message, cause); 30 | } 31 | 32 | public ZkException(String message) { 33 | super(message); 34 | } 35 | 36 | public ZkException(Throwable cause) { 37 | super(cause); 38 | } 39 | 40 | public static ZkException create(KeeperException e) { 41 | switch (e.code()) { 42 | // case DATAINCONSISTENCY: 43 | // return new DataInconsistencyException(); 44 | // case CONNECTIONLOSS: 45 | // return new ConnectionLossException(); 46 | case NONODE: 47 | return new ZkNoNodeException(e); 48 | // case NOAUTH: 49 | // return new ZkNoAuthException(); 50 | case BADVERSION: 51 | return new ZkBadVersionException(e); 52 | // case NOCHILDRENFOREPHEMERALS: 53 | // return new NoChildrenForEphemeralsException(); 54 | case NODEEXISTS: 55 | return new ZkNodeExistsException(e); 56 | // case INVALIDACL: 57 | // return new ZkInvalidACLException(); 58 | // case AUTHFAILED: 59 | // return new AuthFailedException(); 60 | // case NOTEMPTY: 61 | // return new NotEmptyException(); 62 | // case SESSIONEXPIRED: 63 | // return new SessionExpiredException(); 64 | // case INVALIDCALLBACK: 65 | // return new InvalidCallbackException(); 66 | 67 | default: 68 | return new ZkException(e); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/exception/ZkInterruptedException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient.exception; 17 | 18 | public class ZkInterruptedException extends ZkException { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | public ZkInterruptedException(InterruptedException e) { 23 | super(e); 24 | Thread.currentThread().interrupt(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/exception/ZkMarshallingError.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient.exception; 17 | 18 | public class ZkMarshallingError extends ZkException { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | public ZkMarshallingError() { 23 | super(); 24 | } 25 | 26 | public ZkMarshallingError(Throwable cause) { 27 | super(cause); 28 | } 29 | 30 | public ZkMarshallingError(String message, Throwable cause) { 31 | super(message, cause); 32 | } 33 | 34 | public ZkMarshallingError(String message) { 35 | super(message); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/exception/ZkNoNodeException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient.exception; 17 | 18 | import org.apache.zookeeper.KeeperException; 19 | 20 | public class ZkNoNodeException extends ZkException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public ZkNoNodeException() { 25 | super(); 26 | } 27 | 28 | public ZkNoNodeException(KeeperException cause) { 29 | super(cause); 30 | } 31 | 32 | public ZkNoNodeException(String message, KeeperException cause) { 33 | super(message, cause); 34 | } 35 | 36 | public ZkNoNodeException(String message) { 37 | super(message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/exception/ZkNodeExistsException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient.exception; 17 | 18 | import org.apache.zookeeper.KeeperException; 19 | 20 | public class ZkNodeExistsException extends ZkException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public ZkNodeExistsException() { 25 | super(); 26 | } 27 | 28 | public ZkNodeExistsException(KeeperException cause) { 29 | super(cause); 30 | } 31 | 32 | public ZkNodeExistsException(String message, KeeperException cause) { 33 | super(message, cause); 34 | } 35 | 36 | public ZkNodeExistsException(String message) { 37 | super(message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/zkclient/exception/ZkTimeoutException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient.exception; 17 | 18 | public class ZkTimeoutException extends ZkException { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | public ZkTimeoutException() { 23 | super(); 24 | } 25 | 26 | public ZkTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | public ZkTimeoutException(String message) { 31 | super(message); 32 | } 33 | 34 | public ZkTimeoutException(Throwable cause) { 35 | super(cause); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/github/zkclient/DeferredGatewayStarter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | import com.github.zkclient.Gateway; 19 | 20 | public class DeferredGatewayStarter extends Thread { 21 | 22 | private final Gateway _zkServer; 23 | private int _delay; 24 | 25 | public DeferredGatewayStarter(Gateway gateway, int delay) { 26 | _zkServer = gateway; 27 | _delay = delay; 28 | } 29 | 30 | @Override 31 | public void run() { 32 | try { 33 | Thread.sleep(_delay); 34 | _zkServer.start(); 35 | } catch (Exception e) { 36 | // ignore 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/test/java/com/github/zkclient/Gateway.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | public class Gateway { 19 | 20 | private GatewayThread _thread; 21 | private final int _port; 22 | private final int _destinationPort; 23 | 24 | public Gateway(int port, int destinationPort) { 25 | _port = port; 26 | _destinationPort = destinationPort; 27 | } 28 | 29 | public synchronized void start() { 30 | if (_thread != null) { 31 | throw new IllegalStateException("Gateway already running"); 32 | } 33 | _thread = new GatewayThread(_port, _destinationPort); 34 | _thread.start(); 35 | _thread.awaitUp(); 36 | } 37 | 38 | public synchronized void stop() { 39 | if (_thread != null) { 40 | try { 41 | _thread.interruptAndJoin(); 42 | } catch (InterruptedException e) { 43 | Thread.currentThread().interrupt(); 44 | } 45 | _thread = null; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/github/zkclient/GatewayThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | import java.io.Closeable; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.OutputStream; 22 | import java.net.ServerSocket; 23 | import java.net.Socket; 24 | import java.net.SocketException; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.Vector; 28 | import java.util.concurrent.locks.Condition; 29 | import java.util.concurrent.locks.Lock; 30 | import java.util.concurrent.locks.ReentrantLock; 31 | 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | 35 | public class GatewayThread extends Thread { 36 | 37 | protected final static Logger LOG = LoggerFactory.getLogger(GatewayThread.class); 38 | 39 | private final int _port; 40 | private final int _destinationPort; 41 | private ServerSocket _serverSocket; 42 | private Lock _lock = new ReentrantLock(); 43 | private Condition _runningCondition = _lock.newCondition(); 44 | private boolean _running = false; 45 | 46 | public GatewayThread(int port, int destinationPort) { 47 | _port = port; 48 | _destinationPort = destinationPort; 49 | setDaemon(true); 50 | } 51 | 52 | @Override 53 | public void run() { 54 | final List runningThreads = new Vector(); 55 | try { 56 | LOG.info("Starting gateway on port " + _port + " pointing to port " + _destinationPort); 57 | _serverSocket = new ServerSocket(_port); 58 | _lock.lock(); 59 | try { 60 | _running = true; 61 | _runningCondition.signalAll(); 62 | } finally { 63 | _lock.unlock(); 64 | } 65 | while (true) { 66 | final Socket socket = _serverSocket.accept(); 67 | LOG.info("new client is connected " + socket.getInetAddress().getHostAddress()); 68 | final InputStream incomingInputStream = socket.getInputStream(); 69 | final OutputStream incomingOutputStream = socket.getOutputStream(); 70 | 71 | final Socket outgoingSocket; 72 | try { 73 | outgoingSocket = new Socket("localhost", _destinationPort); 74 | } catch (Exception e) { 75 | LOG.warn("could not connect to " + _destinationPort); 76 | continue; 77 | } 78 | final InputStream outgoingInputStream = outgoingSocket.getInputStream(); 79 | final OutputStream outgoingOutputStream = outgoingSocket.getOutputStream(); 80 | 81 | Thread writeThread = new Thread() { 82 | @Override 83 | public void run() { 84 | runningThreads.add(this); 85 | try { 86 | int read = -1; 87 | while ((read = incomingInputStream.read()) != -1) { 88 | outgoingOutputStream.write(read); 89 | } 90 | } catch (IOException e) { 91 | // ignore 92 | } finally { 93 | closeQuietly(outgoingOutputStream); 94 | runningThreads.remove(this); 95 | } 96 | } 97 | 98 | @Override 99 | public void interrupt() { 100 | try { 101 | socket.close(); 102 | outgoingSocket.close(); 103 | } catch (IOException e) { 104 | LOG.error("error on stopping closing sockets", e); 105 | } 106 | 107 | super.interrupt(); 108 | } 109 | }; 110 | 111 | Thread readThread = new Thread() { 112 | @Override 113 | public void run() { 114 | runningThreads.add(this); 115 | try { 116 | int read = -1; 117 | while ((read = outgoingInputStream.read()) != -1) { 118 | incomingOutputStream.write(read); 119 | } 120 | } catch (IOException e) { 121 | // ignore 122 | } finally { 123 | closeQuietly(incomingOutputStream); 124 | runningThreads.remove(this); 125 | } 126 | } 127 | }; 128 | 129 | writeThread.setDaemon(true); 130 | readThread.setDaemon(true); 131 | 132 | writeThread.start(); 133 | readThread.start(); 134 | } 135 | } catch (SocketException e) { 136 | if (!_running) { 137 | throw new RuntimeException(e); 138 | } 139 | LOG.info("Stopping gateway"); 140 | } catch (Exception e) { 141 | LOG.error("error on gateway execution", e); 142 | } 143 | 144 | for (Thread thread : new ArrayList(runningThreads)) { 145 | thread.interrupt(); 146 | try { 147 | thread.join(); 148 | } catch (InterruptedException e) { 149 | // ignore 150 | } 151 | } 152 | } 153 | 154 | protected void closeQuietly(Closeable closable) { 155 | try { 156 | closable.close(); 157 | } catch (IOException e) { 158 | // ignore 159 | } 160 | } 161 | 162 | @Override 163 | public void interrupt() { 164 | try { 165 | _serverSocket.close(); 166 | } catch (Exception cE) { 167 | LOG.error("error on stopping gateway", cE); 168 | } 169 | super.interrupt(); 170 | } 171 | 172 | public void interruptAndJoin() throws InterruptedException { 173 | interrupt(); 174 | join(); 175 | } 176 | 177 | public void awaitUp() { 178 | _lock.lock(); 179 | try { 180 | while (!_running) { 181 | _runningCondition.await(); 182 | } 183 | } catch (InterruptedException e) { 184 | Thread.currentThread().interrupt(); 185 | } finally { 186 | _lock.unlock(); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/test/java/com/github/zkclient/PortUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.zkclient; 2 | 3 | 4 | import java.io.IOException; 5 | import java.net.ServerSocket; 6 | 7 | /** 8 | * copy from https://github.com/adyliu/jafka/blob/master/src/test/java/com/sohu/jafka/PortUtils.java 9 | * 10 | * @author adyliu (imxylz@gmail.com) 11 | * @since 2013-04-25 12 | */ 13 | public class PortUtils { 14 | 15 | public static int checkAvailablePort(int port) { 16 | while (port < 65500) { 17 | ServerSocket serverSocket = null; 18 | try { 19 | serverSocket = new ServerSocket(port); 20 | return port; 21 | } catch (IOException e) { 22 | //ignore error 23 | } finally { 24 | try { 25 | if (serverSocket != null) 26 | serverSocket.close(); 27 | } catch (IOException e) { 28 | //ignore 29 | } 30 | } 31 | port++; 32 | } 33 | throw new RuntimeException("no available port"); 34 | } 35 | 36 | public static void main(String[] args) { 37 | int port = checkAvailablePort(80); 38 | System.out.println("The available port is " + port); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/github/zkclient/TestUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 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.github.zkclient; 17 | 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.util.concurrent.Callable; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | import org.junit.Ignore; 25 | 26 | import com.github.zkclient.ZkServer; 27 | 28 | @Ignore 29 | public class TestUtil { 30 | 31 | static { 32 | System.setProperty("zookeeper.preAllocSize", "1024");//1M data log 33 | } 34 | /** 35 | * This waits until the provided {@link Callable} returns an object that is equals to the given expected value or 36 | * the timeout has been reached. In both cases this method will return the return value of the latest 37 | * {@link Callable} execution. 38 | * 39 | * @param expectedValue 40 | * The expected value of the callable. 41 | * @param callable 42 | * The callable. 43 | * @param 44 | * The return type of the callable. 45 | * @param timeUnit 46 | * The timeout timeunit. 47 | * @param timeout 48 | * The timeout. 49 | * @return the return value of the latest {@link Callable} execution. 50 | * @throws Exception 51 | * @throws InterruptedException 52 | */ 53 | public static T waitUntil(T expectedValue, Callable callable, TimeUnit timeUnit, long timeout) throws Exception { 54 | long startTime = System.currentTimeMillis(); 55 | do { 56 | T actual = callable.call(); 57 | if (expectedValue.equals(actual)) { 58 | return actual; 59 | } 60 | if (System.currentTimeMillis() > startTime + timeUnit.toMillis(timeout)) { 61 | return actual; 62 | } 63 | Thread.sleep(50); 64 | } while (true); 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /src/test/java/com/github/zkclient/ZkClientTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.github.zkclient; 5 | 6 | import com.github.zkclient.exception.ZkNoNodeException; 7 | import org.apache.zookeeper.CreateMode; 8 | import org.apache.zookeeper.Watcher.Event.KeeperState; 9 | import org.apache.zookeeper.data.Stat; 10 | import org.junit.After; 11 | import org.junit.AfterClass; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.io.UnsupportedEncodingException; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.concurrent.Callable; 23 | import java.util.concurrent.TimeUnit; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | import java.util.concurrent.locks.LockSupport; 26 | 27 | import static org.junit.Assert.assertArrayEquals; 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertFalse; 30 | import static org.junit.Assert.assertNull; 31 | import static org.junit.Assert.assertTrue; 32 | import static org.junit.Assert.fail; 33 | 34 | /** 35 | * @author adyliu (imxylz@gmail.com) 36 | * @since 2012-12-3 37 | */ 38 | public class ZkClientTest { 39 | static { 40 | System.setProperty("zookeeper.preAllocSize", "1024");// 1M data log 41 | } 42 | 43 | final Logger logger = LoggerFactory.getLogger(ZkClientTest.class); 44 | private static final AtomicInteger counter = new AtomicInteger(); 45 | // 46 | private ZkServer server; 47 | private ZkClient client; 48 | final int TIMEOUT = 30;//30 second for loop timeout 49 | 50 | // 51 | private static void deleteFile(File f) throws IOException { 52 | if (f.isFile()) { 53 | f.delete(); 54 | //System.out.println("[DELETE FILE] "+f.getPath()); 55 | } else if (f.isDirectory()) { 56 | File[] files = f.listFiles(); 57 | if (files != null) { 58 | for (File fs : files) { 59 | deleteFile(fs); 60 | } 61 | } 62 | f.delete(); 63 | //System.out.println("[DELETE DIRECTORY] "+f.getPath()); 64 | } 65 | } 66 | 67 | private static ZkServer startZkServer(String testName, int port) throws IOException { 68 | String dataPath = "build/test/" + testName + "/data"; 69 | String logPath = "build/test/" + testName + "/log"; 70 | File dataDir = new File(".", dataPath).getCanonicalFile(); 71 | File logDir = new File(".", logPath).getCanonicalFile(); 72 | deleteFile(dataDir); 73 | deleteFile(logDir); 74 | //start the server with 100ms session timeout 75 | ZkServer zkServer = new ZkServer(dataDir.getPath(), logDir.getPath(), port, ZkServer.DEFAULT_TICK_TIME, 100); 76 | zkServer.start(); 77 | return zkServer; 78 | } 79 | 80 | @AfterClass 81 | public static void cleanup() throws IOException { 82 | deleteFile(new File(".", "build/test").getCanonicalFile()); 83 | } 84 | 85 | @Before 86 | public void setUp() throws Exception { 87 | this.server = startZkServer("server_" + counter.incrementAndGet(), 4711); 88 | this.client = this.server.getZkClient(); 89 | assertTrue(this.client.isConnected()); 90 | } 91 | 92 | @After 93 | public void tearDown() throws Exception { 94 | if (this.server != null) { 95 | this.server.shutdown(); 96 | } 97 | } 98 | 99 | /** 100 | * Test method for 101 | * {@link com.github.zkclient.ZkClient#subscribeChildChanges(java.lang.String, com.github.zkclient.IZkChildListener)} 102 | * . 103 | */ 104 | @Test 105 | public void testSubscribeChildChanges() throws Exception { 106 | final String path = "/a"; 107 | final AtomicInteger count = new AtomicInteger(0); 108 | final ArrayList children = new ArrayList(); 109 | IZkChildListener listener = new IZkChildListener() { 110 | public void handleChildChange(String parentPath, List currentChildren) throws Exception { 111 | count.incrementAndGet(); 112 | children.clear(); 113 | if (currentChildren != null) 114 | children.addAll(currentChildren); 115 | logger.info("handle childchange " + parentPath + ", " + currentChildren); 116 | } 117 | }; 118 | // 119 | client.subscribeChildChanges(path, listener); 120 | // 121 | logger.info("create the watcher node " + path); 122 | client.createPersistent(path); 123 | //wait some time to make sure the event was triggered 124 | TestUtil.waitUntil(1, new Callable() { 125 | @Override 126 | public Integer call() throws Exception { 127 | return count.get(); 128 | } 129 | }, TimeUnit.SECONDS, TIMEOUT); 130 | // 131 | assertEquals(1, count.get()); 132 | assertEquals(0, children.size()); 133 | // 134 | //create a child node 135 | count.set(0); 136 | client.createPersistent(path + "/child1"); 137 | logger.info("create the first child node " + path + "/child1"); 138 | TestUtil.waitUntil(1, new Callable() { 139 | @Override 140 | public Integer call() throws Exception { 141 | return count.get(); 142 | } 143 | }, TimeUnit.SECONDS, TIMEOUT); 144 | // 145 | assertEquals(1, count.get()); 146 | assertEquals(1, children.size()); 147 | assertEquals("child1", children.get(0)); 148 | // 149 | // create another child node and delete the node 150 | count.set(0); 151 | logger.info("create the second child node " + path + "/child2"); 152 | client.createPersistent(path + "/child2"); 153 | // 154 | logger.info("delete the watcher node " + path); 155 | client.deleteRecursive(path); 156 | // 157 | Boolean eventReceived = TestUtil.waitUntil(true, new Callable() { 158 | @Override 159 | public Boolean call() throws Exception { 160 | return count.get() > 0 && children.size() == 0; 161 | } 162 | }, TimeUnit.SECONDS, TIMEOUT); 163 | assertTrue(eventReceived); 164 | assertEquals(0, children.size()); 165 | // =========================================== 166 | // do it again and check the listener validate 167 | // =========================================== 168 | count.set(0); 169 | // 170 | logger.info("create the watcher node again " + path); 171 | client.createPersistent(path); 172 | // 173 | eventReceived = TestUtil.waitUntil(true, new Callable() { 174 | @Override 175 | public Boolean call() throws Exception { 176 | return count.get() > 0; 177 | } 178 | }, TimeUnit.SECONDS, TIMEOUT); 179 | assertTrue(eventReceived); 180 | assertEquals(0, children.size()); 181 | // 182 | // now create the first node 183 | count.set(0); 184 | final String child3 = "/child3"; 185 | client.createPersistent(path + child3); 186 | logger.info("create the first child node again " + path + child3); 187 | // 188 | eventReceived = TestUtil.waitUntil(true, new Callable() { 189 | @Override 190 | public Boolean call() throws Exception { 191 | return count.get() > 0; 192 | } 193 | }, TimeUnit.SECONDS, 15); 194 | assertTrue(eventReceived); 195 | assertEquals(1, children.size()); 196 | assertEquals("child3", children.get(0)); 197 | // 198 | // delete root node 199 | count.set(0); 200 | logger.info("delete the watcher node again " + path); 201 | client.deleteRecursive(path); 202 | // This will receive two message: (1) child was deleted (2) parent was deleted 203 | // 204 | eventReceived = TestUtil.waitUntil(true, new Callable() { 205 | @Override 206 | public Boolean call() throws Exception { 207 | return children.isEmpty(); 208 | } 209 | }, TimeUnit.SECONDS, TIMEOUT); 210 | assertTrue(eventReceived); 211 | assertTrue(children.isEmpty()); 212 | } 213 | 214 | static class Holder { 215 | T t; 216 | 217 | public void set(T t) { 218 | this.t = t; 219 | } 220 | 221 | public T get() { 222 | return t; 223 | } 224 | } 225 | 226 | static byte[] toBytes(String s) { 227 | try { 228 | return s != null ? s.getBytes("UTF-8") : null; 229 | } catch (UnsupportedEncodingException e) { 230 | throw new RuntimeException(e); 231 | } 232 | } 233 | 234 | static String toString(byte[] b) { 235 | try { 236 | return b != null ? new String(b, "UTF-8") : null; 237 | } catch (UnsupportedEncodingException e) { 238 | throw new RuntimeException(e); 239 | } 240 | } 241 | 242 | /** 243 | * Test method for 244 | * {@link com.github.zkclient.ZkClient#subscribeDataChanges(java.lang.String, com.github.zkclient.IZkDataListener)} 245 | * . 246 | */ 247 | @Test 248 | public void testSubscribeDataChanges() throws Exception { 249 | String path = "/a"; 250 | final AtomicInteger countChanged = new AtomicInteger(0); 251 | final AtomicInteger countDeleted = new AtomicInteger(0); 252 | final Holder holder = new Holder(); 253 | IZkDataListener listener = new IZkDataListener() { 254 | public void handleDataDeleted(String dataPath) throws Exception { 255 | countDeleted.incrementAndGet(); 256 | holder.set(null); 257 | } 258 | 259 | public void handleDataChange(String dataPath, byte[] data) throws Exception { 260 | countChanged.incrementAndGet(); 261 | holder.set(ZkClientTest.toString(data)); 262 | } 263 | }; 264 | client.subscribeDataChanges(path, listener); 265 | // 266 | // create the node 267 | client.createPersistent(path, toBytes("aaa")); 268 | // 269 | //wait some time to make sure the event was triggered 270 | TestUtil.waitUntil(1, new Callable() { 271 | @Override 272 | public Integer call() throws Exception { 273 | return countChanged.get(); 274 | } 275 | }, TimeUnit.SECONDS, TIMEOUT); 276 | assertEquals(1, countChanged.get()); 277 | assertEquals(0, countDeleted.get()); 278 | assertEquals("aaa", holder.get()); 279 | // 280 | countChanged.set(0); 281 | countDeleted.set(0); 282 | // 283 | client.delete(path); 284 | TestUtil.waitUntil(1, new Callable() { 285 | @Override 286 | public Integer call() throws Exception { 287 | return countDeleted.get(); 288 | } 289 | }, TimeUnit.SECONDS, TIMEOUT); 290 | assertEquals(0, countChanged.get()); 291 | assertEquals(1, countDeleted.get()); 292 | assertNull(holder.get()); 293 | // =========================================== 294 | // do it again and check the listener validate 295 | // =========================================== 296 | countChanged.set(0); 297 | countDeleted.set(0); 298 | client.createPersistent(path, toBytes("bbb")); 299 | TestUtil.waitUntil(1, new Callable() { 300 | @Override 301 | public Integer call() throws Exception { 302 | return countChanged.get(); 303 | } 304 | }, TimeUnit.SECONDS, TIMEOUT); 305 | assertEquals(1, countChanged.get()); 306 | assertEquals("bbb", holder.get()); 307 | // 308 | countChanged.set(0); 309 | client.writeData(path, toBytes("ccc")); 310 | // 311 | TestUtil.waitUntil(1, new Callable() { 312 | @Override 313 | public Integer call() throws Exception { 314 | return countChanged.get(); 315 | } 316 | }, TimeUnit.SECONDS, TIMEOUT); 317 | assertEquals(1, countChanged.get()); 318 | assertEquals("ccc", holder.get()); 319 | } 320 | 321 | 322 | /** 323 | * Test method for 324 | * {@link com.github.zkclient.ZkClient#createPersistent(java.lang.String, boolean)} 325 | * . 326 | */ 327 | @Test 328 | public void testCreatePersistent() { 329 | final String path = "/a/b"; 330 | try { 331 | client.createPersistent(path, false); 332 | fail("should throw exception"); 333 | } catch (ZkNoNodeException e) { 334 | assertFalse(client.exists(path)); 335 | } 336 | client.createPersistent(path, true); 337 | assertTrue(client.exists(path)); 338 | } 339 | 340 | /** 341 | * Test method for 342 | * {@link com.github.zkclient.ZkClient#createPersistent(java.lang.String, byte[])} 343 | * . 344 | */ 345 | @Test 346 | public void testCreatePersistentStringByteArray() { 347 | String path = "/a"; 348 | client.createPersistent(path, toBytes("abc")); 349 | assertEquals("abc", toString(client.readData(path))); 350 | // 351 | } 352 | 353 | /** 354 | * Test method for 355 | * {@link com.github.zkclient.ZkClient#createPersistentSequential(java.lang.String, byte[])} 356 | * . 357 | */ 358 | @Test 359 | public void testCreatePersistentSequential() { 360 | String path = "/a"; 361 | String npath = client.createPersistentSequential(path, toBytes("abc")); 362 | assertTrue(npath != null && npath.length() > 0); 363 | npath = client.createPersistentSequential(path, toBytes("abc")); 364 | assertEquals("abc", toString(client.readData(npath))); 365 | } 366 | 367 | /** 368 | * Test method for 369 | * {@link com.github.zkclient.ZkClient#createEphemeral(java.lang.String)}. 370 | */ 371 | @Test 372 | public void testCreateEphemeralString() { 373 | String path = "/a"; 374 | client.createEphemeral(path); 375 | Stat stat = new Stat(); 376 | client.readData(path, stat); 377 | assertTrue(stat.getEphemeralOwner() > 0); 378 | } 379 | 380 | /** 381 | * Test method for 382 | * {@link com.github.zkclient.ZkClient#create(java.lang.String, byte[], org.apache.zookeeper.CreateMode)} 383 | * . 384 | */ 385 | @Test 386 | public void testCreate() { 387 | String path = "/a"; 388 | client.create(path, toBytes("abc"), CreateMode.PERSISTENT); 389 | assertEquals("abc", toString(client.readData(path))); 390 | } 391 | 392 | 393 | @Test 394 | public void testCreateEphemeralSequential() { 395 | String path = "/a"; 396 | String npath = client.createEphemeralSequential(path, toBytes("abc")); 397 | assertTrue(npath != null && npath.startsWith("/a")); 398 | Stat stat = new Stat(); 399 | assertArrayEquals(toBytes("abc"), client.readData(npath, stat)); 400 | assertTrue(stat.getEphemeralOwner() > 0); 401 | } 402 | 403 | 404 | /** 405 | * Test method for 406 | * {@link com.github.zkclient.ZkClient#getChildren(java.lang.String)}. 407 | */ 408 | @Test 409 | public void testGetChildrenString() { 410 | String path = "/a"; 411 | client.createPersistent(path + "/ch1", true); 412 | client.createPersistent(path + "/ch2"); 413 | client.createPersistent(path + "/ch3"); 414 | List children = client.getChildren(path); 415 | assertEquals(3, children.size()); 416 | assertEquals(3, client.countChildren(path)); 417 | assertNull(client.getChildren("/aaa")); 418 | } 419 | 420 | 421 | /** 422 | * Test method for 423 | * {@link com.github.zkclient.ZkClient#exists(java.lang.String)}. 424 | */ 425 | @Test 426 | public void testExistsString() { 427 | String path = "/a"; 428 | assertFalse(client.exists(path)); 429 | client.createPersistent(path); 430 | assertTrue(client.exists(path)); 431 | client.delete(path); 432 | assertFalse(client.exists(path)); 433 | } 434 | 435 | /** 436 | * Test method for 437 | * {@link com.github.zkclient.ZkClient#deleteRecursive(java.lang.String)}. 438 | */ 439 | @Test 440 | public void testDeleteRecursive() { 441 | String path = "/a/b/c"; 442 | client.createPersistent(path, true); 443 | assertTrue(client.exists(path)); 444 | assertTrue(client.deleteRecursive("/a")); 445 | assertFalse(client.exists(path)); 446 | assertFalse(client.exists("/a/b")); 447 | assertFalse(client.exists("/a")); 448 | } 449 | 450 | /** 451 | * Test method for 452 | * {@link com.github.zkclient.ZkClient#waitUntilExists(java.lang.String, java.util.concurrent.TimeUnit, long)} 453 | * . 454 | */ 455 | @Test 456 | public void testWaitUntilExists() { 457 | final String path = "/a"; 458 | new Thread() { 459 | public void run() { 460 | LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100)); 461 | client.createPersistent(path); 462 | } 463 | }.start(); 464 | assertTrue(client.waitUntilExists(path, TimeUnit.SECONDS, 10)); 465 | assertTrue(client.exists(path)); 466 | // 467 | assertFalse(client.waitUntilExists("/notexists", TimeUnit.SECONDS, 1)); 468 | } 469 | 470 | /** 471 | * Test method for {@link com.github.zkclient.ZkClient#waitUntilConnected()} 472 | * . 473 | */ 474 | @Test 475 | public void testWaitUntilConnected() { 476 | ZkClient client2 = new ZkClient("localhost:4711", 15000); 477 | assertTrue(client2.waitUntilConnected()); 478 | server.shutdown(); 479 | // 480 | assertTrue(client2.waitForKeeperState(KeeperState.Disconnected, 1, TimeUnit.SECONDS)); 481 | // 482 | assertFalse(client2.waitUntilConnected(1, TimeUnit.SECONDS)); 483 | client2.close(); 484 | } 485 | 486 | 487 | /** 488 | * Test method for 489 | * {@link com.github.zkclient.ZkClient#readData(java.lang.String, org.apache.zookeeper.data.Stat)} 490 | * . 491 | */ 492 | @Test 493 | public void testReadDataStringStat() { 494 | client.createPersistent("/a", "data".getBytes()); 495 | Stat stat = new Stat(); 496 | client.readData("/a", stat); 497 | assertEquals(0, stat.getVersion()); 498 | assertTrue(stat.getDataLength() > 0); 499 | } 500 | 501 | 502 | /** 503 | * Test method for {@link com.github.zkclient.ZkClient#numberOfListeners()}. 504 | */ 505 | @Test 506 | public void testNumberOfListeners() { 507 | IZkChildListener zkChildListener = new AbstractListener() { 508 | }; 509 | client.subscribeChildChanges("/", zkChildListener); 510 | assertEquals(1, client.numberOfListeners()); 511 | // 512 | IZkDataListener zkDataListener = new AbstractListener() { 513 | }; 514 | client.subscribeDataChanges("/a", zkDataListener); 515 | assertEquals(2, client.numberOfListeners()); 516 | // 517 | client.subscribeDataChanges("/b", zkDataListener); 518 | assertEquals(3, client.numberOfListeners()); 519 | // 520 | IZkStateListener zkStateListener = new AbstractListener() { 521 | }; 522 | client.subscribeStateChanges(zkStateListener); 523 | assertEquals(4, client.numberOfListeners()); 524 | // 525 | client.unsubscribeChildChanges("/", zkChildListener); 526 | assertEquals(3, client.numberOfListeners()); 527 | // 528 | client.unsubscribeAll(); 529 | assertEquals(0, client.numberOfListeners()); 530 | } 531 | 532 | /** 533 | * Test method for {@link com.github.zkclient.ZkClient#getZooKeeper()}. 534 | */ 535 | @Test 536 | public void testGetZooKeeper() { 537 | assertTrue(client.getZooKeeper() != null); 538 | } 539 | 540 | @Test 541 | public void testRetryUnitConnected_SessionExpiredException() { 542 | int sessionTimeout = 200; 543 | int port = PortUtils.checkAvailablePort(4712); 544 | int dport = this.server.getPort(); 545 | Gateway gateway = new Gateway(port, dport); 546 | gateway.start(); 547 | // 548 | final ZkClient client2 = new ZkClient("localhost:" + port, sessionTimeout, 15000); 549 | gateway.stop(); 550 | // 551 | //start the server after 600ms 552 | new DeferredGatewayStarter(gateway, sessionTimeout * 3).start(); 553 | // 554 | final Boolean connected = client2.retryUntilConnected(new Callable() { 555 | @Override 556 | public Boolean call() throws Exception { 557 | client2.createPersistent("/abc"); 558 | return Boolean.TRUE; 559 | } 560 | }); 561 | assertTrue(connected); 562 | assertTrue(client2.exists("/abc")); 563 | client2.close(); 564 | gateway.stop(); 565 | // 566 | } 567 | 568 | 569 | @Test 570 | public void testChildListenerAfterSessionExpiredException() throws Exception { 571 | final int sessionTimeout = 200; 572 | ZkClient connectedClient = server.getZkClient(); 573 | connectedClient.createPersistent("/root"); 574 | // 575 | int port = PortUtils.checkAvailablePort(4712); 576 | int dport = this.server.getPort(); 577 | Gateway gateway = new Gateway(port, dport); 578 | gateway.start(); 579 | // 580 | final ZkClient disconnectedClient = new ZkClient("localhost:" + port, sessionTimeout, 15000); 581 | final Holder> children = new Holder>(); 582 | disconnectedClient.subscribeChildChanges("/root", new IZkChildListener() { 583 | 584 | @Override 585 | public void handleChildChange(String parentPath, List currentChildren) throws Exception { 586 | children.set(currentChildren); 587 | } 588 | }); 589 | gateway.stop();// 590 | // 591 | // the connected client created a new child node 592 | connectedClient.createPersistent("/root/node1"); 593 | // 594 | // wait for 3x sessionTImeout, the session should have expired 595 | Thread.sleep(3 * sessionTimeout); 596 | // 597 | // now start the gateway 598 | gateway.start(); 599 | // 600 | Boolean hasOneChild = TestUtil.waitUntil(true, new Callable() { 601 | @Override 602 | public Boolean call() throws Exception { 603 | return children.get() != null && children.get().size() == 1; 604 | } 605 | }, TimeUnit.SECONDS, TIMEOUT); 606 | // 607 | assertTrue(hasOneChild); 608 | assertEquals("node1", children.get().get(0)); 609 | assertEquals("node1", disconnectedClient.getChildren("/root").get(0)); 610 | // 611 | disconnectedClient.close(); 612 | gateway.stop(); 613 | } 614 | 615 | } 616 | -------------------------------------------------------------------------------- /src/test/java/com/github/zkclient/ZkClientUtilsTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.github.zkclient; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | import org.junit.Test; 9 | 10 | /** 11 | * 12 | * @author adyliu(imxylz@gmail.com) 13 | * @since 2012-12-3 14 | */ 15 | public class ZkClientUtilsTest { 16 | 17 | /** 18 | * Test method for 19 | * {@link com.github.zkclient.ZkClientUtils#leadingZeros(long, int)}. 20 | */ 21 | @Test 22 | public void testLeadingZeros() { 23 | assertEquals("99", ZkClientUtils.leadingZeros(99, 1)); 24 | assertEquals("99", ZkClientUtils.leadingZeros(99, 2)); 25 | assertEquals("099", ZkClientUtils.leadingZeros(99, 3)); 26 | assertEquals("0099", ZkClientUtils.leadingZeros(99, 4)); 27 | assertEquals("00099", ZkClientUtils.leadingZeros(99, 5)); 28 | assertEquals("0100", ZkClientUtils.leadingZeros(100, 4)); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/github/zkclient/ZkServerDemo.java: -------------------------------------------------------------------------------- 1 | package com.github.zkclient; 2 | 3 | import java.io.File; 4 | import java.util.Arrays; 5 | 6 | public class ZkServerDemo { 7 | public static void main(String[] args) throws Exception{ 8 | File dataDir = new File("/tmp/zkdemo"); 9 | dataDir.mkdirs(); 10 | ZkServer server = new ZkServer(dataDir.getAbsolutePath(), dataDir.getAbsolutePath()); 11 | server.start(); 12 | 13 | ZkClient client = server.getZkClient(); 14 | client.createPersistent("/a", true); 15 | byte[] dat = client.readData("/a"); 16 | System.out.println(Arrays.toString(dat)); 17 | client.writeData("/a", "OK".getBytes()); 18 | System.out.println("agian="+Arrays.toString(dat)); 19 | //server.shutdown(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, console 2 | log4j.logger.org.apache.zookeeper=WARN 3 | 4 | log4j.appender.console=org.apache.log4j.ConsoleAppender 5 | log4j.appender.console.target=System.out 6 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %5p [%t] (%F:%L) - %m%n 8 | --------------------------------------------------------------------------------