├── .gitignore ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── RELEASE.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── org.linkedin.zookeeper-cli-impl ├── build.gradle └── src │ └── main │ └── groovy │ └── org │ └── linkedin │ └── zookeeper │ └── cli │ ├── ClientMain.groovy │ └── commands │ ├── AbstractCommand.groovy │ ├── CatCommand.groovy │ ├── Command.groovy │ ├── CommandException.groovy │ ├── DeleteCommand.groovy │ ├── DuCommand.groovy │ ├── GetCommand.groovy │ ├── LsCommand.groovy │ ├── MkDirCommand.groovy │ ├── PutCommand.groovy │ └── UploadCommand.groovy ├── org.linkedin.zookeeper-cli ├── build.gradle └── src │ └── cmdline │ └── resources │ ├── bin │ └── zk.sh │ └── conf │ ├── config.properties │ └── log4j.xml ├── org.linkedin.zookeeper-impl ├── build.gradle └── src │ ├── main │ └── java │ │ └── org │ │ └── linkedin │ │ └── zookeeper │ │ ├── client │ │ ├── AbstractZKClient.java │ │ ├── AbstractZooKeeper.java │ │ ├── ChrootedZKClient.java │ │ ├── IZKClient.java │ │ ├── IZooKeeper.java │ │ ├── IZooKeeperFactory.java │ │ ├── LifecycleListener.java │ │ ├── WatcherChain.java │ │ ├── ZKChildren.java │ │ ├── ZKClient.java │ │ ├── ZKData.java │ │ ├── ZooKeeperFactory.java │ │ ├── ZooKeeperImpl.java │ │ └── ZooKeeperURLHandler.java │ │ ├── server │ │ └── StandaloneZooKeeperServer.java │ │ └── tracker │ │ ├── ErrorListener.java │ │ ├── NodeEvent.java │ │ ├── NodeEventType.java │ │ ├── NodeEventsListener.java │ │ ├── TrackedNode.java │ │ ├── ZKByteArrayDataReader.java │ │ ├── ZKDataReader.java │ │ ├── ZKStringDataReader.java │ │ └── ZooKeeperTreeTracker.java │ └── test │ └── groovy │ └── test │ └── zookeeper │ └── client │ ├── TestZKClient.groovy │ └── TestZooKeeperTreeTracker.groovy ├── org.linkedin.zookeeper-server ├── build.gradle └── src │ └── cmdline │ └── resources │ ├── bin │ ├── zkCleanup.sh │ ├── zkCli.sh │ ├── zkEnv.sh │ └── zkServer.sh │ └── conf │ ├── log4j.properties │ └── zoo.cfg ├── org.linkedin.zookeeper.log4j-test-config ├── build.gradle └── src │ └── main │ └── resources │ └── log4j.xml ├── project-spec.groovy ├── repositories.gradle └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | *.iml 4 | *.ipr 5 | *.iws 6 | build 7 | out 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | ========================================================================= 2 | == NOTICE file corresponding to the section 4d of == 3 | == the Apache License, Version 2.0 == 4 | ========================================================================= 5 | 6 | This product uses the libraries 7 | commons-cli 8 | maven 9 | log4j 10 | zookeeper 11 | all of which are software developed by The Apache Software Foundation (http://www.apache.org/) 12 | 13 | ========================================================================= 14 | This product uses slf4j made by slf4j.org, with the following license (http://www.slf4j.org/license.html): 15 | Copyright (c) 2004-2008 QOS.ch 16 | All rights reserved. 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining 19 | a copy of this software and associated documentation files (the 20 | "Software"), to deal in the Software without restriction, including 21 | without limitation the rights to use, copy, modify, merge, publish, 22 | distribute, sublicense, and/or sell copies of the Software, and to 23 | permit persons to whom the Software is furnished to do so, subject to 24 | the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be 27 | included in all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 31 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 32 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 33 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 34 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 35 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | 37 | ========================================================================= 38 | This product uses other open source libraries from LinkedIn (util-core and util-groovy) with 39 | the following license: 40 | /* 41 | * Copyright 2010-2010 LinkedIn, Inc 42 | * 43 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 44 | * use this file except in compliance with the License. You may obtain a copy of 45 | * the License at 46 | * 47 | * http://www.apache.org/licenses/LICENSE-2.0 48 | * 49 | * Unless required by applicable law or agreed to in writing, software 50 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 51 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 52 | * License for the specific language governing permissions and limitations under 53 | * the License. 54 | */ 55 | 56 | ========================================================================= 57 | This product uses groovy (the language) with the following license: 58 | /* 59 | * Licensed under the Apache License, Version 2.0 (the "License"); 60 | * you may not use this file except in compliance with the License. 61 | * You may obtain a copy of the License at 62 | * 63 | * http://www.apache.org/licenses/LICENSE-2.0 64 | * 65 | * Unless required by applicable law or agreed to in writing, software 66 | * distributed under the License is distributed on an "AS IS" BASIS, 67 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 68 | * See the License for the specific language governing permissions and 69 | * limitations under the License. 70 | * 71 | */ 72 | 73 | ========================================================================= 74 | This product uses gradle (http://www.gradle.org/) for the build framework with the following 75 | license (http://www.gradle.org/license.html): 76 | Copyright 2007-2010 the original author or authors 77 | 78 | Licensed under the Apache License, Version 2.0 (the "License"); 79 | you may not use this file except in compliance with the License. 80 | You may obtain a copy of the License at 81 | 82 | http://www.apache.org/licenses/LICENSE-2.0 83 | 84 | Unless required by applicable law or agreed to in writing, software 85 | distributed under the License is distributed on an "AS IS" BASIS, 86 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 87 | See the License for the specific language governing permissions and 88 | limitations under the License. 89 | 90 | ========================================================================= 91 | This product uses junit (for testing) with the following license (https://github.com/KentBeck/junit/blob/master/LICENSE): 92 | JUnit 93 | 94 | Common Public License - v 1.0 95 | 96 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC 97 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 98 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 99 | 100 | 1. DEFINITIONS 101 | 102 | "Contribution" means: 103 | 104 | a) in the case of the initial Contributor, the initial code and 105 | documentation distributed under this Agreement, and 106 | b) in the case of each subsequent Contributor: 107 | 108 | i) changes to the Program, and 109 | 110 | ii) additions to the Program; 111 | 112 | where such changes and/or additions to the Program originate from and are 113 | distributed by that particular Contributor. A Contribution 'originates' from a 114 | Contributor if it was added to the Program by such Contributor itself or anyone 115 | acting on such Contributor's behalf. Contributions do not include additions to 116 | the Program which: (i) are separate modules of software distributed in 117 | conjunction with the Program under their own license agreement, and (ii) are 118 | not derivative works of the Program. 119 | 120 | "Contributor" means any person or entity that distributes the Program. 121 | 122 | "Licensed Patents " mean patent claims licensable by a Contributor which are 123 | necessarily infringed by the use or sale of its Contribution alone or when 124 | combined with the Program. 125 | 126 | "Program" means the Contributions distributed in accordance with this Agreement. 127 | 128 | "Recipient" means anyone who receives the Program under this Agreement, 129 | including all Contributors. 130 | 131 | 2. GRANT OF RIGHTS 132 | 133 | a) Subject to the terms of this Agreement, each Contributor hereby grants 134 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 135 | reproduce, prepare derivative works of, publicly display, publicly perform, 136 | distribute and sublicense the Contribution of such Contributor, if any, and 137 | such derivative works, in source code and object code form. 138 | 139 | b) Subject to the terms of this Agreement, each Contributor hereby grants 140 | Recipient a non-exclusive, worldwide, royalty-free patent license under 141 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 142 | transfer the Contribution of such Contributor, if any, in source code and 143 | object code form. This patent license shall apply to the combination of the 144 | Contribution and the Program if, at the time the Contribution is added by the 145 | Contributor, such addition of the Contribution causes such combination to be 146 | covered by the Licensed Patents. The patent license shall not apply to any 147 | other combinations which include the Contribution. No hardware per se is 148 | licensed hereunder. 149 | 150 | c) Recipient understands that although each Contributor grants the 151 | licenses to its Contributions set forth herein, no assurances are provided by 152 | any Contributor that the Program does not infringe the patent or other 153 | intellectual property rights of any other entity. Each Contributor disclaims 154 | any liability to Recipient for claims brought by any other entity based on 155 | infringement of intellectual property rights or otherwise. As a condition to 156 | exercising the rights and licenses granted hereunder, each Recipient hereby 157 | assumes sole responsibility to secure any other intellectual property rights 158 | needed, if any. For example, if a third party patent license is required to 159 | allow Recipient to distribute the Program, it is Recipient's responsibility to 160 | acquire that license before distributing the Program. 161 | 162 | d) Each Contributor represents that to its knowledge it has sufficient 163 | copyright rights in its Contribution, if any, to grant the copyright license 164 | set forth in this Agreement. 165 | 166 | 3. REQUIREMENTS 167 | 168 | A Contributor may choose to distribute the Program in object code form under 169 | its own license agreement, provided that: 170 | 171 | a) it complies with the terms and conditions of this Agreement; and 172 | 173 | b) its license agreement: 174 | 175 | i) effectively disclaims on behalf of all Contributors all warranties and 176 | conditions, express and implied, including warranties or conditions of title 177 | and non-infringement, and implied warranties or conditions of merchantability 178 | and fitness for a particular purpose; 179 | 180 | ii) effectively excludes on behalf of all Contributors all liability for 181 | damages, including direct, indirect, special, incidental and consequential 182 | damages, such as lost profits; 183 | 184 | iii) states that any provisions which differ from this Agreement are 185 | offered by that Contributor alone and not by any other party; and 186 | 187 | iv) states that source code for the Program is available from such 188 | Contributor, and informs licensees how to obtain it in a reasonable manner on 189 | or through a medium customarily used for software exchange. 190 | 191 | When the Program is made available in source code form: 192 | 193 | a) it must be made available under this Agreement; and 194 | 195 | b) a copy of this Agreement must be included with each copy of the 196 | Program. 197 | 198 | Contributors may not remove or alter any copyright notices contained within the 199 | Program. 200 | 201 | Each Contributor must identify itself as the originator of its Contribution, if 202 | any, in a manner that reasonably allows subsequent Recipients to identify the 203 | originator of the Contribution. 204 | 205 | 4. COMMERCIAL DISTRIBUTION 206 | 207 | Commercial distributors of software may accept certain responsibilities with 208 | respect to end users, business partners and the like. While this license is 209 | intended to facilitate the commercial use of the Program, the Contributor who 210 | includes the Program in a commercial product offering should do so in a manner 211 | which does not create potential liability for other Contributors. Therefore, if 212 | a Contributor includes the Program in a commercial product offering, such 213 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 214 | every other Contributor ("Indemnified Contributor") against any losses, damages 215 | and costs (collectively "Losses") arising from claims, lawsuits and other legal 216 | actions brought by a third party against the Indemnified Contributor to the 217 | extent caused by the acts or omissions of such Commercial Contributor in 218 | connection with its distribution of the Program in a commercial product 219 | offering. The obligations in this section do not apply to any claims or Losses 220 | relating to any actual or alleged intellectual property infringement. In order 221 | to qualify, an Indemnified Contributor must: a) promptly notify the Commercial 222 | Contributor in writing of such claim, and b) allow the Commercial Contributor 223 | to control, and cooperate with the Commercial Contributor in, the defense and 224 | any related settlement negotiations. The Indemnified Contributor may 225 | participate in any such claim at its own expense. 226 | 227 | For example, a Contributor might include the Program in a commercial product 228 | offering, Product X. That Contributor is then a Commercial Contributor. If that 229 | Commercial Contributor then makes performance claims, or offers warranties 230 | related to Product X, those performance claims and warranties are such 231 | Commercial Contributor's responsibility alone. Under this section, the 232 | Commercial Contributor would have to defend claims against the other 233 | Contributors related to those performance claims and warranties, and if a court 234 | requires any other Contributor to pay any damages as a result, the Commercial 235 | Contributor must pay those damages. 236 | 237 | 5. NO WARRANTY 238 | 239 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 240 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 241 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 242 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 243 | Recipient is solely responsible for determining the appropriateness of using 244 | and distributing the Program and assumes all risks associated with its exercise 245 | of rights under this Agreement, including but not limited to the risks and 246 | costs of program errors, compliance with applicable laws, damage to or loss of 247 | data, programs or equipment, and unavailability or interruption of operations. 248 | 249 | 6. DISCLAIMER OF LIABILITY 250 | 251 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 252 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 253 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 254 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 255 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 256 | WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS 257 | GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 258 | 259 | 7. GENERAL 260 | 261 | If any provision of this Agreement is invalid or unenforceable under applicable 262 | law, it shall not affect the validity or enforceability of the remainder of the 263 | terms of this Agreement, and without further action by the parties hereto, such 264 | provision shall be reformed to the minimum extent necessary to make such 265 | provision valid and enforceable. 266 | 267 | If Recipient institutes patent litigation against a Contributor with respect to 268 | a patent applicable to software (including a cross-claim or counterclaim in a 269 | lawsuit), then any patent licenses granted by that Contributor to such 270 | Recipient under this Agreement shall terminate as of the date such litigation 271 | is filed. In addition, if Recipient institutes patent litigation against any 272 | entity (including a cross-claim or counterclaim in a lawsuit) alleging that the 273 | Program itself (excluding combinations of the Program with other software or 274 | hardware) infringes such Recipient's patent(s), then such Recipient's rights 275 | granted under Section 2(b) shall terminate as of the date such litigation is 276 | filed. 277 | 278 | All Recipient's rights under this Agreement shall terminate if it fails to 279 | comply with any of the material terms or conditions of this Agreement and does 280 | not cure such failure in a reasonable period of time after becoming aware of 281 | such noncompliance. If all Recipient's rights under this Agreement terminate, 282 | Recipient agrees to cease use and distribution of the Program as soon as 283 | reasonably practicable. However, Recipient's obligations under this Agreement 284 | and any licenses granted by Recipient relating to the Program shall continue 285 | and survive. 286 | 287 | Everyone is permitted to copy and distribute copies of this Agreement, but in 288 | order to avoid inconsistency the Agreement is copyrighted and may only be 289 | modified in the following manner. The Agreement Steward reserves the right to 290 | publish new versions (including revisions) of this Agreement from time to time. 291 | No one other than the Agreement Steward has the right to modify this Agreement. 292 | IBM is the initial Agreement Steward. IBM may assign the responsibility to 293 | serve as the Agreement Steward to a suitable separate entity. Each new version 294 | of the Agreement will be given a distinguishing version number. The Program 295 | (including Contributions) may always be distributed subject to the version of 296 | the Agreement under which it was received. In addition, after a new version of 297 | the Agreement is published, Contributor may elect to distribute the Program 298 | (including its Contributions) under the new version. Except as expressly stated 299 | in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to 300 | the intellectual property of any Contributor under this Agreement, whether 301 | expressly, by implication, estoppel or otherwise. All rights in the Program not 302 | expressly granted under this Agreement are reserved. 303 | 304 | This Agreement is governed by the laws of the State of New York and the 305 | intellectual property laws of the United States of America. No party to this 306 | Agreement will bring a legal action under this Agreement more than one year 307 | after the cause of action arose. Each party waives its rights to a jury trial 308 | in any resulting litigation. 309 | 310 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WARNING WARNING WARNING WARNING 2 | =============================== 3 | 4 | This project is no longer maintained in this repository. To find the latest version of this project, check the [pongasoft/utils-zookeeper](https://github.com/pongasoft/utils-zookeeper) fork. 5 | 6 | WARNING WARNING WARNING WARNING 7 | =============================== 8 | 9 | Introduction 10 | ============ 11 | The project represents a set of utility classes and wrappers around [ZooKeeper](http://zookeeper.apache.org/). 12 | 13 | Download 14 | ======== 15 | You can download pre-built binaries directly from the [github downloads page](https://github.com/linkedin/linkedin-zookeeper/downloads) 16 | 17 | * `org.linkedin.zookeeper-server-.tgz`: 18 | A simple to use prepackaged zookeeper distribution: 19 | 20 | ./bin/zkServer.sh start 21 | 22 | * `org.linkedin.zookeeper-cli-.tgz`: 23 | A command line (similar to the one bundled with ZooKeeper) with the idea of having a syntax very close to a 'regular' shell: 24 | 25 | zk.sh ls /a/b/c 26 | zk.sh du /a/b/d 27 | zk.sh put localFile.txt /a/b/c 28 | zk.sh cat /a/b/c 29 | etc... 30 | 31 | Compilation 32 | =========== 33 | In order to compile the code you need 34 | 35 | * java 1.6 36 | 37 | At the top simply run 38 | 39 | ./gradlew test 40 | 41 | which should compile and run all the tests. 42 | 43 | IDE Support 44 | =========== 45 | You can issue the command (at the top) 46 | 47 | ./gradlew cleanIdea idea 48 | 49 | which will use the gradle IDEA plugin to create the right set of modules in order to open the 50 | project in IntelliJ IDEA. 51 | 52 | Directory structure 53 | =================== 54 | 55 | * `org.linkedin.zookeeper-impl`: 56 | Contains a set of utility classes and wrappers to make it easier to use ZooKeeper: 57 | 58 | * `IZooKeeper` is an interface/abstraction to ZooKeeper (which is (unfortunately) a class) 59 | * `IZKClient` (which extends from `IZooKeeper`) adds a host of convenient calls and lifecycle listeners 60 | * `ZooKeeperURLHandler` is a URL handler which knows how to handle `zookeeper:/a/b/c` type urls 61 | * `ZooKeeperTreeTracker` (the core of this project) essentially keeps an in memory replica of a portion 62 | of a tree or entire subtree with easy to use listeners (`NodeEventsListener` and `ErrorListener`): you get notified when nodes are added, updated or deleted (you never deal with ZooKeeper watchers, nor have to set them over and over!). You can see a good example of how to use this class in the glu project [org.linkedin.glu.agent.tracker.AgentsTrackerImpl](https://github.com/linkedin/glu/blob/master/agent/org.linkedin.glu.agent-tracker/src/main/groovy/org/linkedin/glu/agent/tracker/AgentsTrackerImpl.groovy) 63 | * `StandaloneZooKeeperServer`: a simple class to start a standalone ZooKeeper server (simple to use 64 | in testing) 65 | 66 | * `org.linkedin.zookeeper-cli-impl`: 67 | A command line (similar to the one bundled with ZooKeeper) with the idea of having a syntax very close to a 'regular' shell: 68 | 69 | zk.sh ls /a/b/c 70 | zk.sh du /a/b/d 71 | zk.sh put localFile.txt /a/b/c 72 | zk.sh cat /a/b/c 73 | etc... 74 | 75 | * `org.linkedin.zookeeper-cli`: 76 | Create the packaged version of the cli. 77 | 78 | * `org.linkedin.zookeeper-server`: 79 | Simply create a packaged server which is easy to install and start. Useful in dev. 80 | 81 | 5. Installing/Running locally 82 | ----------------------------- 83 | To install the zookeeper server: 84 | 85 | cd org.linkedin.zookeeper-server 86 | ../gradlew package-install 87 | 88 | then go to the install directory and run 89 | 90 | ./bin/zkServer start 91 | 92 | To install the zookeeper cli: 93 | 94 | cd org.linkedin.zookeeper-cli 95 | ../gradlew package-install 96 | 97 | then go to the install directory and run (to see help) 98 | 99 | ./bin/zk.sh -h 100 | 101 | and then try 102 | 103 | ./bin/zk.sh ls / 104 | 105 | which returns 106 | 107 | zookeeper 108 | 109 | Note: it should work on any Linux/Unix based system (developped/tested on Mac OS X) 110 | 111 | Build configuration 112 | =================== 113 | The project uses the [`org.linkedin.userConfig`](https://github.com/linkedin/gradle-plugins/blob/master/README.md) plugin and as such can be configured 114 | 115 | Example: 116 | ~/.userConfig.properties 117 | top.build.dir="/Volumes/Disk2/deployment/${userConfig.project.name}" 118 | top.install.dir="/export/content/${userConfig.project.name}" 119 | top.release.dir="/export/content/repositories/release" 120 | top.publish.dir="/export/content/repositories/publish" 121 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | 1.5.1 (2013/04/16) 2 | ------------------ 3 | * Implemented [#14](https://github.com/linkedin/linkedin-zookeeper/issues/14) _Race condition? glu-210_ 4 | 5 | 1.5.0 (2013/04/01) 6 | ------------------ 7 | * Fixed [#13](https://github.com/linkedin/linkedin-zookeeper/issues/13) _zk.sh get does not work_ 8 | * Implemented [#12](https://github.com/linkedin/linkedin-zookeeper/issues/12) _Upgrade to latest versions_ 9 | * Fixed [#11](https://github.com/linkedin/linkedin-zookeeper/issues/11) _set connect timeout to a more reasonable value than 100ms_ 10 | * Fixed [#10](https://github.com/linkedin/linkedin-zookeeper/issues/10) _Make zk.sh use $JAVA\_HOME_ 11 | 12 | This release uses the latest version of ZooKeeper (3.4.5). Thanks to Patrick Hunt for the pull request. The build now uses gradle 1.4.0 and can be invoked using the wrapper (``gradlew``) at the root. 13 | 14 | Note that there is an [issue with ZooKeeper](https://issues.apache.org/jira/browse/ZOOKEEPER-1661) and the workaround for it is to use ``127.0.0.1`` instead of ``localhost``. 15 | 16 | 1.4.1 (2012/03/31) 17 | ------------------ 18 | * use of [linkedin-utils 1.8.0](https://github.com/linkedin/linkedin-utils/tree/v1.8.0) 19 | 20 | 1.4.0 (2011/09/23) 21 | ------------------ 22 | * use of [linkedin-utils 1.7.1](https://github.com/linkedin/linkedin-utils/tree/v1.7.1) 23 | * Implemented [#1](https://github.com/linkedin/linkedin-zookeeper/issues/1) _Expose a getConnectString in the IZKClient interface and IZooKeeperFactory_ (thanks Hiram) 24 | 25 | 1.3.0 (2011/04/30) 26 | ------------------ 27 | * use of [linkedin-utils 1.4.0](https://github.com/linkedin/linkedin-utils/tree/v1.4.0) 28 | * use of latest version of ZooKeeper (3.3.3) 29 | 30 | 1.2.2 (2011/01/26) 31 | ------------------ 32 | * made constructor public 33 | * use of [linkedin-utils 1.3.0](https://github.com/linkedin/linkedin-utils/tree/v1.3.0) 34 | 35 | 1.2.1 (2010/12/20) 36 | ------------------ 37 | * use of `gradle-plugins 1.5.0` in order to support `gradle 0.9` (no version change as the code did not change) 38 | 39 | 1.2.1 (2010/12/07) 40 | ------------------ 41 | * use of [linkedin-utils 1.2.1](https://github.com/linkedin/linkedin-utils/tree/REL_1.2.1) 42 | 43 | 1.0.0 (2010/11/06) 44 | ------------------ 45 | * First release -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 3 | * Portions Copyright (c) 2013 Yan Pujante 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | * use this file except in compliance with the License. You may obtain a copy of 7 | * 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, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations under 15 | * the License. 16 | */ 17 | 18 | buildscript { 19 | repositories { 20 | if(project.hasProperty('maven.buildscript.repository')) 21 | { 22 | mavenRepo url: new File(project.properties["maven.buildscript.repository"]).toURI() 23 | } 24 | 25 | if(project.hasProperty('ivy.buildscript.repository')) 26 | { 27 | add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) { 28 | name = "ivy.buildscript.repository" 29 | addIvyPattern "${project.properties['ivy.buildscript.repository']}/[organisation]/[module]/[revision]/[module]-[revision].ivy" 30 | addArtifactPattern "${project.properties['ivy.buildscript.repository']}/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]" 31 | m2compatible = true 32 | checkmodified = true 33 | } 34 | } 35 | 36 | // if you want to disallow maven central 37 | if(project.properties['no.maven.central'] != 'true') 38 | { 39 | mavenCentral() 40 | } 41 | } 42 | 43 | dependencies { 44 | classpath 'org.linkedin:org.linkedin.gradle-plugins:1.6.0' 45 | } 46 | } 47 | 48 | apply plugin: 'org.linkedin.userConfig' 49 | apply plugin: 'org.linkedin.spec' 50 | 51 | ext { 52 | topBuildDir = userConfig.top.build.dir ?: "${rootDir}/out/build" 53 | } 54 | 55 | apply plugin: 'org.linkedin.repository' 56 | 57 | allprojects { 58 | apply plugin: 'idea' 59 | group = spec.group 60 | version = spec.version 61 | } 62 | 63 | // we use git 64 | idea.project { 65 | ipr.withXml { root -> 66 | root.asNode().component.find { it.@name == 'VcsDirectoryMappings' }.mapping.@vcs = 'Git' 67 | } 68 | } 69 | 70 | subprojects { p -> 71 | buildscript { 72 | allRepositories.buildscript.configure(repositories) 73 | } 74 | 75 | apply plugin: 'maven' 76 | 77 | allRepositories.build.configure(repositories) 78 | 79 | afterEvaluate { 80 | p.convention.plugins.java?.sourceCompatibility = spec.versions.jdk 81 | p.convention.plugins.java?.targetCompatibility = spec.versions.jdk 82 | } 83 | 84 | buildDir = "${topBuildDir}${project.path.replace(':','/')}".toString() 85 | } 86 | 87 | task wrapper(type: Wrapper) { 88 | gradleVersion = '1.4' 89 | } 90 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkedInAttic/linkedin-zookeeper/600b1d01318594ed425ede566bbbdc94b026a53e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Mar 06 11:00:04 HST 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.4-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 3 | * Portions Copyright (c) 2013 Yan Pujante 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | * use this file except in compliance with the License. You may obtain a copy of 7 | * 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, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations under 15 | * the License. 16 | */ 17 | 18 | apply plugin: 'groovy' 19 | apply plugin: 'org.linkedin.release' 20 | 21 | dependencies { 22 | compile project(':org.linkedin.zookeeper-impl') 23 | compile spec.external.linkedinUtilsCore 24 | compile spec.external.linkedinUtilsGroovy 25 | compile spec.external.slf4j 26 | compile spec.external.zookeeper 27 | compile spec.external.groovy 28 | compile spec.external.commonsCli 29 | 30 | runtime spec.external.slf4jLog4j 31 | runtime spec.external.log4j 32 | } 33 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/ClientMain.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli 18 | 19 | import org.slf4j.Logger 20 | import org.slf4j.LoggerFactory 21 | import org.linkedin.groovy.util.cli.CliUtils 22 | import org.linkedin.util.clock.Timespan 23 | import org.linkedin.zookeeper.cli.commands.CatCommand 24 | import org.linkedin.zookeeper.cli.commands.Command 25 | import org.linkedin.zookeeper.cli.commands.DeleteCommand 26 | import org.linkedin.zookeeper.cli.commands.DuCommand 27 | import org.linkedin.zookeeper.cli.commands.GetCommand 28 | import org.linkedin.zookeeper.cli.commands.LsCommand 29 | import org.linkedin.zookeeper.cli.commands.MkDirCommand 30 | import org.linkedin.zookeeper.cli.commands.PutCommand 31 | import org.linkedin.zookeeper.cli.commands.UploadCommand 32 | import org.linkedin.zookeeper.client.IZKClient 33 | import org.linkedin.zookeeper.client.ZKClient 34 | 35 | /** 36 | * Zookeeper client app 37 | */ 38 | public class ClientMain 39 | { 40 | public static final String MODULE = ClientMain.class.getName(); 41 | public static final Logger log = LoggerFactory.getLogger(MODULE); 42 | 43 | private def config 44 | private Command command 45 | private int returnCode = 0 46 | private def commandArgs = [] 47 | private IZKClient client 48 | 49 | private final static Map ACTIONS = 50 | [ 51 | 'get': new GetCommand(), 52 | 'cat': new CatCommand(), 53 | 'put': new PutCommand(), 54 | 'upload': new UploadCommand(), 55 | 'ls': new LsCommand(), 56 | 'rm': new DeleteCommand(), 57 | 'du': new DuCommand(), 58 | 'mkdir': new MkDirCommand() 59 | ] 60 | 61 | def start() 62 | { 63 | def connectString = config.zkConnectionString ?: "127.0.0.1:2181" 64 | def connectTimeout = config.zkConnectionTimeout ?: "100" 65 | 66 | client = new ZKClient(connectString, Timespan.parse(connectTimeout), null) 67 | 68 | client.start() 69 | log.debug "Talking to zookeeper on ${connectString}" 70 | client.waitForStart(Timespan.parse('10s')) 71 | 72 | returnCode = command.execute(client, commandArgs) 73 | } 74 | 75 | def destroy() 76 | { 77 | client?.destroy() 78 | } 79 | 80 | static int mainNoExit(Object args) 81 | { 82 | def cli = new CliBuilder(usage: './bin/zk.sh [-h] [-s ] [-t ] command') 83 | cli.h(longOpt: 'help', 'display help') 84 | cli.s(longOpt: 'zkConnectionString', 'the zookeeper connection string (host:port)', args: 1, required: false) 85 | cli.t(longOpt: 'zkConnectionTimeout', 'the zookeeper connection timeout (e.g. 10s)', args: 1, required: false) 86 | 87 | // this is splitting the main cmdline args from the command and its args 88 | boolean isMainArg = true 89 | def mainArgs = [] 90 | def cmdArgs = [] 91 | args.each { 92 | if (ACTIONS.keySet().contains(it)) 93 | { 94 | isMainArg = false 95 | } 96 | if (isMainArg) 97 | { 98 | mainArgs << it 99 | } 100 | else 101 | { 102 | cmdArgs << it 103 | } 104 | } 105 | 106 | log.debug "Main args are ${mainArgs}" 107 | log.debug "CMD args are ${cmdArgs}" 108 | 109 | def options = CliUtils.parseCliOptions(cli: cli, args: mainArgs) 110 | 111 | if(!options || options.options.h || cmdArgs.size() == 0) 112 | { 113 | println "Allowed actions are ${ACTIONS.keySet()}!" 114 | cli.usage() 115 | return 0 116 | } 117 | 118 | Command cmd = ACTIONS.get(cmdArgs[0]) 119 | 120 | cmdArgs = cmdArgs - cmdArgs[0] // remove the name of the command from the list of args 121 | def clientMain = new ClientMain(config: options.config, command: cmd, commandArgs: cmdArgs) 122 | try 123 | { 124 | clientMain.start() 125 | } 126 | catch(ScriptException e) 127 | { 128 | log.error e.scriptStackTrace 129 | throw e 130 | } 131 | finally 132 | { 133 | clientMain.destroy() 134 | } 135 | 136 | return clientMain.returnCode 137 | } 138 | 139 | static void main(String[] args) 140 | { 141 | System.exit(mainNoExit(args)) 142 | } 143 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/AbstractCommand.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | import org.linkedin.zookeeper.client.IZKClient 20 | 21 | /** 22 | * Encapsulate some boiler plate code for commands 23 | */ 24 | public abstract class AbstractCommand implements Command 25 | { 26 | public static final String MODULE = AbstractCommand.class.getName(); 27 | public static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MODULE); 28 | 29 | public int execute(IZKClient client, Object args) 30 | { 31 | CliBuilder cli = getCli() 32 | 33 | OptionAccessor options = cli.parse(args) 34 | 35 | if(!options) 36 | { 37 | cli.usage() 38 | return 39 | } 40 | 41 | try 42 | { 43 | doExecute(client, options) 44 | return 0 45 | } 46 | catch (Throwable th) 47 | { 48 | if(log.isDebugEnabled()) 49 | log.debug("command exception", th) 50 | System.err.println(th.message) 51 | return 1 52 | } 53 | } 54 | 55 | /** 56 | * Gets the cli for the command 57 | */ 58 | public abstract CliBuilder getCli() 59 | 60 | /** 61 | * Executes the actual command 62 | */ 63 | public abstract void doExecute(IZKClient client, OptionAccessor options) 64 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/CatCommand.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | import org.linkedin.zookeeper.client.IZKClient 20 | 21 | /** 22 | * Mimics the shell cat command 23 | */ 24 | public class CatCommand extends AbstractCommand 25 | { 26 | 27 | public CliBuilder getCli() 28 | { 29 | def cli = new CliBuilder(usage: 'cat ...') 30 | return cli 31 | } 32 | 33 | public void doExecute(IZKClient client, OptionAccessor options) 34 | { 35 | def paths = options.arguments() 36 | 37 | log.debug "CAT command" 38 | log.debug "paths=${paths}" 39 | 40 | // execute 41 | paths.each {path -> 42 | if (!client.exists(path)) 43 | { 44 | log.warn ("Couldn't find path ${path}") 45 | } 46 | else 47 | { 48 | def res = client.getZKByteData(path) 49 | byte[] data = res.data 50 | if (data?.size() > 0) 51 | { 52 | data.each {b -> 53 | print new String(b) 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/Command.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | import org.linkedin.zookeeper.client.IZKClient 20 | 21 | /** 22 | * A command for the zookeeper shell 23 | */ 24 | public interface Command 25 | { 26 | 27 | /** 28 | * Execute the command using the given client and the given parameters 29 | */ 30 | int execute(IZKClient client, def args) 31 | 32 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/CommandException.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | /** 20 | * @author ypujante@linkedin.com */ 21 | class CommandException extends Exception 22 | { 23 | private static final long serialVersionUID = 1L; 24 | 25 | 26 | CommandException() 27 | { 28 | } 29 | 30 | CommandException(String s) 31 | { 32 | super(s); 33 | } 34 | 35 | CommandException(String s, Throwable throwable) 36 | { 37 | super(s, throwable); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/DeleteCommand.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | import org.linkedin.zookeeper.client.IZKClient 20 | 21 | /** 22 | * Deletes a node (could be recursive) 23 | */ 24 | public class DeleteCommand extends AbstractCommand 25 | { 26 | 27 | public CliBuilder getCli() 28 | { 29 | def cli = new CliBuilder(usage: 'rm [-r] ...') 30 | cli.r(longOpt: 'recurse', 'recursively delete nodes') 31 | return cli 32 | 33 | } 34 | 35 | public void doExecute(IZKClient client, OptionAccessor options) 36 | { 37 | boolean recurse = options.r 38 | // valid paths start with a / 39 | def paths = options.arguments() 40 | 41 | log.debug "RM command" 42 | log.debug "recurse=${recurse}, paths=${paths}" 43 | 44 | // execute 45 | paths.each {path -> 46 | if (!client.exists(path)) 47 | { 48 | println "${path} - NOT FOUND" 49 | } 50 | else 51 | { 52 | def children = client.getChildren(path) 53 | log.debug "children: ${children}" 54 | if (recurse || !children) 55 | { 56 | delete(client, path) 57 | println "${path} - DELETED" 58 | } 59 | else 60 | { 61 | println "${path} - CANNOT DELETE (not empty)" 62 | } 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * delete a node and its children 69 | */ 70 | private void delete(IZKClient client, String path) 71 | { 72 | client.getChildren(path).each { delete(client, "${path}/${it}") } 73 | client.delete(path) 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/DuCommand.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | import org.linkedin.zookeeper.client.IZKClient 20 | import org.apache.zookeeper.KeeperException 21 | import org.apache.zookeeper.data.Stat 22 | import org.linkedin.util.io.PathUtils 23 | 24 | /** 25 | * Mimics the shell ls command 26 | */ 27 | public class DuCommand extends AbstractCommand 28 | { 29 | 30 | public CliBuilder getCli() 31 | { 32 | def cli = new CliBuilder(usage: 'du [-r] ...') 33 | cli.r(longOpt: 'recurse', 'recursively computes the du') 34 | return cli 35 | } 36 | 37 | public void doExecute(IZKClient client, OptionAccessor options) 38 | { 39 | boolean recurse = options.r 40 | def paths = options.arguments() 41 | 42 | if(log.isDebugEnabled()) 43 | { 44 | log.debug "du command" 45 | log.debug "recurse=${recurse}, paths=${paths}" 46 | } 47 | 48 | 49 | Stat stat = new Stat() 50 | 51 | paths.each { String path -> 52 | long du = 0 53 | try 54 | { 55 | client.getACL(path, stat) 56 | du += stat.dataLength 57 | 58 | if(recurse) 59 | { 60 | def children = client.getAllChildren(path) 61 | children.each { String child -> 62 | def fullpath = PathUtils.addPaths(path, child) 63 | try 64 | { 65 | client.getACL(fullpath, stat) 66 | du += stat.dataLength 67 | } 68 | catch(KeeperException.NoNodeException e) 69 | { 70 | if(log.isDebugEnabled()) 71 | log.debug("Node ${fullpath} has disapeared") 72 | } 73 | } 74 | } 75 | 76 | printf("%-7d ${path}\n", du) 77 | } 78 | catch(KeeperException.NoNodeException e) 79 | { 80 | if(log.isDebugEnabled()) 81 | log.debug("Node ${path} has disapeared") 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/GetCommand.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 3 | * Portions Copyright (c) 2013 Yan Pujante 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | * use this file except in compliance with the License. You may obtain a copy of 7 | * 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, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations under 15 | * the License. 16 | */ 17 | 18 | package org.linkedin.zookeeper.cli.commands 19 | 20 | import org.linkedin.zookeeper.client.IZKClient 21 | 22 | /** 23 | * Get data and stats for a command 24 | */ 25 | public class GetCommand extends AbstractCommand 26 | { 27 | 28 | public CliBuilder getCli() 29 | { 30 | def cli = new CliBuilder(usage: 'get ...') 31 | return cli 32 | } 33 | 34 | public void doExecute(IZKClient client, OptionAccessor options) 35 | { 36 | def paths = options.arguments() 37 | 38 | log.debug "GET command" 39 | log.debug "paths=${paths}" 40 | 41 | // execute 42 | paths.each { path -> 43 | if (!client.exists(path)) 44 | { 45 | println "${path} - NOT FOUND" 46 | } 47 | else 48 | { 49 | println "${path}:" 50 | def res = client.getZKByteData(path) 51 | byte[] data = res.data 52 | def stat = res.stat 53 | println "cZxid = " + stat.czxid 54 | println "ctime = " + new Date(stat.ctime).toString() 55 | println "mZxid = " + stat.mzxid 56 | println "mtime = " + new Date(stat.mtime).toString() 57 | println "pZxid = " + stat.pzxid 58 | println "cversion = " + stat.cversion 59 | println "dataVersion = " + stat.version 60 | println "aclVersion = " + stat.aversion 61 | println "ephemeralOwner = " + stat.ephemeralOwner 62 | println "dataLength = " + stat.dataLength 63 | println "numChildren = " + stat.numChildren 64 | if (stat.dataLength > 0) 65 | { 66 | println new String(data , "UTF-8") 67 | } 68 | else 69 | { 70 | println "no data" 71 | } 72 | } 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/LsCommand.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | import org.linkedin.zookeeper.client.IZKClient 20 | import org.apache.zookeeper.data.Stat 21 | import org.apache.zookeeper.KeeperException 22 | import org.linkedin.util.io.PathUtils 23 | 24 | /** 25 | * Mimics the shell ls command 26 | */ 27 | public class LsCommand extends AbstractCommand 28 | { 29 | 30 | public CliBuilder getCli() 31 | { 32 | def cli = new CliBuilder(usage: 'ls [-l] [-r] ...') 33 | cli.l(longOpt: 'list', 'display extra information for the elements') 34 | cli.r(longOpt: 'recurse', 'recursively display nodes') 35 | return cli 36 | } 37 | 38 | public void doExecute(IZKClient client, OptionAccessor options) 39 | { 40 | boolean list = options.l 41 | boolean recurse = options.r 42 | def paths = options.arguments() 43 | 44 | if(log.isDebugEnabled()) 45 | { 46 | log.debug "LS command" 47 | log.debug "list=${list}, recurse=${recurse}, paths=${paths}" 48 | } 49 | 50 | 51 | // execute 52 | boolean summary = false 53 | if (paths.size() > 1) 54 | { 55 | summary = true 56 | } 57 | paths.each {String path -> 58 | if (!client.exists(path)) 59 | { 60 | println "${path} - No such node" 61 | } 62 | else 63 | { 64 | if (summary) println "${path}:" 65 | def children = recurse ? client.getAllChildren(path) : client.getChildren(path) 66 | if(list) 67 | { 68 | Stat stat = new Stat() 69 | children.each { String child -> 70 | def fullpath = PathUtils.addPaths(path, child) 71 | try 72 | { 73 | def acl = client.getACL(fullpath, stat) 74 | printf("%-7d ${child}\n", stat.dataLength) 75 | } 76 | catch(KeeperException.NoNodeException e) 77 | { 78 | if(log.isDebugEnabled()) 79 | log.debug("Node ${fullpath} has disapeared") 80 | } 81 | } 82 | } 83 | else 84 | { 85 | children.each {print "${it} "} 86 | println "" 87 | } 88 | } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/MkDirCommand.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | import org.linkedin.zookeeper.client.IZKClient 20 | import org.apache.zookeeper.CreateMode 21 | import org.apache.zookeeper.ZooDefs.Ids 22 | import org.apache.zookeeper.KeeperException.NodeExistsException 23 | import org.apache.zookeeper.KeeperException.NoNodeException 24 | 25 | /** 26 | * creates a folder 27 | */ 28 | public class MkDirCommand extends AbstractCommand 29 | { 30 | 31 | public CliBuilder getCli() 32 | { 33 | def cli = new CliBuilder(usage: 'mkdir []*') 34 | cli.p(longOpt: 'parent', 'create parents if necessary') 35 | return cli 36 | } 37 | 38 | public void doExecute(IZKClient client, OptionAccessor options) 39 | { 40 | def paths = options.arguments() 41 | def createParents = options.p 42 | 43 | log.debug "mkdir paths=${paths}, createParents=${createParents}" 44 | 45 | def res = 0 46 | 47 | // execute 48 | paths.each { path -> 49 | if(createParents) 50 | { 51 | try 52 | { 53 | client.createWithParents(path, '', Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 54 | } 55 | catch (NodeExistsException e) 56 | { 57 | if(log.isDebugEnabled()) 58 | log.debug("ignoring exception with -p option...", e) 59 | } 60 | } 61 | else 62 | { 63 | try 64 | { 65 | client.create(path, '', Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 66 | } 67 | catch (NodeExistsException e) 68 | { 69 | throw new CommandException("mkdir: ${path}: Node exists", e) 70 | } 71 | catch (NoNodeException e) 72 | { 73 | throw new CommandException("mkdir: ${path}: No such node", e) 74 | } 75 | } 76 | } 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/PutCommand.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | import org.apache.zookeeper.ZooDefs.Ids 20 | import org.apache.zookeeper.CreateMode 21 | import org.linkedin.zookeeper.client.IZKClient 22 | 23 | /** 24 | * Put a string into a node 25 | */ 26 | public class PutCommand extends AbstractCommand 27 | { 28 | 29 | public CliBuilder getCli() 30 | { 31 | def cli = new CliBuilder(usage: 'put [-f] ...') 32 | cli.f(longOpt: 'force', 'force put') 33 | return cli 34 | } 35 | 36 | public void doExecute(IZKClient client, OptionAccessor options) 37 | { 38 | def data = options.arguments()[0] 39 | def paths = options.arguments()[1..-1] 40 | boolean force = options.f 41 | 42 | log.debug "PUT command" 43 | log.debug "force=${force}, data=${data}, paths=${paths}, options=${options.arguments()}" 44 | 45 | // execute 46 | paths.each { path -> 47 | if (!client.exists(path) || force) 48 | { 49 | if (client.exists(path)) 50 | { 51 | client.setByteData(path, data.bytes) 52 | println "${path} - OVERWRITING" 53 | } 54 | else 55 | { 56 | client.createBytesNodeWithParents(path, data.bytes, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 57 | } 58 | } 59 | else 60 | { 61 | println "${path} - SKIPPED (already exists)" 62 | } 63 | } 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli-impl/src/main/groovy/org/linkedin/zookeeper/cli/commands/UploadCommand.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.cli.commands 18 | 19 | import org.apache.zookeeper.ZooDefs.Ids 20 | import org.apache.zookeeper.CreateMode 21 | import org.linkedin.zookeeper.client.IZKClient 22 | 23 | /** 24 | * Upload a file in zookeeper 25 | */ 26 | public class UploadCommand extends AbstractCommand 27 | { 28 | 29 | public CliBuilder getCli() 30 | { 31 | def cli = new CliBuilder(usage: 'upload -f ...') 32 | cli.f(longOpt: 'force', 'force upload') 33 | return cli 34 | } 35 | 36 | public void doExecute(IZKClient client, OptionAccessor options) 37 | { 38 | boolean force = options.f 39 | def source = options.arguments()[0] 40 | def paths = options.arguments()[1..-1] 41 | 42 | log.debug "UPLOAD command" 43 | log.debug "force=${force}, source=${source}, paths=${paths}, options=${options.arguments()}" 44 | 45 | // execute 46 | paths.each { path -> 47 | if (!client.exists(path) || force) 48 | { 49 | def buffer = new File(source).readBytes() 50 | if (client.exists(path)) 51 | { 52 | client.setByteData(path, buffer) 53 | println "${path} - OVERWRITING" 54 | } 55 | else 56 | { 57 | client.createBytesNodeWithParents(path, buffer, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 58 | } 59 | } 60 | else 61 | { 62 | println "${path} - SKIPPED (already exists)" 63 | } 64 | } 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | apply plugin: 'org.linkedin.cmdline' 18 | apply plugin: 'org.linkedin.release' 19 | 20 | dependencies { 21 | lib project(':org.linkedin.zookeeper-cli-impl') 22 | } 23 | 24 | cmdline { 25 | resources << fileTree(dir: rootDir, includes: ['*.txt', '*.md']) 26 | } 27 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli/src/cmdline/resources/bin/zk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Copyright 2010-2010 LinkedIn, Inc 5 | # Portions Copyright (c) 2013 Yan Pujante 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | # use this file except in compliance with the License. You may obtain a copy of 9 | # the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | # License for the specific language governing permissions and limitations under 17 | # the License. 18 | # 19 | 20 | # locations 21 | CURRDIR=`pwd` 22 | BASEDIR=`cd $(dirname $0)/.. ; pwd` 23 | 24 | cd $BASEDIR 25 | 26 | LIB_DIR=lib 27 | 28 | for file in `ls -1 $LIB_DIR/*.jar `; do 29 | if [ -z "$JVM_CLASSPATH" ]; then 30 | JVM_CLASSPATH="-classpath $JAVA_HOME/lib/tools.jar:$file" 31 | else 32 | JVM_CLASSPATH=$JVM_CLASSPATH:$file 33 | fi 34 | done 35 | 36 | OPTIONS="" 37 | JVM_LOG4J=-Dlog4j.configuration=file:$BASEDIR/conf/log4j.xml 38 | JVM_DEBUG= 39 | #JVM_DEBUG="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005" 40 | 41 | # set JAVA_HOME (JDK6) & JAVA_CMD 42 | if [ -z "$JAVA_HOME" ]; then 43 | case $(uname -s) in 44 | SunOS ) 45 | JAVA_HOME=/export/apps/jdk/JDK-1_6_0_16/usr/java 46 | JAVA_CMD=$JAVA_HOME/bin/$(isainfo -n)/java 47 | ;; 48 | Darwin) 49 | JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home 50 | JAVA_CMD=$JAVA_HOME/bin/java 51 | ;; 52 | * ) 53 | if [ -z "$JAVA_HOME" ] 54 | then 55 | echo "Unknown platform (not SunOS or Darwin)!!! Please set JAVA_HOME manually." 56 | exit 1 57 | fi 58 | ;; 59 | esac 60 | fi 61 | 62 | if [ -z "$JAVA_CMD" ]; then 63 | JAVA_CMD=$JAVA_HOME/bin/java 64 | fi 65 | 66 | #echo $JAVA_CMD $JVM_LOG4J $JVM_DEBUG $JVM_CLASSPATH org.linkedin.zookeeper.cli.ClientMain $OPTIONS $@ 67 | $JAVA_CMD $JVM_LOG4J $JVM_DEBUG $JVM_CLASSPATH org.linkedin.zookeeper.cli.ClientMain $OPTIONS $@ 68 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli/src/cmdline/resources/conf/config.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010-2010 LinkedIn, Inc 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 | 17 | # empty -------------------------------------------------------------------------------- /org.linkedin.zookeeper-cli/src/cmdline/resources/conf/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 3 | * Portions Copyright (c) 2013 Yan Pujante 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | * use this file except in compliance with the License. You may obtain a copy of 7 | * 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, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations under 15 | * the License. 16 | */ 17 | 18 | apply plugin: 'groovy' 19 | apply plugin: 'org.linkedin.release' 20 | 21 | dependencies { 22 | compile spec.external.linkedinUtilsCore 23 | compile spec.external.slf4j 24 | compile spec.external.zookeeper 25 | 26 | testCompile spec.external.groovyTest 27 | testCompile spec.external.linkedinUtilsGroovy 28 | testCompile spec.external.junit 29 | testRuntime spec.external.slf4jLog4j 30 | testRuntime project(':org.linkedin.zookeeper.log4j-test-config') 31 | } 32 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/AbstractZKClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | import org.slf4j.Logger; 20 | import org.apache.zookeeper.CreateMode; 21 | import org.apache.zookeeper.KeeperException; 22 | import org.apache.zookeeper.Watcher; 23 | import org.apache.zookeeper.data.ACL; 24 | import org.apache.zookeeper.data.Stat; 25 | import org.linkedin.util.io.PathUtils; 26 | 27 | import java.io.UnsupportedEncodingException; 28 | import java.util.ArrayList; 29 | import java.util.Collections; 30 | import java.util.List; 31 | 32 | /** 33 | * @author ypujante@linkedin.com 34 | */ 35 | public abstract class AbstractZKClient extends AbstractZooKeeper implements IZKClient 36 | { 37 | public static final String MODULE = AbstractZKClient.class.getName(); 38 | public static final Logger log = org.slf4j.LoggerFactory.getLogger(MODULE); 39 | 40 | private final static String CHARSET = "UTF-8"; 41 | 42 | /** 43 | * Constructor 44 | */ 45 | public AbstractZKClient(String chroot) 46 | { 47 | super(chroot); 48 | } 49 | 50 | // ZooKeeper convenient calls 51 | @Override 52 | public Stat exists(String path) throws InterruptedException, KeeperException 53 | { 54 | return exists(path, false); 55 | } 56 | 57 | @Override 58 | public List getChildren(String path) throws InterruptedException, KeeperException 59 | { 60 | return getChildren(path, false); 61 | } 62 | 63 | /** 64 | * @return both children and stat in one object 65 | */ 66 | @Override 67 | public ZKChildren getZKChildren(String path, Watcher watcher) 68 | throws KeeperException, InterruptedException 69 | { 70 | Stat stat = new Stat(); 71 | 72 | List children = getChildren(path, watcher, stat); 73 | 74 | return new ZKChildren(children, stat); 75 | } 76 | 77 | /** 78 | * Returns all the children (recursively) of the provided path. Note that like {@link #getChildren(String)} 79 | * the result is relative to path. 80 | */ 81 | @Override 82 | public List getAllChildren(String path) throws InterruptedException, KeeperException 83 | { 84 | return findAllChildren(path); 85 | } 86 | 87 | @Override 88 | public void create(String path, String data, List acl, CreateMode createMode) 89 | throws InterruptedException, KeeperException 90 | 91 | { 92 | create(path, toByteData(data), acl, createMode); 93 | } 94 | 95 | @Override 96 | public void createBytesNode(String path, byte[] data, List acl, CreateMode createMode) 97 | throws InterruptedException, KeeperException 98 | 99 | { 100 | create(path, data, acl, createMode); 101 | } 102 | 103 | /** 104 | * Creates the parents if they don't exist 105 | */ 106 | @Override 107 | public void createWithParents(String path, String data, List acl, CreateMode createMode) 108 | throws InterruptedException, KeeperException 109 | 110 | { 111 | createParents(path, acl); 112 | create(path, data, acl, createMode); 113 | } 114 | 115 | /** 116 | * Creates the parents if they don't exist 117 | */ 118 | @Override 119 | public void createBytesNodeWithParents(String path, byte[] data, List acl, CreateMode createMode) 120 | throws InterruptedException, KeeperException 121 | 122 | { 123 | createParents(path, acl); 124 | createBytesNode(path, data, acl, createMode); 125 | } 126 | 127 | @Override 128 | public byte[] getData(String path) throws InterruptedException, KeeperException 129 | { 130 | return getData(path, false, null); 131 | } 132 | 133 | @Override 134 | public String getStringData(String path) throws InterruptedException, KeeperException 135 | { 136 | return toStringData(getData(path, false, null)); 137 | } 138 | 139 | private String toStringData(byte[] data) 140 | { 141 | if(data == null) 142 | return null; 143 | else 144 | { 145 | try 146 | { 147 | return new String(data, CHARSET); 148 | } 149 | catch(UnsupportedEncodingException e) 150 | { 151 | throw new RuntimeException(e); 152 | } 153 | } 154 | } 155 | 156 | private byte[] toByteData(String data) 157 | { 158 | if(data == null) 159 | return null; 160 | else 161 | { 162 | try 163 | { 164 | return data.getBytes(CHARSET); 165 | } 166 | catch(UnsupportedEncodingException e) 167 | { 168 | throw new RuntimeException(e); 169 | } 170 | } 171 | } 172 | 173 | /** 174 | * Returns both the data as a string as well as the stat 175 | */ 176 | @Override 177 | public ZKData getZKStringData(String path) throws InterruptedException, KeeperException 178 | { 179 | return getZKStringData(path, null); 180 | } 181 | 182 | /** 183 | * Returns both the data as a string as well as the stat (and sets a watcher if not null) 184 | */ 185 | @Override 186 | public ZKData getZKStringData(String path, Watcher watcher) 187 | throws InterruptedException, KeeperException 188 | 189 | { 190 | ZKData zkData = getZKByteData(path, watcher); 191 | 192 | return new ZKData(toStringData(zkData.getData()), zkData.getStat()); 193 | } 194 | 195 | /** 196 | * Returns both the data as a byte[] as well as the stat 197 | */ 198 | @Override 199 | public ZKData getZKByteData(String path) throws InterruptedException, KeeperException 200 | { 201 | return getZKByteData(path, null); 202 | } 203 | 204 | /** 205 | * Returns both the data as a byte[] as well as the stat (and sets a watcher if not null) 206 | */ 207 | @Override 208 | public ZKData getZKByteData(String path, Watcher watcher) 209 | throws InterruptedException, KeeperException 210 | 211 | { 212 | Stat stat = new Stat(); 213 | return new ZKData(getData(path, watcher, stat), stat); 214 | } 215 | 216 | @Override 217 | public Stat setData(String path, String data) throws InterruptedException, KeeperException 218 | { 219 | return setByteData(path, toByteData(data)); 220 | } 221 | 222 | @Override 223 | public Stat setByteData(String path, byte[] data) throws InterruptedException, KeeperException 224 | { 225 | return setData(path, data, -1); 226 | } 227 | 228 | /** 229 | * Tries to create first and if the node exists, then does a setData. 230 | * 231 | * @return null if create worked, otherwise the result of setData 232 | */ 233 | @Override 234 | public Stat createOrSetWithParents(String path, String data, List acl, CreateMode createMode) 235 | throws InterruptedException, KeeperException 236 | 237 | { 238 | if(exists(path) != null) 239 | return setData(path, data); 240 | 241 | try 242 | { 243 | createWithParents(path, data, acl, createMode); 244 | return null; 245 | } 246 | catch(KeeperException.NodeExistsException e) 247 | { 248 | // this should not happen very often (race condition) 249 | return setData(path, data); 250 | } 251 | } 252 | 253 | @Override 254 | public void delete(String path) throws InterruptedException, KeeperException 255 | { 256 | delete(path, -1); 257 | } 258 | 259 | /** 260 | * delete all the children if they exist 261 | */ 262 | @Override 263 | public void deleteWithChildren(String path) throws InterruptedException, KeeperException 264 | { 265 | List allChildren = findAllChildren(path); 266 | 267 | for(String child : allChildren) 268 | { 269 | delete(PathUtils.addPaths(path, child)); 270 | } 271 | 272 | delete(path); 273 | } 274 | 275 | /** 276 | * Implementation note: the method adjusts the path and use getZk() directly because in the 277 | * case where chroot is not null, the chroot path itself may not exist which is why we have to go 278 | * all the way to the root. 279 | */ 280 | private void createParents(String path, List acl) 281 | throws InterruptedException, KeeperException 282 | 283 | { 284 | path = PathUtils.getParentPath(adjustPath(path)); 285 | path = PathUtils.removeTrailingSlash(path); 286 | 287 | List paths = new ArrayList(); 288 | 289 | while(!path.equals("") && getZk().exists(path, false) == null) 290 | { 291 | paths.add(path); 292 | 293 | path = PathUtils.getParentPath(path); 294 | path = PathUtils.removeTrailingSlash(path); 295 | } 296 | 297 | Collections.reverse(paths); 298 | 299 | for(String p : paths) 300 | { 301 | try 302 | { 303 | getZk().create(p, 304 | null, 305 | acl, 306 | CreateMode.PERSISTENT); 307 | } 308 | catch(KeeperException.NodeExistsException e) 309 | { 310 | // ok we continue... 311 | if(log.isDebugEnabled()) 312 | log.debug("parent already exists " + p); 313 | } 314 | } 315 | } 316 | 317 | private List findAllChildren(String path) throws InterruptedException, KeeperException 318 | { 319 | List allChildren = new ArrayList(); 320 | 321 | findAllChildren(path, null, allChildren); 322 | 323 | return allChildren; 324 | } 325 | 326 | private void findAllChildren(String path, String parentPath, List allChildren) 327 | throws InterruptedException, KeeperException 328 | 329 | { 330 | List directChildren = getChildren(path); 331 | 332 | Collections.sort(directChildren); 333 | 334 | for(String child : directChildren) 335 | { 336 | String childPath = parentPath == null ? child : PathUtils.addPaths(parentPath, child); 337 | findAllChildren(PathUtils.addPaths(path, child), childPath, allChildren); 338 | allChildren.add(childPath); 339 | } 340 | } 341 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/AbstractZooKeeper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | import org.slf4j.Logger; 20 | import org.apache.zookeeper.AsyncCallback; 21 | import org.apache.zookeeper.CreateMode; 22 | import org.apache.zookeeper.KeeperException; 23 | import org.apache.zookeeper.Watcher; 24 | import org.apache.zookeeper.ZooKeeper; 25 | import org.apache.zookeeper.data.ACL; 26 | import org.apache.zookeeper.data.Stat; 27 | import org.linkedin.util.io.PathUtils; 28 | 29 | import java.util.List; 30 | 31 | 32 | /** 33 | * Base abstract class which delegate all calls to another {@link IZooKeeper} implementation 34 | * and ajusts the path variable. Handle chroot as well. 35 | * 36 | * @author ypujante@linkedin.com 37 | */ 38 | public abstract class AbstractZooKeeper implements IZooKeeper 39 | { 40 | public static final String MODULE = AbstractZooKeeper.class.getName(); 41 | public static final Logger log = org.slf4j.LoggerFactory.getLogger(MODULE); 42 | 43 | private final String _chroot; 44 | 45 | /** 46 | * Constructor 47 | */ 48 | public AbstractZooKeeper() 49 | { 50 | this(null); 51 | } 52 | 53 | /** 54 | * Constructor 55 | */ 56 | public AbstractZooKeeper(String chroot) 57 | { 58 | _chroot = chroot; 59 | } 60 | 61 | public static String adjustPath(String path, String chroot) 62 | { 63 | if(chroot != null) 64 | path = PathUtils.addPaths(chroot, path); 65 | path = PathUtils.removeTrailingSlash(path); 66 | path = PathUtils.addLeadingSlash(path); 67 | return path; 68 | } 69 | 70 | protected String adjustPath(String path) 71 | { 72 | return adjustPath(path, _chroot); 73 | } 74 | 75 | @Override 76 | public void addAuthInfo(String scheme, byte[] auth) 77 | { 78 | getZk().addAuthInfo(scheme, auth); 79 | } 80 | 81 | protected abstract IZooKeeper getZk(); 82 | 83 | @Override 84 | public void close() throws InterruptedException 85 | { 86 | getZk().close(); 87 | } 88 | 89 | @Override 90 | public String create(String path, byte[] data, List acl, CreateMode createMode) 91 | throws KeeperException, InterruptedException 92 | { 93 | return getZk().create(adjustPath(path), data, acl, createMode); 94 | } 95 | 96 | @Override 97 | public void create(String path, 98 | byte[] data, 99 | List acl, 100 | CreateMode createMode, 101 | AsyncCallback.StringCallback cb, Object ctx) 102 | { 103 | getZk().create(adjustPath(path), data, acl, createMode, cb, ctx); 104 | } 105 | 106 | @Override 107 | public void delete(String path, int version) 108 | throws KeeperException, InterruptedException 109 | { 110 | getZk().delete(adjustPath(path), version); 111 | } 112 | 113 | @Override 114 | public void delete(String path, int version, AsyncCallback.VoidCallback cb, Object ctx) 115 | { 116 | getZk().delete(adjustPath(path), version, cb, ctx); 117 | } 118 | 119 | @Override 120 | public Stat exists(String path, boolean watch) 121 | throws KeeperException, InterruptedException 122 | { 123 | return getZk().exists(adjustPath(path), watch); 124 | } 125 | 126 | @Override 127 | public void exists(String path, boolean watch, AsyncCallback.StatCallback cb, Object ctx) 128 | { 129 | getZk().exists(adjustPath(path), watch, cb, ctx); 130 | } 131 | 132 | @Override 133 | public Stat exists(String path, Watcher watcher) 134 | throws KeeperException, InterruptedException 135 | { 136 | return getZk().exists(adjustPath(path), watcher); 137 | } 138 | 139 | @Override 140 | public void exists(String path, Watcher watcher, AsyncCallback.StatCallback cb, Object ctx) 141 | { 142 | getZk().exists(adjustPath(path), watcher, cb, ctx); 143 | } 144 | 145 | @Override 146 | public List getACL(String path, Stat stat) 147 | throws KeeperException, InterruptedException 148 | { 149 | return getZk().getACL(adjustPath(path), stat); 150 | } 151 | 152 | @Override 153 | public void getACL(String path, Stat stat, AsyncCallback.ACLCallback cb, Object ctx) 154 | { 155 | getZk().getACL(adjustPath(path), stat, cb, ctx); 156 | } 157 | 158 | @Override 159 | public List getChildren(String path, boolean watch) 160 | throws KeeperException, InterruptedException 161 | { 162 | return getZk().getChildren(adjustPath(path), watch); 163 | } 164 | 165 | @Override 166 | public void getChildren(String path, 167 | boolean watch, 168 | AsyncCallback.ChildrenCallback cb, 169 | Object ctx) 170 | { 171 | getZk().getChildren(adjustPath(path), watch, cb, ctx); 172 | } 173 | 174 | @Override 175 | public List getChildren(String path, Watcher watcher) 176 | throws KeeperException, InterruptedException 177 | { 178 | return getZk().getChildren(adjustPath(path), watcher); 179 | } 180 | 181 | @Override 182 | public void getChildren(String path, 183 | Watcher watcher, 184 | AsyncCallback.ChildrenCallback cb, 185 | Object ctx) 186 | { 187 | getZk().getChildren(adjustPath(path), watcher, cb, ctx); 188 | } 189 | 190 | @Override 191 | public void getData(String path, boolean watch, AsyncCallback.DataCallback cb, Object ctx) 192 | { 193 | getZk().getData(adjustPath(path), watch, cb, ctx); 194 | } 195 | 196 | @Override 197 | public byte[] getData(String path, boolean watch, Stat stat) 198 | throws KeeperException, InterruptedException 199 | { 200 | return getZk().getData(adjustPath(path), watch, stat); 201 | } 202 | 203 | @Override 204 | public void getData(String path, Watcher watcher, AsyncCallback.DataCallback cb, Object ctx) 205 | { 206 | getZk().getData(adjustPath(path), watcher, cb, ctx); 207 | } 208 | 209 | @Override 210 | public byte[] getData(String path, Watcher watcher, Stat stat) 211 | throws KeeperException, InterruptedException 212 | { 213 | return getZk().getData(adjustPath(path), watcher, stat); 214 | } 215 | 216 | @Override 217 | public long getSessionId() 218 | { 219 | return getZk().getSessionId(); 220 | } 221 | 222 | @Override 223 | public byte[] getSessionPasswd() 224 | { 225 | return getZk().getSessionPasswd(); 226 | } 227 | 228 | @Override 229 | public ZooKeeper.States getState() 230 | { 231 | return getZk().getState(); 232 | } 233 | 234 | @Override 235 | public void register(Watcher watcher) 236 | { 237 | getZk().register(watcher); 238 | } 239 | 240 | @Override 241 | public Stat setACL(String path, List acl, int version) 242 | throws KeeperException, InterruptedException 243 | { 244 | return getZk().setACL(adjustPath(path), acl, version); 245 | } 246 | 247 | @Override 248 | public void setACL(String path, 249 | List acl, 250 | int version, 251 | AsyncCallback.StatCallback cb, 252 | Object ctx) 253 | { 254 | getZk().setACL(adjustPath(path), acl, version, cb, ctx); 255 | } 256 | 257 | @Override 258 | public Stat setData(String path, byte[] data, int version) 259 | throws KeeperException, InterruptedException 260 | { 261 | return getZk().setData(adjustPath(path), data, version); 262 | } 263 | 264 | @Override 265 | public void setData(String path, 266 | byte[] data, 267 | int version, 268 | AsyncCallback.StatCallback cb, 269 | Object ctx) 270 | { 271 | getZk().setData(adjustPath(path), data, version, cb, ctx); 272 | } 273 | 274 | @Override 275 | public void sync(String path, AsyncCallback.VoidCallback cb, Object ctx) 276 | { 277 | getZk().sync(adjustPath(path), cb, ctx); 278 | } 279 | 280 | @Override 281 | public void getChildren(String path, 282 | boolean watch, 283 | AsyncCallback.Children2Callback cb, 284 | Object ctx) 285 | { 286 | getZk().getChildren(adjustPath(path), watch, cb, ctx); 287 | } 288 | 289 | @Override 290 | public int getSessionTimeout() 291 | { 292 | return getZk().getSessionTimeout(); 293 | } 294 | 295 | @Override 296 | public List getChildren(String path, Watcher watcher, Stat stat) 297 | throws KeeperException, InterruptedException 298 | { 299 | return getZk().getChildren(adjustPath(path), watcher, stat); 300 | } 301 | 302 | @Override 303 | public List getChildren(String path, boolean watch, Stat stat) 304 | throws KeeperException, InterruptedException 305 | { 306 | return getZk().getChildren(adjustPath(path), watch, stat); 307 | } 308 | 309 | @Override 310 | public void getChildren(String path, 311 | Watcher watcher, 312 | AsyncCallback.Children2Callback cb, 313 | Object ctx) 314 | { 315 | getZk().getChildren(adjustPath(path), watcher, cb, ctx); 316 | } 317 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/ChrootedZKClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | /** 20 | * @author ypujante@linkedin.com 21 | */ 22 | public class ChrootedZKClient extends AbstractZKClient implements IZKClient 23 | { 24 | private final IZKClient _zkClient; 25 | 26 | /** 27 | * Constructor 28 | */ 29 | public ChrootedZKClient(IZKClient zkClient, String chroot) 30 | { 31 | super(chroot); 32 | _zkClient = zkClient; 33 | } 34 | 35 | @Override 36 | protected IZooKeeper getZk() 37 | { 38 | return _zkClient; 39 | } 40 | 41 | /** 42 | * @return a new client with the path that has been chrooted... meaning all paths will be relative 43 | * to the path provided. 44 | */ 45 | @Override 46 | public IZKClient chroot(String path) 47 | { 48 | return new ChrootedZKClient(_zkClient, adjustPath(path)); 49 | } 50 | 51 | /** 52 | * @return true if connected 53 | */ 54 | @Override 55 | public boolean isConnected() 56 | { 57 | return _zkClient.isConnected(); 58 | } 59 | 60 | /** 61 | * {@inheritDoc} 62 | */ 63 | @Override 64 | public void registerListener(LifecycleListener listener) 65 | { 66 | _zkClient.registerListener(listener); 67 | } 68 | 69 | /** 70 | * {@inheritDoc} 71 | */ 72 | @Override 73 | public void removeListener(LifecycleListener listener) 74 | { 75 | _zkClient.removeListener(listener); 76 | } 77 | 78 | /** 79 | * {@inheritDoc} 80 | */ 81 | @Override 82 | public String getConnectString() { 83 | return _zkClient.getConnectString(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/IZKClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | import org.apache.zookeeper.CreateMode; 20 | import org.apache.zookeeper.KeeperException; 21 | import org.apache.zookeeper.Watcher; 22 | import org.apache.zookeeper.data.ACL; 23 | import org.apache.zookeeper.data.Stat; 24 | 25 | import java.util.List; 26 | 27 | /** 28 | * @author ypujante@linkedin.com 29 | */ 30 | public interface IZKClient extends IZooKeeper 31 | { 32 | /** 33 | * Registers a listener for lifecycle management. Due to the nature of ZooKeeper, you should always 34 | * register a listener and act appropriately on the 2 events that you can receive. Note that it is 35 | * guaranteed that the listener will be called even if the connection with ZooKeeper has been 36 | * established a while ago... 37 | * 38 | * @param listener the listener 39 | */ 40 | void registerListener(LifecycleListener listener); 41 | 42 | /** 43 | * Removes a listener previously set with {@link #registerListener(LifecycleListener)} 44 | * @param listener 45 | */ 46 | void removeListener(LifecycleListener listener); 47 | 48 | /** 49 | * @return a new client with the path that has been chrooted... meaning all paths will be relative 50 | * to the path provided. 51 | */ 52 | IZKClient chroot(String path); 53 | 54 | /** 55 | * @return true if connected 56 | */ 57 | boolean isConnected(); 58 | 59 | // ZooKeeper convenient calls 60 | Stat exists(String path) throws InterruptedException, KeeperException; 61 | 62 | List getChildren(String path) throws InterruptedException, KeeperException; 63 | 64 | /** 65 | * @return both children and stat in one object 66 | */ 67 | ZKChildren getZKChildren(String path, Watcher watcher) 68 | throws KeeperException, InterruptedException; 69 | 70 | /** 71 | * Returns all the children (recursively) of the provided path. Note that like {@link #getChildren(String)} 72 | * the result is relative to path. 73 | */ 74 | List getAllChildren(String path) throws InterruptedException, KeeperException; 75 | 76 | void create(String path, String data, List acl, CreateMode createMode) 77 | throws InterruptedException, KeeperException 78 | ; 79 | 80 | void createBytesNode(String path, byte[] data, List acl, CreateMode createMode) 81 | throws InterruptedException, KeeperException 82 | ; 83 | 84 | /** 85 | * Creates the parents if they don't exist 86 | */ 87 | void createWithParents(String path, String data, List acl, CreateMode createMode) 88 | throws InterruptedException, KeeperException 89 | ; 90 | 91 | /** 92 | * Creates the parents if they don't exist 93 | */ 94 | void createBytesNodeWithParents(String path, byte[] data, List acl, CreateMode createMode) 95 | throws InterruptedException, KeeperException 96 | ; 97 | 98 | byte[] getData(String path) throws InterruptedException, KeeperException; 99 | 100 | String getStringData(String path) throws InterruptedException, KeeperException; 101 | 102 | /** 103 | * Returns both the data as a string as well as the stat 104 | */ 105 | ZKData getZKStringData(String path) throws InterruptedException, KeeperException; 106 | 107 | /** 108 | * Returns both the data as a string as well as the stat (and sets a watcher if not null) 109 | */ 110 | ZKData getZKStringData(String path, Watcher watcher) 111 | throws InterruptedException, KeeperException 112 | ; 113 | 114 | /** 115 | * Returns both the data as a byte[] as well as the stat 116 | */ 117 | ZKData getZKByteData(String path) throws InterruptedException, KeeperException; 118 | 119 | /** 120 | * Returns both the data as a byte[] as well as the stat (and sets a watcher if not null) 121 | */ 122 | ZKData getZKByteData(String path, Watcher watcher) 123 | throws InterruptedException, KeeperException 124 | ; 125 | 126 | Stat setData(String path, String data) throws InterruptedException, KeeperException; 127 | 128 | Stat setByteData(String path, byte[] data) throws InterruptedException, KeeperException; 129 | 130 | /** 131 | * Tries to create first and if the node exists, then does a setData. 132 | * 133 | * @return null if create worked, otherwise the result of setData 134 | */ 135 | Stat createOrSetWithParents(String path, String data, List acl, CreateMode createMode) 136 | throws InterruptedException, KeeperException 137 | ; 138 | 139 | void delete(String path) throws InterruptedException, KeeperException; 140 | 141 | /** 142 | * delete all the children if they exist 143 | */ 144 | void deleteWithChildren(String path) throws InterruptedException, KeeperException; 145 | 146 | /** 147 | * @return the connect string that this client used. 148 | */ 149 | String getConnectString(); 150 | 151 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/IZooKeeper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | import org.apache.zookeeper.AsyncCallback; 20 | import org.apache.zookeeper.CreateMode; 21 | import org.apache.zookeeper.KeeperException; 22 | import org.apache.zookeeper.Watcher; 23 | import org.apache.zookeeper.ZooKeeper; 24 | import org.apache.zookeeper.data.ACL; 25 | import org.apache.zookeeper.data.Stat; 26 | 27 | import java.util.List; 28 | 29 | /** 30 | * Provides an interface to ZooKeeper (which is a class!). PLease see ZooKeeper javadoc 31 | * for details 32 | * 33 | * @author ypujante@linkedin.com 34 | */ 35 | public interface IZooKeeper 36 | { 37 | long getSessionId(); 38 | 39 | byte[] getSessionPasswd(); 40 | 41 | int getSessionTimeout(); 42 | 43 | void addAuthInfo(String scheme, byte auth[]); 44 | 45 | void register(Watcher watcher); 46 | 47 | void close() throws InterruptedException; 48 | 49 | String create(String path, byte data[], List acl, 50 | CreateMode createMode) 51 | throws KeeperException, InterruptedException; 52 | 53 | void create(String path, byte data[], List acl, 54 | CreateMode createMode, AsyncCallback.StringCallback cb, Object ctx); 55 | 56 | void delete(String path, int version) 57 | throws InterruptedException, KeeperException; 58 | 59 | void delete(String path, int version, AsyncCallback.VoidCallback cb, 60 | Object ctx); 61 | 62 | Stat exists(String path, Watcher watcher) 63 | throws KeeperException, InterruptedException; 64 | 65 | Stat exists(String path, boolean watch) throws KeeperException, 66 | InterruptedException; 67 | 68 | void exists(String path, Watcher watcher, 69 | AsyncCallback.StatCallback cb, Object ctx); 70 | 71 | void exists(String path, boolean watch, AsyncCallback.StatCallback cb, Object ctx); 72 | 73 | byte[] getData(String path, Watcher watcher, Stat stat) 74 | throws KeeperException, InterruptedException; 75 | 76 | byte[] getData(String path, boolean watch, Stat stat) 77 | throws KeeperException, InterruptedException; 78 | 79 | void getData(String path, Watcher watcher, 80 | AsyncCallback.DataCallback cb, Object ctx); 81 | 82 | void getData(String path, boolean watch, AsyncCallback.DataCallback cb, Object ctx); 83 | 84 | Stat setData(String path, byte data[], int version) 85 | throws KeeperException, InterruptedException; 86 | 87 | void setData(String path, byte data[], int version, 88 | AsyncCallback.StatCallback cb, Object ctx); 89 | 90 | List getACL(String path, Stat stat) 91 | throws KeeperException, InterruptedException; 92 | 93 | void getACL(String path, Stat stat, AsyncCallback.ACLCallback cb, 94 | Object ctx); 95 | 96 | Stat setACL(String path, List acl, int version) 97 | throws KeeperException, InterruptedException; 98 | 99 | 100 | void setACL(String path, List acl, int version, 101 | AsyncCallback.StatCallback cb, Object ctx); 102 | 103 | 104 | List getChildren(String path, Watcher watcher) 105 | throws KeeperException, InterruptedException; 106 | 107 | List getChildren(String path, boolean watch) 108 | throws KeeperException, InterruptedException; 109 | 110 | void getChildren(String path, Watcher watcher, 111 | AsyncCallback.ChildrenCallback cb, Object ctx); 112 | 113 | void getChildren(String path, boolean watch, AsyncCallback.ChildrenCallback cb, 114 | Object ctx); 115 | 116 | List getChildren(String path, Watcher watcher, 117 | Stat stat) 118 | throws KeeperException, InterruptedException; 119 | 120 | List getChildren(String path, boolean watch, Stat stat) 121 | throws KeeperException, InterruptedException; 122 | 123 | void getChildren(String path, Watcher watcher, 124 | AsyncCallback.Children2Callback cb, Object ctx); 125 | 126 | void getChildren(String path, boolean watch, AsyncCallback.Children2Callback cb, 127 | Object ctx); 128 | 129 | void sync(String path, AsyncCallback.VoidCallback cb, Object ctx); 130 | 131 | ZooKeeper.States getState(); 132 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/IZooKeeperFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | import org.apache.zookeeper.Watcher; 20 | 21 | /** 22 | * Factory of {@link IZooKeeper}. 23 | * 24 | * @author ypujante@linkedin.com 25 | */ 26 | public interface IZooKeeperFactory 27 | { 28 | /** 29 | * @return the connect string that the factory uses. 30 | */ 31 | String getConnectString(); 32 | IZooKeeper createZooKeeper(Watcher watcher); 33 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/LifecycleListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | /** 20 | * Listener for state change 21 | */ 22 | public interface LifecycleListener 23 | { 24 | void onConnected(); 25 | void onDisconnected(); 26 | } 27 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/WatcherChain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 3 | * Portions Copyright 2013 Yan Pujante 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | * use this file except in compliance with the License. You may obtain a copy of 7 | * 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, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations under 15 | * the License. 16 | */ 17 | 18 | package org.linkedin.zookeeper.client; 19 | 20 | import org.apache.zookeeper.Watcher; 21 | import org.apache.zookeeper.WatchedEvent; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import java.util.Collection; 26 | import java.util.Arrays; 27 | import java.util.ArrayList; 28 | 29 | /** 30 | * @author ypujante@linkedin.com 31 | */ 32 | public class WatcherChain implements Watcher 33 | { 34 | public static final String MODULE = WatcherChain.class.getName(); 35 | public static final Logger log = LoggerFactory.getLogger(MODULE); 36 | 37 | private final Collection _watchers; 38 | 39 | /** 40 | * Constructor 41 | */ 42 | public WatcherChain(Watcher... watchers) 43 | { 44 | this(Arrays.asList(watchers)); 45 | } 46 | 47 | /** 48 | * Constructor 49 | */ 50 | public WatcherChain(Collection watchers) 51 | { 52 | _watchers = watchers; 53 | } 54 | 55 | @Override 56 | public void process(WatchedEvent event) 57 | { 58 | for(Watcher watcher : _watchers) 59 | { 60 | try 61 | { 62 | watcher.process(event); 63 | } 64 | catch(Throwable th) 65 | { 66 | log.warn("Unexpected exception while processing event [" + event 67 | + "] for watcher [" + watcher + "] (ignored)", th); 68 | } 69 | } 70 | } 71 | 72 | public static Watcher createChain(Watcher... watchers) 73 | { 74 | if(watchers == null || watchers.length == 0) 75 | return null; 76 | 77 | ArrayList list = new ArrayList(); 78 | 79 | for(Watcher watcher : watchers) 80 | { 81 | if(watcher != null) 82 | list.add(watcher); 83 | } 84 | 85 | if(list.size() == 1) 86 | return list.get(0); 87 | else 88 | return new WatcherChain(list); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/ZKChildren.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | 18 | package org.linkedin.zookeeper.client; 19 | 20 | import org.apache.zookeeper.data.Stat; 21 | 22 | import java.util.List; 23 | 24 | /** 25 | * @author ypujante@linkedin.com 26 | * 27 | */ 28 | public class ZKChildren 29 | { 30 | private final List _children; 31 | private final Stat _stat; 32 | 33 | /** 34 | * Constructor 35 | */ 36 | public ZKChildren(List children, Stat stat) 37 | { 38 | _children = children; 39 | _stat = stat; 40 | } 41 | 42 | public List getChildren() 43 | { 44 | return _children; 45 | } 46 | 47 | public Stat getStat() 48 | { 49 | return _stat; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/ZKData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | import org.apache.zookeeper.data.Stat; 20 | 21 | /** 22 | * @author ypujante@linkedin.com 23 | */ 24 | public class ZKData 25 | { 26 | private final T _data; 27 | private final Stat _stat; 28 | 29 | /** 30 | * Constructor 31 | */ 32 | public ZKData(T data, Stat stat) 33 | { 34 | _data = data; 35 | _stat = stat; 36 | } 37 | 38 | public Stat getStat() 39 | { 40 | return _stat; 41 | } 42 | 43 | public T getData() 44 | { 45 | return _data; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/ZooKeeperFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | import org.slf4j.Logger; 20 | import org.apache.zookeeper.Watcher; 21 | import org.apache.zookeeper.ZooKeeper; 22 | import org.linkedin.util.clock.Timespan; 23 | import org.linkedin.util.exceptions.InternalException; 24 | 25 | import java.io.IOException; 26 | 27 | /** 28 | * @author ypujante@linkedin.com 29 | */ 30 | public class ZooKeeperFactory implements IZooKeeperFactory 31 | { 32 | public static final String MODULE = ZooKeeperFactory.class.getName(); 33 | public static final Logger log = org.slf4j.LoggerFactory.getLogger(MODULE); 34 | 35 | private final String _connectString; 36 | private final Timespan _sessionTimeout; 37 | private final Watcher _watcher; 38 | 39 | /** 40 | * Constructor 41 | */ 42 | public ZooKeeperFactory(String connectString, Timespan sessionTimeout, Watcher watcher) 43 | { 44 | _connectString = connectString; 45 | _sessionTimeout = sessionTimeout; 46 | _watcher = watcher; 47 | } 48 | 49 | /** 50 | * Constructor 51 | */ 52 | public ZooKeeperFactory(String connectString, Timespan sessionTimeout) 53 | { 54 | this(connectString, sessionTimeout, null); 55 | } 56 | 57 | @Override 58 | public IZooKeeper createZooKeeper(Watcher watcher) 59 | { 60 | try 61 | { 62 | return new ZooKeeperImpl(new ZooKeeper(_connectString, 63 | (int) _sessionTimeout.getDurationInMilliseconds(), 64 | WatcherChain.createChain(_watcher, watcher))); 65 | } 66 | catch(IOException e) 67 | { 68 | throw new InternalException(MODULE, e); 69 | } 70 | } 71 | 72 | public String getConnectString() 73 | { 74 | return _connectString; 75 | } 76 | 77 | public Timespan getSessionTimeout() 78 | { 79 | return _sessionTimeout; 80 | } 81 | 82 | public Watcher getWatcher() 83 | { 84 | return _watcher; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/ZooKeeperImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | import org.slf4j.Logger; 20 | import org.apache.zookeeper.ZooKeeper; 21 | import org.apache.zookeeper.CreateMode; 22 | import org.apache.zookeeper.KeeperException; 23 | import org.apache.zookeeper.AsyncCallback; 24 | import org.apache.zookeeper.Watcher; 25 | import org.apache.zookeeper.data.ACL; 26 | import org.apache.zookeeper.data.Stat; 27 | 28 | import java.util.List; 29 | 30 | /** 31 | * Delegate all calls to ZooKeeper (and adjusts the exceptions). 32 | * 33 | * @author ypujante@linkedin.com 34 | */ 35 | public class ZooKeeperImpl implements IZooKeeper 36 | { 37 | public static final String MODULE = ZooKeeperImpl.class.getName(); 38 | public static final Logger log = org.slf4j.LoggerFactory.getLogger(MODULE); 39 | 40 | private final ZooKeeper _zk; 41 | 42 | /** 43 | * Constructor 44 | */ 45 | public ZooKeeperImpl(ZooKeeper zk) 46 | { 47 | _zk = zk; 48 | } 49 | 50 | private ZooKeeper getZk() 51 | { 52 | return _zk; 53 | } 54 | 55 | @Override 56 | public void addAuthInfo(String scheme, byte[] auth) 57 | { 58 | getZk().addAuthInfo(scheme, auth); 59 | } 60 | 61 | @Override 62 | public void close() throws InterruptedException 63 | { 64 | getZk().close(); 65 | } 66 | 67 | @Override 68 | public String create(String path, byte[] data, List acl, CreateMode createMode) 69 | throws KeeperException, InterruptedException 70 | { 71 | return getZk().create(path, data, acl, createMode); 72 | } 73 | 74 | @Override 75 | public void create(String path, 76 | byte[] data, 77 | List acl, 78 | CreateMode createMode, 79 | AsyncCallback.StringCallback cb, Object ctx) 80 | { 81 | getZk().create(path, data, acl, createMode, cb, ctx); 82 | } 83 | 84 | @Override 85 | public void delete(String path, int version) 86 | throws KeeperException, InterruptedException 87 | { 88 | getZk().delete(path, version); 89 | } 90 | 91 | @Override 92 | public void delete(String path, int version, AsyncCallback.VoidCallback cb, Object ctx) 93 | { 94 | getZk().delete(path, version, cb, ctx); 95 | } 96 | 97 | @Override 98 | public Stat exists(String path, boolean watch) 99 | throws KeeperException, InterruptedException 100 | { 101 | return getZk().exists(path, watch); 102 | } 103 | 104 | @Override 105 | public void exists(String path, boolean watch, AsyncCallback.StatCallback cb, Object ctx) 106 | { 107 | getZk().exists(path, watch, cb, ctx); 108 | } 109 | 110 | @Override 111 | public Stat exists(String path, Watcher watcher) 112 | throws KeeperException, InterruptedException 113 | { 114 | return getZk().exists(path, watcher); 115 | } 116 | 117 | @Override 118 | public void exists(String path, Watcher watcher, AsyncCallback.StatCallback cb, Object ctx) 119 | { 120 | getZk().exists(path, watcher, cb, ctx); 121 | } 122 | 123 | @Override 124 | public List getACL(String path, Stat stat) 125 | throws KeeperException, InterruptedException 126 | { 127 | return getZk().getACL(path, stat); 128 | } 129 | 130 | @Override 131 | public void getACL(String path, Stat stat, AsyncCallback.ACLCallback cb, Object ctx) 132 | { 133 | getZk().getACL(path, stat, cb, ctx); 134 | } 135 | 136 | @Override 137 | public List getChildren(String path, boolean watch) 138 | throws KeeperException, InterruptedException 139 | { 140 | return getZk().getChildren(path, watch); 141 | } 142 | 143 | @Override 144 | public void getChildren(String path, 145 | boolean watch, 146 | AsyncCallback.ChildrenCallback cb, 147 | Object ctx) 148 | { 149 | getZk().getChildren(path, watch, cb, ctx); 150 | } 151 | 152 | @Override 153 | public List getChildren(String path, Watcher watcher) 154 | throws KeeperException, InterruptedException 155 | { 156 | return getZk().getChildren(path, watcher); 157 | } 158 | 159 | @Override 160 | public void getChildren(String path, 161 | Watcher watcher, 162 | AsyncCallback.ChildrenCallback cb, 163 | Object ctx) 164 | { 165 | getZk().getChildren(path, watcher, cb, ctx); 166 | } 167 | 168 | @Override 169 | public void getData(String path, boolean watch, AsyncCallback.DataCallback cb, Object ctx) 170 | { 171 | getZk().getData(path, watch, cb, ctx); 172 | } 173 | 174 | @Override 175 | public byte[] getData(String path, boolean watch, Stat stat) 176 | throws KeeperException, InterruptedException 177 | { 178 | return getZk().getData(path, watch, stat); 179 | } 180 | 181 | @Override 182 | public void getData(String path, Watcher watcher, AsyncCallback.DataCallback cb, Object ctx) 183 | { 184 | getZk().getData(path, watcher, cb, ctx); 185 | } 186 | 187 | @Override 188 | public byte[] getData(String path, Watcher watcher, Stat stat) 189 | throws KeeperException, InterruptedException 190 | { 191 | return getZk().getData(path, watcher, stat); 192 | } 193 | 194 | @Override 195 | public long getSessionId() 196 | { 197 | return getZk().getSessionId(); 198 | } 199 | 200 | @Override 201 | public byte[] getSessionPasswd() 202 | { 203 | return getZk().getSessionPasswd(); 204 | } 205 | 206 | @Override 207 | public ZooKeeper.States getState() 208 | { 209 | return getZk().getState(); 210 | } 211 | 212 | @Override 213 | public void register(Watcher watcher) 214 | { 215 | getZk().register(watcher); 216 | } 217 | 218 | @Override 219 | public Stat setACL(String path, List acl, int version) 220 | throws KeeperException, InterruptedException 221 | { 222 | return getZk().setACL(path, acl, version); 223 | } 224 | 225 | @Override 226 | public void setACL(String path, 227 | List acl, 228 | int version, 229 | AsyncCallback.StatCallback cb, 230 | Object ctx) 231 | { 232 | getZk().setACL(path, acl, version, cb, ctx); 233 | } 234 | 235 | @Override 236 | public Stat setData(String path, byte[] data, int version) 237 | throws KeeperException, InterruptedException 238 | { 239 | return getZk().setData(path, data, version); 240 | } 241 | 242 | @Override 243 | public void setData(String path, 244 | byte[] data, 245 | int version, 246 | AsyncCallback.StatCallback cb, 247 | Object ctx) 248 | { 249 | getZk().setData(path, data, version, cb, ctx); 250 | } 251 | 252 | @Override 253 | public void sync(String path, AsyncCallback.VoidCallback cb, Object ctx) 254 | { 255 | getZk().sync(path, cb, ctx); 256 | } 257 | 258 | @Override 259 | public void getChildren(String path, 260 | boolean watch, 261 | AsyncCallback.Children2Callback cb, 262 | Object ctx) 263 | { 264 | getZk().getChildren(path, watch, cb, ctx); 265 | } 266 | 267 | @Override 268 | public int getSessionTimeout() 269 | { 270 | return getZk().getSessionTimeout(); 271 | } 272 | 273 | @Override 274 | public List getChildren(String path, Watcher watcher, Stat stat) 275 | throws KeeperException, InterruptedException 276 | { 277 | return getZk().getChildren(path, watcher, stat); 278 | } 279 | 280 | @Override 281 | public List getChildren(String path, boolean watch, Stat stat) 282 | throws KeeperException, InterruptedException 283 | { 284 | return getZk().getChildren(path, watch, stat); 285 | } 286 | 287 | @Override 288 | public void getChildren(String path, 289 | Watcher watcher, 290 | AsyncCallback.Children2Callback cb, 291 | Object ctx) 292 | { 293 | getZk().getChildren(path, watcher, cb, ctx); 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/client/ZooKeeperURLHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.client; 18 | 19 | import org.apache.zookeeper.KeeperException; 20 | import org.linkedin.util.exceptions.InternalException; 21 | import org.linkedin.util.text.TextUtils; 22 | 23 | import java.io.ByteArrayInputStream; 24 | import java.io.IOException; 25 | import java.io.InputStream; 26 | import java.net.URL; 27 | import java.net.URLConnection; 28 | import java.net.URLStreamHandler; 29 | 30 | /** 31 | * @author ypujante@linkedin.com 32 | */ 33 | public class ZooKeeperURLHandler extends URLStreamHandler 34 | { 35 | /** 36 | * Handle zookeeper:/a/b/c style urls 37 | * 38 | * @author ypujante@linkedin.com 39 | */ 40 | public static class ZooKeeperURLConnection extends URLConnection 41 | { 42 | private final IZKClient _zk; 43 | 44 | ZooKeeperURLConnection(URL url, IZKClient zk) 45 | { 46 | super(url); 47 | _zk = zk; 48 | } 49 | 50 | @Override 51 | public void connect() 52 | { 53 | // nothing to do here because the client is already connected... need to revisit when 54 | // we support host/port 55 | } 56 | 57 | @Override 58 | public InputStream getInputStream() throws IOException 59 | { 60 | try 61 | { 62 | return new ByteArrayInputStream(_zk.getData(url.getPath())); 63 | } 64 | catch(InternalException e) 65 | { 66 | throw new IOException(e); 67 | } 68 | catch(InterruptedException e) 69 | { 70 | throw new IOException(e); 71 | } 72 | catch(KeeperException e) 73 | { 74 | throw new IOException(e); 75 | } 76 | } 77 | } 78 | 79 | private final IZKClient _defaultClient; 80 | 81 | public ZooKeeperURLHandler(IZKClient defaultClient) 82 | { 83 | _defaultClient = defaultClient; 84 | } 85 | 86 | @Override 87 | protected URLConnection openConnection(URL url) 88 | { 89 | return new ZooKeeperURLConnection(url, _defaultClient); 90 | } 91 | 92 | @Override 93 | protected void parseURL(URL u, String spec, int start, int limit) 94 | { 95 | super.parseURL(u, spec, start, limit); 96 | 97 | if(!TextUtils.isEmptyString(u.getHost()) || u.getPort() != -1) 98 | throw new UnsupportedOperationException("host/port not supported yet"); 99 | 100 | if(!TextUtils.isEmptyString(u.getQuery())) 101 | throw new IllegalArgumentException("no query string is allowed"); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/server/StandaloneZooKeeperServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | 18 | package org.linkedin.zookeeper.server; 19 | 20 | import org.slf4j.Logger; 21 | import org.apache.zookeeper.server.NIOServerCnxn; 22 | import org.apache.zookeeper.server.NIOServerCnxnFactory; 23 | import org.apache.zookeeper.server.ServerConfig; 24 | import org.apache.zookeeper.server.ZooKeeperServer; 25 | import org.apache.zookeeper.server.persistence.FileTxnSnapLog; 26 | import org.apache.zookeeper.server.quorum.QuorumPeerConfig; 27 | import org.linkedin.util.annotations.Initializable; 28 | import org.linkedin.util.annotations.Initializer; 29 | import org.linkedin.util.clock.Clock; 30 | import org.linkedin.util.clock.ClockUtils; 31 | import org.linkedin.util.clock.SystemClock; 32 | import org.linkedin.util.concurrent.ConcurrentUtils; 33 | import org.linkedin.util.exceptions.InternalException; 34 | import org.linkedin.util.lifecycle.Shutdownable; 35 | import org.linkedin.util.lifecycle.Startable; 36 | import java.net.InetSocketAddress; 37 | 38 | import java.io.File; 39 | import java.io.IOException; 40 | import java.util.Properties; 41 | import java.util.concurrent.TimeoutException; 42 | 43 | /** 44 | * Encapsulates creation of a standalone zookeeper server (most likely 45 | * going to be used for testing only) 46 | * 47 | * @author ypujante@linkedin.com 48 | */ 49 | public class StandaloneZooKeeperServer implements Startable, Shutdownable 50 | { 51 | public static final String MODULE = StandaloneZooKeeperServer.class.getName(); 52 | public static final Logger log = org.slf4j.LoggerFactory.getLogger(MODULE); 53 | 54 | @Initializable 55 | public Clock clock = SystemClock.INSTANCE; 56 | 57 | private String _tickTime; 58 | private int _clientPort; 59 | private String _dataDir; 60 | 61 | private Thread _mainThread; 62 | 63 | private volatile boolean _shutdown = false; 64 | private NIOServerCnxnFactory _cnxnFactory; 65 | private ZooKeeperServer _zkServer; 66 | 67 | public StandaloneZooKeeperServer(String tickTime, String dataDir, int clientPort) 68 | { 69 | _dataDir = dataDir; 70 | _clientPort = clientPort; 71 | _tickTime = tickTime; 72 | } 73 | 74 | StandaloneZooKeeperServer() 75 | { 76 | _tickTime = "2000"; 77 | _clientPort = 2120; 78 | } 79 | 80 | public int getClientPort() 81 | { 82 | return _clientPort; 83 | } 84 | 85 | @Initializer 86 | public void setClientPort(int clientPort) 87 | { 88 | _clientPort = clientPort; 89 | } 90 | 91 | public String getDataDir() 92 | { 93 | return _dataDir; 94 | } 95 | 96 | @Initializer(required = true) 97 | public void setDataDir(String dataDir) 98 | { 99 | _dataDir = dataDir; 100 | } 101 | 102 | public String getTickTime() 103 | { 104 | return _tickTime; 105 | } 106 | 107 | @Initializer 108 | public void setTickTime(String tickTime) 109 | { 110 | _tickTime = tickTime; 111 | } 112 | 113 | @Override 114 | public synchronized void start() 115 | { 116 | try 117 | { 118 | QuorumPeerConfig qpc = new QuorumPeerConfig(); 119 | Properties p = new Properties(); 120 | p.setProperty("tickTime", _tickTime); 121 | p.setProperty("clientPort", String.valueOf(_clientPort)); 122 | p.setProperty("dataDir", _dataDir); 123 | qpc.parseProperties(p); 124 | 125 | ServerConfig serverConfig = new ServerConfig(); 126 | serverConfig.readFrom(qpc); 127 | 128 | runFromConfig(serverConfig); 129 | 130 | _mainThread = new Thread(new Runnable() 131 | { 132 | @Override 133 | public void run() 134 | { 135 | try 136 | { 137 | _cnxnFactory.join(); 138 | } 139 | catch(InterruptedException e) 140 | { 141 | throw new InternalException(e); 142 | } 143 | } 144 | }); 145 | _mainThread.start(); 146 | } 147 | catch(Exception e) 148 | { 149 | throw new InternalException(e); 150 | } 151 | } 152 | 153 | /** 154 | * This method is coming from ZooKeeperServerMain but tweaked to not block and to 155 | * have access to _zkServer and _cnxnFactory. 156 | */ 157 | private void runFromConfig(ServerConfig config) throws IOException 158 | { 159 | log.info("Starting server"); 160 | try 161 | { 162 | _zkServer = new ZooKeeperServer(); 163 | 164 | FileTxnSnapLog ftxn = new FileTxnSnapLog(new File(config.getDataLogDir()), 165 | new File(config.getDataDir())); 166 | _zkServer.setTxnLogFactory(ftxn); 167 | _zkServer.setTickTime(config.getTickTime()); 168 | _zkServer.setMinSessionTimeout(config.getMinSessionTimeout()); 169 | _zkServer.setMaxSessionTimeout(config.getMaxSessionTimeout()); 170 | _cnxnFactory = new NIOServerCnxnFactory(); 171 | _cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns()); 172 | 173 | _cnxnFactory.startup(_zkServer); 174 | } 175 | catch(InterruptedException e) 176 | { 177 | // warn, but generally this is ok 178 | log.warn("Server interrupted", e); 179 | } 180 | } 181 | 182 | @Override 183 | public synchronized void shutdown() 184 | { 185 | if(_mainThread == null) 186 | throw new IllegalStateException("not started"); 187 | 188 | if(_shutdown) 189 | return; 190 | 191 | _cnxnFactory.shutdown(); 192 | 193 | _shutdown = true; 194 | } 195 | 196 | @Override 197 | public void waitForShutdown() throws InterruptedException, IllegalStateException 198 | { 199 | try 200 | { 201 | waitForShutdown(-1); 202 | } 203 | catch(TimeoutException e) 204 | { 205 | throw new RuntimeException(e); 206 | } 207 | } 208 | 209 | /** 210 | * Waits for shutdown to be completed. After calling shutdown, there may still be some pending work 211 | * that needs to be accomplised. This method will block until it is done but no longer than the 212 | * timeout. 213 | * 214 | * @param timeout how long to wait maximum for the shutdown (see {@link ClockUtils#toTimespan(Object)}) 215 | * @throws InterruptedException if interrupted while waiting 216 | * @throws IllegalStateException if shutdown has not been called 217 | * @throws TimeoutException if shutdown still not complete after timeout 218 | */ 219 | @Override 220 | public void waitForShutdown(Object timeout) 221 | throws InterruptedException, IllegalStateException, TimeoutException 222 | { 223 | if(!_shutdown) 224 | throw new IllegalStateException("call shutdown first"); 225 | ConcurrentUtils.joinFor(clock, _mainThread, timeout); 226 | 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/tracker/ErrorListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.tracker; 18 | 19 | import org.apache.zookeeper.WatchedEvent; 20 | 21 | /** 22 | * An error listener. 23 | * 24 | * @author ypujante 25 | */ 26 | public interface ErrorListener 27 | { 28 | void onError(WatchedEvent event, Throwable throwable); 29 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/tracker/NodeEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | 18 | package org.linkedin.zookeeper.tracker; 19 | 20 | import org.linkedin.util.annotations.Initializer; 21 | 22 | import java.io.File; 23 | 24 | /** 25 | * Event generated when something changes 26 | * 27 | * @author ypujante@linkedin.com 28 | */ 29 | public class NodeEvent 30 | { 31 | private NodeEventType _eventType; 32 | private TrackedNode _node; 33 | 34 | public NodeEvent() 35 | { 36 | // use initializers 37 | } 38 | 39 | public NodeEvent(NodeEventType eventType, TrackedNode node) 40 | { 41 | _eventType = eventType; 42 | _node = node; 43 | } 44 | 45 | public NodeEventType getEventType() 46 | { 47 | return _eventType; 48 | } 49 | 50 | @Initializer(required = true) 51 | public void setEventType(NodeEventType eventType) 52 | { 53 | _eventType = eventType; 54 | } 55 | 56 | public TrackedNode getNode() 57 | { 58 | return _node; 59 | } 60 | 61 | @Initializer(required = true) 62 | public void setNode(TrackedNode node) 63 | { 64 | _node = node; 65 | } 66 | 67 | public T getData() 68 | { 69 | return _node.getData(); 70 | } 71 | 72 | public String getPath() 73 | { 74 | return _node.getPath(); 75 | } 76 | 77 | public String getParentName() 78 | { 79 | return new File(getPath()).getParentFile().getName(); 80 | } 81 | 82 | public String getName() 83 | { 84 | return new File(getPath()).getName(); 85 | } 86 | 87 | public int getDepth() 88 | { 89 | return _node.getDepth(); 90 | } 91 | 92 | public String toString() 93 | { 94 | StringBuilder sb = new StringBuilder(); 95 | sb.append("NodeEvent ").append(getNode()).append(": "); 96 | sb.append(getEventType()); 97 | return sb.toString(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/tracker/NodeEventType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | 18 | package org.linkedin.zookeeper.tracker; 19 | 20 | /** 21 | * Enum about node types 22 | * 23 | * @author ypujante@linkedin.com 24 | */ 25 | public enum NodeEventType 26 | { 27 | ADDED, 28 | UPDATED, 29 | DELETED 30 | } 31 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/tracker/NodeEventsListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | 18 | package org.linkedin.zookeeper.tracker; 19 | 20 | import java.util.Collection; 21 | 22 | /** 23 | * An even listener. 24 | * 25 | * @author ypujante@linkedin.com 26 | */ 27 | public interface NodeEventsListener 28 | { 29 | void onEvents(Collection> events); 30 | } 31 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/tracker/TrackedNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | 18 | package org.linkedin.zookeeper.tracker; 19 | 20 | import org.apache.zookeeper.data.Stat; 21 | import org.linkedin.util.annotations.Initializer; 22 | 23 | /** 24 | * A node being tracked 25 | * 26 | * @author ypujante@linkedin.com 27 | */ 28 | public class TrackedNode 29 | { 30 | private String _path; 31 | private T _data; 32 | private Stat _stat; 33 | private int _depth; 34 | 35 | public TrackedNode() 36 | { 37 | // use initializers... 38 | } 39 | 40 | public TrackedNode(String path, T data, Stat stat, int depth) 41 | { 42 | _data = data; 43 | _depth = depth; 44 | _path = path; 45 | _stat = stat; 46 | } 47 | 48 | public T getData() 49 | { 50 | return _data; 51 | } 52 | 53 | @Initializer(required = true) 54 | public void setData(T data) 55 | { 56 | _data = data; 57 | } 58 | 59 | public int getDepth() 60 | { 61 | return _depth; 62 | } 63 | 64 | @Initializer(required = true) 65 | public void setDepth(int depth) 66 | { 67 | _depth = depth; 68 | } 69 | 70 | public String getPath() 71 | { 72 | return _path; 73 | } 74 | 75 | @Initializer(required = true) 76 | public void setPath(String path) 77 | { 78 | _path = path; 79 | } 80 | 81 | public Stat getStat() 82 | { 83 | return _stat; 84 | } 85 | 86 | @Initializer(required = true) 87 | public void setStat(Stat stat) 88 | { 89 | _stat = stat; 90 | } 91 | 92 | public long getCreationTime() 93 | { 94 | return _stat.getCtime(); 95 | } 96 | 97 | public long getModifiedTime() 98 | { 99 | return _stat.getMtime(); 100 | } 101 | 102 | public long getZkTxId() 103 | { 104 | return Math.max(_stat.getMzxid(), _stat.getPzxid()); 105 | } 106 | 107 | public String toString() 108 | { 109 | StringBuilder sb = new StringBuilder(); 110 | sb.append("{path:'").append(getPath()).append("',"); 111 | sb.append("zktxid:").append(getZkTxId()).append(","); 112 | sb.append("stat:'").append(getStat().toString().trim()).append("',"); 113 | sb.append("data:'").append(getData()).append("'}"); 114 | return sb.toString(); 115 | } 116 | 117 | @Override 118 | public boolean equals(Object o) 119 | { 120 | if(this == o) return true; 121 | if(o == null || getClass() != o.getClass()) return false; 122 | 123 | TrackedNode that = (TrackedNode) o; 124 | 125 | if(_depth != that._depth) return false; 126 | if(_data != null ? !_data.equals(that._data) : that._data != null) return false; 127 | if(_path != null ? !_path.equals(that._path) : that._path != null) return false; 128 | if(_stat != null ? !_stat.equals(that._stat) : that._stat != null) return false; 129 | 130 | return true; 131 | } 132 | 133 | @Override 134 | public int hashCode() 135 | { 136 | int result = _path != null ? _path.hashCode() : 0; 137 | result = 31 * result + (_stat != null ? _stat.hashCode() : 0); 138 | result = 31 * result + (_data != null ? _data.hashCode() : 0); 139 | result = 31 * result + _depth; 140 | return result; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/tracker/ZKByteArrayDataReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | 18 | package org.linkedin.zookeeper.tracker; 19 | 20 | import org.apache.zookeeper.KeeperException; 21 | import org.apache.zookeeper.Watcher; 22 | import org.linkedin.zookeeper.client.IZKClient; 23 | import org.linkedin.zookeeper.client.ZKData; 24 | 25 | import java.util.Arrays; 26 | 27 | /** 28 | * @author ypujante@linkedin.com 29 | * 30 | */ 31 | public class ZKByteArrayDataReader implements ZKDataReader 32 | { 33 | @Override 34 | public ZKData readData(IZKClient zkClient, String path, Watcher watcher) 35 | throws InterruptedException, KeeperException 36 | { 37 | return zkClient.getZKByteData(path, watcher); 38 | } 39 | 40 | /** 41 | * Compare 2 data equality 42 | * 43 | * @return true if equal (in the {@link Object#equals(Object)} definition) 44 | */ 45 | @Override 46 | public boolean isEqual(byte[] data1, byte[] data2) 47 | { 48 | return Arrays.equals(data1, data2); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/tracker/ZKDataReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | 18 | package org.linkedin.zookeeper.tracker; 19 | 20 | import org.apache.zookeeper.KeeperException; 21 | import org.apache.zookeeper.Watcher; 22 | import org.linkedin.zookeeper.client.IZKClient; 23 | import org.linkedin.zookeeper.client.ZKData; 24 | 25 | /** 26 | * Note that the implementation *must* set the watcher on read!!! 27 | * 28 | * @author ypujante@linkedin.com 29 | * 30 | */ 31 | public interface ZKDataReader 32 | { 33 | ZKData readData(IZKClient zkClient, 34 | String path, 35 | Watcher watcher) throws InterruptedException, KeeperException; 36 | 37 | /** 38 | * Compare 2 data equality 39 | * @return true if equal (in the {@link Object#equals(Object)} definition) 40 | */ 41 | boolean isEqual(T data1, T data2); 42 | } -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/tracker/ZKStringDataReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | 18 | package org.linkedin.zookeeper.tracker; 19 | 20 | import org.apache.zookeeper.KeeperException; 21 | import org.apache.zookeeper.Watcher; 22 | import org.linkedin.util.lang.LangUtils; 23 | import org.linkedin.zookeeper.client.IZKClient; 24 | import org.linkedin.zookeeper.client.ZKData; 25 | 26 | /** 27 | * @author ypujante@linkedin.com 28 | * 29 | */ 30 | public class ZKStringDataReader implements ZKDataReader 31 | { 32 | public static final ZKStringDataReader INSTANCE = new ZKStringDataReader(); 33 | 34 | public static ZKStringDataReader instance() 35 | { 36 | return INSTANCE; 37 | } 38 | 39 | @Override 40 | public ZKData readData(IZKClient zkClient, String path, Watcher watcher) 41 | throws InterruptedException, KeeperException 42 | { 43 | return zkClient.getZKStringData(path, watcher); 44 | } 45 | 46 | /** 47 | * Compare 2 data equality 48 | * 49 | * @return true if equal (in the {@link Object#equals(Object)} definition) 50 | */ 51 | @Override 52 | public boolean isEqual(String data1, String data2) 53 | { 54 | return LangUtils.isEqual(data1, data2); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/main/java/org/linkedin/zookeeper/tracker/ZooKeeperTreeTracker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package org.linkedin.zookeeper.tracker; 18 | 19 | import org.slf4j.Logger; 20 | import org.apache.zookeeper.KeeperException; 21 | import org.apache.zookeeper.WatchedEvent; 22 | import org.apache.zookeeper.Watcher; 23 | import org.linkedin.util.annotations.Initializable; 24 | import org.linkedin.util.clock.Clock; 25 | import org.linkedin.util.clock.ClockUtils; 26 | import org.linkedin.util.clock.SystemClock; 27 | import org.linkedin.util.concurrent.ConcurrentUtils; 28 | import org.linkedin.util.io.PathUtils; 29 | import org.linkedin.util.lang.LangUtils; 30 | import org.linkedin.util.lifecycle.Destroyable; 31 | import org.linkedin.zookeeper.client.IZKClient; 32 | import org.linkedin.zookeeper.client.ZKChildren; 33 | import org.linkedin.zookeeper.client.ZKData; 34 | 35 | import java.util.ArrayList; 36 | import java.util.Collection; 37 | import java.util.Collections; 38 | import java.util.LinkedHashMap; 39 | import java.util.LinkedHashSet; 40 | import java.util.Map; 41 | import java.util.Set; 42 | import java.util.concurrent.TimeoutException; 43 | 44 | 45 | /** 46 | * The purpose of this class is to essentially keep a replica of ZooKeeper data in memory and 47 | * be able to be notified when it changes. 48 | * 49 | * @author ypujante@linkedin.com */ 50 | public class ZooKeeperTreeTracker implements Destroyable 51 | { 52 | public static final String MODULE = ZooKeeperTreeTracker.class.getName(); 53 | public static final Logger log = org.slf4j.LoggerFactory.getLogger(MODULE); 54 | 55 | @Initializable 56 | public Clock clock = SystemClock.INSTANCE; 57 | 58 | private final IZKClient _zk; 59 | private final ZKDataReader _zkDataReader; 60 | private final String _root; 61 | private final int _depth; 62 | 63 | private final Set _errorListeners = new LinkedHashSet(); 64 | private final Set> _eventsListeners = new LinkedHashSet>(); 65 | 66 | private volatile Map> _tree = new LinkedHashMap>(); 67 | private volatile boolean _destroyed = false; 68 | private long _lastZkTxId; 69 | private final Object _lock = new Object(); 70 | private final int _rootDepth; 71 | private final Watcher _treeWacher = new TreeWatcher(); 72 | 73 | private class TreeWatcher implements Watcher 74 | { 75 | @Override 76 | public void process(WatchedEvent event) 77 | { 78 | if(log.isDebugEnabled()) 79 | log.debug(logString(event.getPath(), 80 | "treeWatcher: type=" + event.getType()) + ", state=" + event.getState()); 81 | 82 | Collection> events = new ArrayList>(); 83 | 84 | try 85 | { 86 | synchronized(_lock) 87 | { 88 | if(!handleEvent(event)) 89 | return; 90 | 91 | switch(event.getType()) 92 | { 93 | case NodeDeleted: 94 | _tree = handleNodeDeleted(event.getPath(), events); 95 | break; 96 | 97 | case NodeCreated: 98 | throw new RuntimeException("getting node created event ? when ?"); 99 | 100 | case NodeChildrenChanged: 101 | _tree = handleNodeChildrenChanged(event.getPath(), events); 102 | break; 103 | 104 | case NodeDataChanged: 105 | _tree = handleNodeDataChanged(event.getPath(), events); 106 | break; 107 | 108 | case None: 109 | // nothing to do 110 | break; 111 | } 112 | } 113 | 114 | raiseEvents(events); 115 | } 116 | catch (Throwable th) 117 | { 118 | log.warn(logString(event.getPath(), "Error in treeWatcher (ignored)"), th); 119 | raiseError(event, th); 120 | } 121 | } 122 | } 123 | 124 | 125 | public ZooKeeperTreeTracker(IZKClient zk, ZKDataReader zkDataReader, String root) 126 | { 127 | this(zk, zkDataReader, root, Integer.MAX_VALUE); 128 | } 129 | 130 | public ZooKeeperTreeTracker(IZKClient zk, ZKDataReader zkDataReader, String root, int depth) 131 | { 132 | _zk = zk; 133 | _zkDataReader = zkDataReader; 134 | _root = root; 135 | _rootDepth = computeAbsoluteDepth(_root); 136 | _depth = depth; 137 | } 138 | 139 | public static int computeAbsoluteDepth(String path) 140 | { 141 | if(path == null) 142 | return 0; 143 | 144 | int depth = 0; 145 | 146 | for(int i = 0; i < path.length(); i++) 147 | { 148 | char c = path.charAt(i); 149 | if(c == '/') 150 | depth++; 151 | } 152 | 153 | return depth; 154 | } 155 | 156 | public String getRoot() 157 | { 158 | return _root; 159 | } 160 | 161 | public IZKClient getZKCient() 162 | { 163 | return _zk; 164 | } 165 | 166 | public int getDepth() 167 | { 168 | return _depth; 169 | } 170 | 171 | public long getLastZkTxId() 172 | { 173 | synchronized(_lock) 174 | { 175 | return _lastZkTxId; 176 | } 177 | } 178 | 179 | /** 180 | * Waits no longer than the timeout provided (or forever if null) for this tracker 181 | * to have seen at least the provided zookeeper transaction id 182 | * @return the last known zkTxId (guaranteed to be >= to zkTxId) 183 | */ 184 | public long waitForZkTxId(long zkTxId, Object timeout) 185 | throws TimeoutException, InterruptedException 186 | { 187 | long endTime = ClockUtils.toEndTime(clock, timeout); 188 | 189 | synchronized(_lock) 190 | { 191 | while(_lastZkTxId < zkTxId) 192 | { 193 | ConcurrentUtils.awaitUntil(clock, _lock, endTime); 194 | } 195 | 196 | return _lastZkTxId; 197 | } 198 | } 199 | 200 | /** 201 | * It is not possible to remove a watcher... so we just set the tracker in destroyed mode which 202 | * will simply ignore all subsequent event. 203 | */ 204 | @Override 205 | public void destroy() 206 | { 207 | synchronized(_lock) 208 | { 209 | _destroyed = true; 210 | } 211 | } 212 | 213 | /** 214 | * @return the tree 215 | */ 216 | public Map> getTree() 217 | { 218 | // note that there is no need for synchronization because: 219 | // 1. the attribute is volatile 220 | // 2. the map is never modified (a new one is created / replaced when needed) 221 | return _tree; 222 | } 223 | 224 | public void track(NodeEventsListener eventsListener) 225 | throws InterruptedException, KeeperException 226 | { 227 | registerListener(eventsListener); 228 | track(); 229 | } 230 | 231 | public void track() throws InterruptedException, KeeperException 232 | { 233 | Collection> events = new ArrayList>(); 234 | 235 | synchronized(_lock) 236 | { 237 | _tree = trackNode(_root, new LinkedHashMap>(), events, 0); 238 | } 239 | 240 | raiseEvents(events); 241 | } 242 | 243 | public void registerListener(NodeEventsListener eventsListener) 244 | { 245 | synchronized(_lock) 246 | { 247 | _eventsListeners.add(eventsListener); 248 | } 249 | } 250 | 251 | public void registerErrorListener(ErrorListener errorListener) 252 | { 253 | synchronized(_lock) 254 | { 255 | _errorListeners.add(errorListener); 256 | } 257 | } 258 | 259 | /** 260 | * Must be called from a synchronized section 261 | */ 262 | private Map> trackNode(String path, 263 | Map> tree, 264 | Collection> events, 265 | int depth) 266 | throws InterruptedException, KeeperException 267 | { 268 | if(depth > _depth) 269 | { 270 | if(log.isDebugEnabled()) 271 | log.debug(logString(path, "max depth reached ${depth}")); 272 | 273 | return tree; 274 | } 275 | 276 | TrackedNode oldTrackedNode = tree.get(path); 277 | 278 | try 279 | { 280 | ZKData res = _zkDataReader.readData(_zk, path, _treeWacher); 281 | 282 | TrackedNode newTrackedNode = 283 | new TrackedNode(path, res.getData(), res.getStat(), depth); 284 | if(oldTrackedNode != null) 285 | { 286 | if(!_zkDataReader.isEqual(oldTrackedNode.getData(), newTrackedNode.getData())) 287 | { 288 | events.add(new NodeEvent(NodeEventType.UPDATED, 289 | newTrackedNode)); 290 | } 291 | } 292 | else 293 | { 294 | events.add(new NodeEvent(NodeEventType.ADDED, 295 | newTrackedNode)); 296 | } 297 | 298 | tree.put(path, newTrackedNode); 299 | 300 | if(depth < _depth) 301 | { 302 | ZKChildren children = _zk.getZKChildren(path, _treeWacher); 303 | // the stat may change between the 2 calls 304 | if(!newTrackedNode.getStat().equals(children.getStat())) 305 | newTrackedNode.setStat(children.getStat()); 306 | Collections.sort(children.getChildren()); 307 | for(String child : children.getChildren()) 308 | { 309 | String childPath = PathUtils.addPaths(path, child); 310 | if(!tree.containsKey(childPath)) 311 | trackNode(childPath, tree, events, depth + 1); 312 | } 313 | } 314 | 315 | _lastZkTxId = Math.max(_lastZkTxId, newTrackedNode.getZkTxId()); 316 | _lock.notifyAll(); 317 | if(log.isDebugEnabled()) 318 | log.debug(logString(path, 319 | "start tracking " + (depth < _depth ? "": "leaf ") + 320 | "node zkTxId=" + newTrackedNode.getZkTxId())); 321 | 322 | } 323 | catch (KeeperException.NoNodeException e) 324 | { 325 | // this is a race condition which could happen between the moment the event is received 326 | // and this call 327 | if(log.isDebugEnabled()) 328 | log.debug(logString(path, "no such node")); 329 | // it means that the node has disappeared 330 | 331 | tree.remove(path); 332 | 333 | if(oldTrackedNode != null) 334 | { 335 | events.add(new NodeEvent(NodeEventType.DELETED, 336 | oldTrackedNode)); 337 | } 338 | } 339 | 340 | return tree; 341 | } 342 | 343 | private Map> handleNodeDeleted(String path, 344 | Collection> events) 345 | throws InterruptedException, KeeperException 346 | { 347 | Map> tree = _tree; 348 | if(_tree.containsKey(path)) 349 | { 350 | tree = new LinkedHashMap>(_tree); 351 | TrackedNode trackedNode = tree.remove(path); 352 | events.add(new NodeEvent(NodeEventType.DELETED, 353 | trackedNode)); 354 | if(log.isDebugEnabled()) 355 | log.debug(logString(path, "stop tracking node")); 356 | 357 | // after a delete event, we try to track the node again as a delete/add event could happen 358 | // and be undetected otherwise! 359 | trackNode(path, tree, events, trackedNode.getDepth()); 360 | } 361 | 362 | return tree; 363 | } 364 | 365 | private Map> handleNodeDataChanged(String path, 366 | Collection> events) 367 | throws InterruptedException, KeeperException 368 | { 369 | return trackNode(path, 370 | new LinkedHashMap>(_tree), 371 | events, 372 | computeDepth(path)); 373 | } 374 | 375 | private Map> handleNodeChildrenChanged(String path, 376 | Collection> events) 377 | throws InterruptedException, KeeperException 378 | { 379 | return trackNode(path, 380 | new LinkedHashMap>(_tree), 381 | events, 382 | computeDepth(path)); 383 | } 384 | 385 | private int computeDepth(String path) 386 | { 387 | return computeAbsoluteDepth(path) - _rootDepth; 388 | } 389 | 390 | private void raiseEvents(Collection> events) 391 | { 392 | if(!events.isEmpty()) 393 | { 394 | Set> listeners; 395 | synchronized(_lock) 396 | { 397 | listeners = new LinkedHashSet>(_eventsListeners); 398 | } 399 | 400 | for(NodeEventsListener listener : listeners) 401 | { 402 | listener.onEvents(events); 403 | } 404 | } 405 | } 406 | 407 | private boolean handleEvent(WatchedEvent event) 408 | { 409 | if(_destroyed) 410 | { 411 | return false; 412 | } 413 | 414 | switch(event.getState()) 415 | { 416 | case SyncConnected: 417 | return true; 418 | 419 | case Disconnected: 420 | return false; 421 | 422 | case Expired: 423 | return false; 424 | 425 | default: 426 | return false; 427 | } 428 | } 429 | 430 | private String logString(String path, String msg) 431 | { 432 | StringBuilder sb = new StringBuilder(); 433 | sb.append("[").append(path).append("] "); 434 | sb.append("[").append(LangUtils.shortIdentityString(this)).append("] "); 435 | sb.append("[").append(Thread.currentThread()).append("] "); 436 | sb.append(msg); 437 | return sb.toString(); 438 | } 439 | 440 | private void raiseError(WatchedEvent event, Throwable th) 441 | { 442 | Set listeners; 443 | synchronized(_lock) 444 | { 445 | listeners = new LinkedHashSet(_errorListeners); 446 | } 447 | 448 | if(!listeners.isEmpty()) 449 | { 450 | for(ErrorListener listener : listeners) 451 | { 452 | try 453 | { 454 | if(log.isDebugEnabled()) 455 | log.debug(logString(event.getPath(), "Raising error to " + 456 | LangUtils.identityString(listener)), 457 | th); 458 | 459 | listener.onError(event, th); 460 | } 461 | catch(Throwable th2) 462 | { 463 | log.warn(logString(event.getPath(), "Error in watcher while executing listener " + 464 | LangUtils.identityString(listener) + 465 | " (ignored)"), 466 | th2); 467 | } 468 | } 469 | } 470 | } 471 | } 472 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/test/groovy/test/zookeeper/client/TestZKClient.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 3 | * Portions Copyright 2013 Yan Pujante 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | * use this file except in compliance with the License. You may obtain a copy of 7 | * 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, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations under 15 | * the License. 16 | */ 17 | 18 | package test.zookeeper.client 19 | 20 | import org.apache.zookeeper.CreateMode 21 | import org.apache.zookeeper.KeeperException 22 | import org.apache.zookeeper.Watcher 23 | import org.apache.zookeeper.ZooDefs.Ids 24 | import org.apache.zookeeper.ZooKeeper 25 | import org.linkedin.groovy.util.concurrent.GroovyConcurrentUtils 26 | import org.linkedin.groovy.util.net.SingletonURLStreamHandlerFactory 27 | import org.linkedin.util.clock.SystemClock 28 | import org.linkedin.util.clock.Timespan 29 | import org.linkedin.util.concurrent.ThreadControl 30 | import org.linkedin.util.exceptions.InternalException 31 | import org.linkedin.zookeeper.client.IZKClient 32 | import org.linkedin.zookeeper.client.IZooKeeperFactory 33 | import org.linkedin.zookeeper.client.LifecycleListener 34 | import org.linkedin.zookeeper.client.ZKClient 35 | import org.linkedin.zookeeper.client.ZooKeeperImpl 36 | import org.linkedin.zookeeper.client.ZooKeeperURLHandler 37 | import org.linkedin.zookeeper.server.StandaloneZooKeeperServer 38 | import org.linkedin.groovy.util.io.fs.FileSystemImpl 39 | import org.slf4j.Logger 40 | import org.slf4j.LoggerFactory 41 | 42 | import java.util.concurrent.TimeoutException 43 | 44 | /** 45 | * Test for the zookeeper client 46 | * 47 | * @author ypujante@linkedin.com 48 | */ 49 | class TestZKClient extends GroovyTestCase 50 | { 51 | public static final String MODULE = TestZKClient.class.getName(); 52 | public static final Logger log = LoggerFactory.getLogger(MODULE); 53 | 54 | FileSystemImpl fs = FileSystemImpl.createTempFileSystem() 55 | StandaloneZooKeeperServer zookeeperServer 56 | 57 | protected void setUp() 58 | { 59 | super.setUp(); 60 | 61 | zkStart() 62 | } 63 | 64 | private void zkStart() 65 | { 66 | zookeeperServer = new StandaloneZooKeeperServer(tickTime: 2000, 67 | clientPort: 2121, 68 | dataDir: fs.root.file.canonicalPath) 69 | zookeeperServer.start() 70 | } 71 | 72 | protected void tearDown() 73 | { 74 | try 75 | { 76 | zkShutdown() 77 | fs.destroy() 78 | } 79 | finally 80 | { 81 | super.tearDown(); 82 | } 83 | } 84 | 85 | private void zkShutdown() 86 | { 87 | zookeeperServer.shutdown() 88 | zookeeperServer.waitForShutdown(1000) 89 | } 90 | 91 | void testClient() 92 | { 93 | ZKClient client 94 | client = new ZKClient('127.0.0.1:2121', Timespan.parse('1m'), null) 95 | 96 | try 97 | { 98 | client.start() 99 | client.waitForStart(Timespan.parse('10s')) 100 | 101 | assertEquals('127.0.0.1:2121', client.getConnectString()) 102 | 103 | // registers url factory 104 | def factory = new SingletonURLStreamHandlerFactory() 105 | factory.registerHandler('zookeeper') { 106 | return new ZooKeeperURLHandler(client) 107 | } 108 | URL.setURLStreamHandlerFactory(factory) 109 | 110 | assertEquals(ZooKeeper.States.CONNECTED, client.state) 111 | 112 | // there is always a zookeeper child when zookeeper starts... 113 | assertEquals(1, client.exists('/').numChildren) 114 | assertEquals('zookeeper', client.getChildren('/')[0]) 115 | 116 | assertNull(client.exists('/a')) 117 | client.create('/a', 'test1', Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 118 | assertEquals(2, client.exists('/').numChildren) 119 | assertEquals('test1', client.getStringData('/a')) 120 | assertEquals('test1', new URL('zookeeper:/a').text) 121 | assertEquals(new HashSet(['zookeeper', 'a']), new HashSet(client.getChildren('/'))) 122 | 123 | // cannot create a child with no parent 124 | shouldFail(KeeperException.NoNodeException) { 125 | client.create('/a/b/c/d/e/f', 'test2', Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 126 | } 127 | 128 | // now it should work 129 | client.createWithParents('/a/b/c/d/e/f', 'test2', Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 130 | assertEquals('test2', client.getStringData('/a/b/c/d/e/f')) 131 | assertEquals('test2', new URL('zookeeper:/a/b/c/d/e/f').text) 132 | 133 | // cannot delete a non empty directory 134 | shouldFail(KeeperException.NotEmptyException) { 135 | client.delete('/a/b') 136 | } 137 | 138 | // this one should work 139 | client.deleteWithChildren('/a/b') 140 | assertNull(client.exists('/a/b')) 141 | 142 | // we make sure that /a still exists 143 | assertEquals('test1', client.getStringData('/a')) 144 | 145 | IZKClient chrootedClient = client.chroot("/a") 146 | assertEquals(0, chrootedClient.exists('/').numChildren) 147 | 148 | chrootedClient.createWithParents('/z/k/w', 'test3', Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 149 | assertEquals('test3', chrootedClient.getStringData('/z/k/w')) 150 | assertEquals('test3', client.getStringData('/a/z/k/w')) 151 | 152 | // now we test createOrSet 153 | client.createOrSetWithParents('/a/b/c', 'test4', Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 154 | assertEquals('test4', client.getStringData('/a/b/c')) 155 | client.createOrSetWithParents('/a/b/c', 'test4.updated', Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 156 | assertEquals('test4.updated', client.getStringData('/a/b/c')) 157 | 158 | // at this state we have /a, /a/b, /a/b/c, /a/z, /a/z/k, /a/z/k/w 159 | assertEquals(['b', 'z'], client.getChildren('/a').sort()) 160 | assertEquals(['b', 'b/c', 'z', 'z/k', 'z/k/w'], client.getAllChildren('/a').sort()) 161 | 162 | // error cases 163 | assertEquals('host/port not supported yet', 164 | shouldFail(MalformedURLException) { new URL('zookeeper://127.0.0.1:2121/a') }) 165 | assertEquals('no query string is allowed', 166 | shouldFail(MalformedURLException) { new URL('zookeeper:/a?p1=v1') }) 167 | } 168 | finally 169 | { 170 | client.destroy() 171 | } 172 | } 173 | 174 | private ZooKeeper _testableZooKeeper 175 | private boolean _failCreation = false 176 | private int _failedCount = 0 177 | 178 | private testableZooKeeperFactory = { Watcher watcher -> 179 | if(_failCreation) 180 | { 181 | _failedCount++ 182 | throw new InternalException('TestZKClient', "failing creation ${_failedCount}") 183 | } 184 | 185 | _testableZooKeeper = new ZooKeeper('127.0.0.1:2121', 186 | (int) Timespan.parse('1m').durationInMilliseconds, 187 | watcher) 188 | 189 | new ZooKeeperImpl(_testableZooKeeper) 190 | } 191 | 192 | /** 193 | * see FAQ about forcing expire session http://wiki.apache.org/hadoop/ZooKeeper/FAQ#A4 194 | */ 195 | private void expireZooKeeperSession() 196 | { 197 | def factory = { Watcher watcher -> 198 | new ZooKeeperImpl(new ZooKeeper('127.0.0.1:2121', 199 | _testableZooKeeper.sessionTimeout, 200 | watcher as Watcher, 201 | _testableZooKeeper.sessionId, // session id 202 | _testableZooKeeper.sessionPasswd)) // password 203 | } 204 | 205 | def zk = new ZKClient(factory as IZooKeeperFactory) 206 | zk.start() 207 | zk.waitForStart(Timespan.parse('10s')) 208 | zk.close() 209 | } 210 | 211 | /** 212 | * We verify that we can recover properly. 213 | */ 214 | public void testRecovery() 215 | { 216 | ZKClient client 217 | client = new ZKClient(testableZooKeeperFactory as IZooKeeperFactory) 218 | client.reconnectTimeout = Timespan.parse('500') 219 | 220 | ThreadControl th = new ThreadControl(Timespan.parse('10s')) 221 | 222 | client.registerListener([ 223 | onConnected: { 224 | th.block('onConnected') 225 | }, 226 | 227 | onDisconnected: { 228 | th.block('onDisconnected') 229 | } 230 | ] as LifecycleListener) 231 | 232 | try 233 | { 234 | assertEquals(ZKClient.State.NONE, client.ZKClientState) 235 | 236 | // start has not been called yet 237 | assertEquals("not connected", shouldFail(IllegalStateException) { 238 | client.exists('/a') 239 | }) 240 | 241 | client.start() 242 | client.waitForStart(Timespan.parse('10s')) 243 | 244 | th.waitForBlock('onConnected') 245 | th.unblock('onConnected') 246 | 247 | assertEquals(ZKClient.State.CONNECTED, client.ZKClientState) 248 | 249 | assertNull(client.exists('/a')) 250 | 251 | assertEquals(ZKClient.State.CONNECTED, client.ZKClientState) 252 | 253 | client.create('/a', 'testa', Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 254 | assertEquals('testa', client.getStringData('/a')) 255 | 256 | client.create('/b', 'testb', Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL) 257 | assertEquals('testb', client.getStringData('/b')) 258 | 259 | // now we simulate a disconnect from zookeeper: 260 | zkShutdown() 261 | 262 | // depending on when we receive the event, we can get either exception... 263 | try 264 | { 265 | client.getStringData('/a') 266 | fail("should fail with an exception") 267 | } 268 | catch (KeeperException.ConnectionLossException ignored) 269 | { 270 | // ok 271 | } 272 | catch (IllegalStateException e) 273 | { 274 | assertEquals("not connected", e.message) 275 | } 276 | 277 | client.waitForState(ZKClient.State.RECONNECTING, Timespan.parse('5s')) 278 | 279 | th.waitForBlock('onDisconnected') 280 | th.unblock('onDisconnected') 281 | 282 | // once the client is disconnected then we should get the IllegalStateException only 283 | assertEquals("not connected", shouldFail(IllegalStateException) { 284 | client.getStringData('/a') 285 | }) 286 | 287 | // we wait a little... to make sure that we exercise the recovery loop 288 | Thread.sleep(600) 289 | 290 | // we restart the server 291 | zkStart() 292 | 293 | // we wait for the client to 'reconnect' 294 | client.waitForStart(Timespan.parse('10s')) 295 | 296 | th.waitForBlock('onConnected') 297 | th.unblock('onConnected') 298 | 299 | assertEquals('testa', client.getStringData('/a')) 300 | // in this type of recovery, ephemeral nodes should not be lost! 301 | assertEquals('testb', client.getStringData('/b')) 302 | 303 | // we force an expired event 304 | expireZooKeeperSession() 305 | 306 | // this will trigger a disconnect and connect event 307 | th.waitForBlock('onDisconnected') 308 | th.unblock('onDisconnected') 309 | 310 | th.waitForBlock('onConnected') 311 | th.unblock('onConnected') 312 | 313 | // we wait for start again 314 | client.waitForStart(Timespan.parse('10s')) 315 | 316 | assertEquals('testa', client.getStringData('/a')) 317 | // when reconnecting from an expiration, ephemeral nodes are lost 318 | shouldFail(KeeperException.NoNodeException) { 319 | client.getStringData('/b') 320 | } 321 | 322 | // we generate a failure 323 | _failCreation = true 324 | _failedCount = 0 325 | 326 | expireZooKeeperSession() 327 | 328 | th.waitForBlock('onDisconnected') 329 | th.unblock('onDisconnected') 330 | 331 | // the expiration recovery mode should kick in and fail several time since reconnect timeout 332 | // is 500ms 333 | shouldFail(TimeoutException) { 334 | client.waitForStart(Timespan.parse('2s')) 335 | } 336 | 337 | GroovyConcurrentUtils.waitForCondition(SystemClock.INSTANCE, Timespan.parse('10s'), 250) { 338 | _failedCount >= 4 339 | } 340 | 341 | _failCreation = false 342 | 343 | th.waitForBlock('onConnected') 344 | th.unblock('onConnected') 345 | 346 | assertTrue(_failedCount >= 4) 347 | 348 | // we wait for start again 349 | client.waitForStart(Timespan.parse('10s')) 350 | 351 | assertEquals('testa', client.getStringData('/a')) 352 | // when reconnecting from an expiration, ephemeral nodes are lost 353 | shouldFail(KeeperException.NoNodeException) { 354 | client.getStringData('/b') 355 | } 356 | } 357 | finally 358 | { 359 | log.info "destroying client" 360 | client.destroy() 361 | } 362 | 363 | th.waitForBlock('onDisconnected') 364 | th.unblock('onDisconnected') 365 | } 366 | 367 | /** 368 | * We verify that the listener gets the onConnected event even if registered after the start 369 | * of the client. 370 | */ 371 | public void testListener() 372 | { 373 | ZKClient client 374 | client = new ZKClient('127.0.0.1:2121', Timespan.parse('1m'), null) 375 | client.reconnectTimeout = Timespan.parse('500') 376 | 377 | ThreadControl th = new ThreadControl() 378 | 379 | try 380 | { 381 | client.start() 382 | client.waitForStart(Timespan.parse('10s')) 383 | 384 | client.registerListener([ 385 | onConnected: { 386 | th.block('onConnected') 387 | }, 388 | 389 | onDisconnected: { 390 | th.block('onDisconnected') 391 | } 392 | ] as LifecycleListener) 393 | 394 | th.waitForBlock('onConnected') 395 | th.unblock('onConnected') 396 | 397 | } 398 | finally 399 | { 400 | client.destroy() 401 | } 402 | 403 | th.waitForBlock('onDisconnected') 404 | th.unblock('onDisconnected') 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-impl/src/test/groovy/test/zookeeper/client/TestZooKeeperTreeTracker.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | package test.zookeeper.client 18 | 19 | import org.apache.zookeeper.ZooDefs.Ids 20 | import org.apache.zookeeper.CreateMode 21 | import junit.framework.AssertionFailedError 22 | import org.linkedin.groovy.util.io.fs.FileSystemImpl 23 | import org.linkedin.zookeeper.server.StandaloneZooKeeperServer 24 | import org.linkedin.zookeeper.client.ZKClient 25 | import org.linkedin.util.clock.Timespan 26 | import org.linkedin.zookeeper.tracker.ZooKeeperTreeTracker 27 | import org.linkedin.zookeeper.tracker.NodeEventsListener 28 | import org.linkedin.zookeeper.tracker.NodeEventType 29 | import org.linkedin.zookeeper.tracker.NodeEvent 30 | import java.util.concurrent.TimeoutException 31 | import org.linkedin.zookeeper.tracker.TrackedNode 32 | import org.linkedin.zookeeper.client.IZKClient 33 | import org.linkedin.util.io.PathUtils 34 | import org.linkedin.zookeeper.tracker.ZKStringDataReader 35 | import org.slf4j.Logger 36 | import org.slf4j.LoggerFactory 37 | 38 | /** 39 | * @author ypujante@linkedin.com */ 40 | class TestZooKeeperTreeTracker extends GroovyTestCase 41 | { 42 | public static final String MODULE = TestZooKeeperTreeTracker.class.getName(); 43 | public static final Logger log = LoggerFactory.getLogger(MODULE); 44 | 45 | FileSystemImpl fs = FileSystemImpl.createTempFileSystem() 46 | StandaloneZooKeeperServer zookeeperServer 47 | 48 | protected void setUp() 49 | { 50 | super.setUp(); 51 | 52 | zkStart() 53 | } 54 | 55 | private void zkStart() 56 | { 57 | zookeeperServer = new StandaloneZooKeeperServer(tickTime: 2000, 58 | clientPort: 2121, 59 | dataDir: fs.root.file.canonicalPath) 60 | zookeeperServer.start() 61 | } 62 | 63 | protected void tearDown() 64 | { 65 | try 66 | { 67 | zkShutdown() 68 | fs.destroy() 69 | } 70 | finally 71 | { 72 | super.tearDown(); 73 | } 74 | } 75 | 76 | private void zkShutdown() 77 | { 78 | zookeeperServer.shutdown() 79 | zookeeperServer.waitForShutdown(1000) 80 | } 81 | 82 | public void testClient() 83 | { 84 | ZKClient client 85 | client = new ZKClient('127.0.0.1:2121', Timespan.parse('1m'), null) 86 | 87 | try 88 | { 89 | client.start() 90 | client.waitForStart(Timespan.parse('10s')) 91 | 92 | createNode(client, '/root', 'rootContent') 93 | 94 | def newEvents = [] 95 | def listener = { events -> 96 | events.each { newEvents << it } 97 | } 98 | 99 | def tracker = new ZooKeeperTreeTracker(client, new ZKStringDataReader(), '/root', 2) 100 | 101 | try 102 | { 103 | // starting to track 104 | tracker.track(listener as NodeEventsListener) 105 | checkTracker(tracker, newEvents) 106 | checkEvents([[type: NodeEventType.ADDED, path: '/root', data: 'rootContent']], newEvents) 107 | 108 | // adding a node at depth 1 109 | createNode(client, '/root/depth1_node1', 'depth1_node1') 110 | checkTracker(tracker, newEvents) 111 | checkEvents([[type: NodeEventType.ADDED, path: '/root/depth1_node1', data: 'depth1_node1']], newEvents) 112 | 113 | // adding a node at depth 2 114 | createNode(client, '/root/depth1_node1/depth2_node1', 'depth2_node1') 115 | checkTracker(tracker, newEvents) 116 | checkEvents([[type: NodeEventType.ADDED, path: '/root/depth1_node1/depth2_node1', data: 'depth2_node1']], newEvents) 117 | 118 | // adding a node at depth 3 (no events because not tracked) 119 | createNode(client, '/root/depth1_node1/depth2_node1/depth3_node1', 'depth3_node1') 120 | checkTracker(tracker, newEvents) 121 | checkEvents([], newEvents) 122 | 123 | // updating node at depth2 124 | client.setData('root/depth1_node1/depth2_node1', 'depth2_node1_new') 125 | checkTracker(tracker, newEvents) 126 | checkEvents([[type: NodeEventType.UPDATED, path: '/root/depth1_node1/depth2_node1', data: 'depth2_node1_new']], newEvents) 127 | 128 | // updating node at depth2 with same value (no events) 129 | client.setData('root/depth1_node1/depth2_node1', 'depth2_node1_new') 130 | checkTracker(tracker, newEvents) 131 | checkEvents([], newEvents) 132 | 133 | // updating node at depth1 134 | client.setData('root/depth1_node1', 'depth1_node1_new') 135 | checkTracker(tracker, newEvents) 136 | checkEvents([[type: NodeEventType.UPDATED, path: '/root/depth1_node1', data: 'depth1_node1_new']], newEvents) 137 | 138 | // updating node at depth2 again 139 | client.setData('root/depth1_node1/depth2_node1', 'depth2_node1') 140 | checkTracker(tracker, newEvents) 141 | checkEvents([[type: NodeEventType.UPDATED, path: '/root/depth1_node1/depth2_node1', data: 'depth2_node1']], newEvents) 142 | 143 | // adding a node at depth 2 144 | createNode(client, '/root/depth1_node1/depth2_node2', 'depth2_node2') 145 | checkTracker(tracker, newEvents) 146 | checkEvents([[type: NodeEventType.ADDED, path: '/root/depth1_node1/depth2_node2', data: 'depth2_node2']], newEvents) 147 | 148 | // deleting node at depth2 and adding another one 149 | client.delete('root/depth1_node1/depth2_node2') 150 | createNode(client, '/root/depth1_node1/depth2_node3', 'depth2_node3') 151 | checkTracker(tracker, newEvents) 152 | checkEvents([[type: NodeEventType.DELETED, path: '/root/depth1_node1/depth2_node2', data: 'depth2_node2'], 153 | [type: NodeEventType.ADDED, path: '/root/depth1_node1/depth2_node3', data: 'depth2_node3']], newEvents) 154 | 155 | // delete all nodes 156 | client.deleteWithChildren('root/depth1_node1') 157 | checkTracker(tracker, newEvents) 158 | checkEvents([[type: NodeEventType.DELETED, path: '/root/depth1_node1', data: 'depth1_node1_new'], 159 | [type: NodeEventType.DELETED, path: '/root/depth1_node1/depth2_node1', data: 'depth2_node1'], 160 | [type: NodeEventType.DELETED, path: '/root/depth1_node1/depth2_node3', data: 'depth2_node3']], newEvents) 161 | } 162 | finally 163 | { 164 | tracker.destroy() 165 | } 166 | } 167 | finally 168 | { 169 | client.destroy() 170 | } 171 | } 172 | 173 | /** 174 | * This test tries to reproduce the problem described in GLU-190: create/add/update children 175 | * in a very fast fashion and make sure we do not loose events 176 | */ 177 | public void testRaceCondition() 178 | { 179 | ZKClient client 180 | client = new ZKClient('127.0.0.1:2121', Timespan.parse('1m'), null) 181 | 182 | try 183 | { 184 | client.start() 185 | client.waitForStart(Timespan.parse('10s')) 186 | 187 | def idx = 1 188 | 189 | while(idx < 2) 190 | { 191 | def root = "/root${idx++}".toString() 192 | 193 | log.debug "executing for ${root}" 194 | 195 | createNode(client, root, 'rootContent') 196 | 197 | def newEvents = [] 198 | def listener = { events -> 199 | events.each { newEvents << it } 200 | } 201 | 202 | def tracker = new ZooKeeperTreeTracker(client, new ZKStringDataReader(), root, 2) 203 | 204 | try 205 | { 206 | // starting to track 207 | tracker.track(listener as NodeEventsListener) 208 | checkTracker(tracker, newEvents) 209 | checkEvents([[type: NodeEventType.ADDED, path: root, data: 'rootContent']], newEvents) 210 | 211 | def hostsCount = 10 212 | 213 | def threads = [] 214 | 215 | def rnd = new Random() 216 | 217 | (1..hostsCount).each { host -> 218 | def thread = Thread.start { 219 | createNode(client, "${root}/h${host}", "host h${host} content") 220 | Thread.sleep(rnd.nextInt(100)) 221 | 222 | def iterationsCount = rnd.nextInt(100) + 100 223 | 224 | def childrenCount = rnd.nextInt(4) + 1 225 | 226 | (1..iterationsCount).each { iter -> 227 | (0..childrenCount).each { child -> 228 | def path = "${root}/h${host}/c${child}" 229 | if(client.exists(path)) 230 | { 231 | if(rnd.nextBoolean()) 232 | client.setData(path, "U: h${host}/${child} -> ${iter}") 233 | else 234 | client.delete(path) 235 | } 236 | else 237 | createNode(client, path, "C: h${host}/${child} -> ${iter}") 238 | } 239 | Thread.sleep(rnd.nextInt(10)) 240 | } 241 | 242 | 243 | } 244 | threads << thread 245 | } 246 | 247 | threads.each { it.join() } 248 | checkTracker(tracker, newEvents) 249 | client.deleteWithChildren(root) 250 | } 251 | finally 252 | { 253 | tracker.destroy() 254 | } 255 | } 256 | } 257 | finally 258 | { 259 | client.destroy() 260 | } 261 | } 262 | 263 | private void checkEvents(def expectedEvents, def receivedEvents) 264 | { 265 | try 266 | { 267 | receivedEvents = receivedEvents.sort { it.node.path } 268 | assertEquals(expectedEvents.size(), receivedEvents.size()) 269 | receivedEvents.eachWithIndex { NodeEvent event, idx -> 270 | def expectedEvent = expectedEvents[idx] 271 | assertEquals(expectedEvent.type, event.eventType) 272 | assertEquals(expectedEvent.path, event.path) 273 | assertEquals(expectedEvent.data, event.node.data) 274 | } 275 | } 276 | catch(AssertionFailedError e) 277 | { 278 | def error = new AssertionFailedError("${expectedEvents} != ${receivedEvents}") 279 | error.initCause(e) 280 | throw error 281 | } 282 | 283 | receivedEvents.clear() 284 | } 285 | 286 | private void checkTracker(ZooKeeperTreeTracker tracker, newEvents) 287 | { 288 | def expectedTree = readTree(tracker.ZKCient, tracker.root, tracker.depth, 0, new TreeMap()) 289 | 290 | def expectedLastZkTxId = expectedTree.values().zkTxId.max() 291 | 292 | try 293 | { 294 | def lastZkTxId = tracker.waitForZkTxId(expectedLastZkTxId, '10s') 295 | assertEquals(expectedLastZkTxId, lastZkTxId) 296 | } 297 | catch(TimeoutException e) 298 | { 299 | println "######## Issue with waitForZkTxId for ${expectedLastZkTxId}" 300 | println "### Events:" 301 | println newEvents.join('\n') 302 | 303 | println "### Expected tree:" 304 | expectedTree.each { k,v -> 305 | println "${k} | ${v}" 306 | } 307 | println "### Computed tree:" 308 | tracker.tree.each { 309 | k,v -> 310 | println "${k} | ${v}" 311 | } 312 | throw e 313 | } 314 | 315 | 316 | def tree = new TreeMap(tracker.tree) 317 | assertEquals(expectedTree.size(), tree.size()) 318 | expectedTree.values().each { TrackedNode expectedTrackedNode -> 319 | TrackedNode trackedNode = tree[expectedTrackedNode.path] 320 | assertNotNull(expectedTrackedNode.path, trackedNode) 321 | assertEquals(expectedTrackedNode.depth, trackedNode.depth) 322 | assertEquals(expectedTrackedNode.data, trackedNode.data) 323 | } 324 | } 325 | 326 | private def readTree(IZKClient client, String path, int maxDepth, int depth, def tree) 327 | { 328 | def res = client.getZKStringData(path) 329 | tree[path] = new TrackedNode(path: path, data: res.data, stat: res.stat, depth: depth) 330 | 331 | if(depth < maxDepth) 332 | { 333 | client.getChildren(path)?.each { String child -> 334 | def childPath = PathUtils.addPaths(path, child) 335 | readTree(client, childPath, maxDepth, depth + 1, tree) 336 | } 337 | } 338 | else 339 | { 340 | // adjusting the pzxid for leaves as they are not tracked by the tracker 341 | tree[path].stat.pzxid = tree[path].stat.mzxid 342 | } 343 | 344 | return tree 345 | } 346 | 347 | 348 | 349 | private void createNode(ZKClient client, String path, String content) 350 | { 351 | client.createWithParents(path, content, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-server/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | apply plugin: 'org.linkedin.cmdline' 18 | apply plugin: 'org.linkedin.release' 19 | 20 | dependencies { 21 | lib spec.external.zookeeper 22 | } 23 | 24 | cmdline { 25 | folders << 'data' 26 | resources << fileTree(dir: rootDir, includes: ['*.txt', '*.md']) 27 | } 28 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-server/src/cmdline/resources/bin/zkCleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # 19 | # This script cleans up old transaction logs and snapshots 20 | # 21 | 22 | # 23 | # If this scripted is run out of /usr/bin or some other system bin directory 24 | # it should be linked to and not copied. Things like java jar files are found 25 | # relative to the canonical path of this script. 26 | # 27 | 28 | # Only follow symlinks if readlink supports it 29 | if readlink -f "$0" > /dev/null 2>&1 30 | then 31 | ZOOBIN=`readlink -f "$0"` 32 | else 33 | ZOOBIN="$0" 34 | fi 35 | ZOOBINDIR=`dirname "$ZOOBIN"` 36 | 37 | . $ZOOBINDIR/zkEnv.sh 38 | 39 | ZOODATADIR=$(grep '^dataDir=' $ZOOCFG | sed -e 's/.*=//') 40 | ZOODATALOGDIR=$(grep '^dataLogDir=' $ZOOCFG | sed -e 's/.*=//') 41 | 42 | if [ "x${ZOODATALOGDIR}" = "x" ] 43 | then 44 | echo $JAVA_CMD "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ 45 | -cp $CLASSPATH $JVMFLAGS \ 46 | org.apache.zookeeper.server.PurgeTxnLog $ZOODATADIR $* 47 | else 48 | echo $JAVA_CMD "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ 49 | -cp $CLASSPATH $JVMFLAGS \ 50 | org.apache.zookeeper.server.PurgeTxnLog $ZOODATALOGDIR $ZOODATADIR $* 51 | fi 52 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-server/src/cmdline/resources/bin/zkCli.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # 19 | # This script cleans up old transaction logs and snapshots 20 | # 21 | 22 | # 23 | # If this scripted is run out of /usr/bin or some other system bin directory 24 | # it should be linked to and not copied. Things like java jar files are found 25 | # relative to the canonical path of this script. 26 | # 27 | 28 | # Only follow symlinks if readlink supports it 29 | if readlink -f "$0" > /dev/null 2>&1 30 | then 31 | ZOOBIN=`readlink -f "$0"` 32 | else 33 | ZOOBIN="$0" 34 | fi 35 | ZOOBINDIR=`dirname "$ZOOBIN"` 36 | 37 | . $ZOOBINDIR/zkEnv.sh 38 | 39 | eval `grep -e "^dataDir=" $ZOOCFG` 40 | 41 | $JAVA_CMD "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ 42 | -cp $CLASSPATH $JVMFLAGS \ 43 | org.apache.zookeeper.ZooKeeperMain $@ 44 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-server/src/cmdline/resources/bin/zkEnv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Customized LinkedIn ZooKeeper ENV 4 | # 5 | 6 | # Licensed to the Apache Software Foundation (ASF) under one or more 7 | # contributor license agreements. See the NOTICE file distributed with 8 | # this work for additional information regarding copyright ownership. 9 | # The ASF licenses this file to You under the Apache License, Version 2.0 10 | # (the "License"); you may not use this file except in compliance with 11 | # the License. You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | 21 | # This script should be sourced into other zookeeper 22 | # scripts to setup the env variables 23 | 24 | ZOOCFGDIR="$ZOOBINDIR/../conf" 25 | 26 | ZOOCFG="zoo.cfg" 27 | 28 | ZOOCFG="$ZOOCFGDIR/$ZOOCFG" 29 | 30 | ZOO_LOG_DIR="$ZOOBINDIR/../logs" 31 | 32 | ZOO_LOG4J_PROP="INFO,CONSOLE" 33 | 34 | ZOOLIBDIR=${ZOOLIBDIR:-$ZOOBINDIR/../lib} 35 | for i in "$ZOOLIBDIR"/*.jar 36 | do 37 | CLASSPATH="$CLASSPATH:$i" 38 | done 39 | 40 | # Add conf dir to the classpath (to find zoo.cfg) 41 | CLASSPATH=$ZOOCFGDIR:$CLASSPATH 42 | 43 | # set JAVA_HOME (JDK6) & JAVA_CMD 44 | if [ -z "$JAVA_HOME" ]; then 45 | case $(uname -s) in 46 | SunOS ) 47 | JAVA_HOME=/export/apps/jdk/JDK-1_6_0_16/usr/java 48 | JAVA_CMD=$JAVA_HOME/bin/$(isainfo -n)/java 49 | ;; 50 | Darwin) 51 | JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home 52 | JAVA_CMD=$JAVA_HOME/bin/java 53 | ;; 54 | * ) 55 | if [ -z "$JAVA_HOME" ] 56 | then 57 | echo "Unknown platform (not SunOS or Darwin)!!! Please set JAVA_HOME manually." 58 | exit 1 59 | fi 60 | ;; 61 | esac 62 | fi 63 | 64 | if [ -z "$JAVA_CMD" ]; then 65 | JAVA_CMD=$JAVA_HOME/bin/java 66 | fi 67 | 68 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-server/src/cmdline/resources/bin/zkServer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # LinkedIn Custom ZooKeeper Script 4 | 5 | # Licensed to the Apache Software Foundation (ASF) under one or more 6 | # contributor license agreements. See the NOTICE file distributed with 7 | # this work for additional information regarding copyright ownership. 8 | # The ASF licenses this file to You under the Apache License, Version 2.0 9 | # (the "License"); you may not use this file except in compliance with 10 | # the License. You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | # 21 | # If this scripted is run out of /usr/bin or some other system bin directory 22 | # it should be linked to and not copied. Things like java jar files are found 23 | # relative to the canonical path of this script. 24 | # 25 | 26 | # See the following page for extensive details on setting 27 | # up the JVM to accept JMX remote management: 28 | # http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html 29 | # by default we allow local JMX connections 30 | if [ "x$JMXLOCALONLY" = "x" ] 31 | then 32 | JMXLOCALONLY=false 33 | fi 34 | 35 | if [ "x$JMXDISABLE" = "x" ] 36 | then 37 | echo "JMX enabled by default" 38 | # for some reason these two options are necessary on jdk6 on Ubuntu 39 | # accord to the docs they are not necessary, but otw jconsole cannot 40 | # do a local attach 41 | ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain" 42 | else 43 | echo "JMX disabled by user request" 44 | ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain" 45 | fi 46 | 47 | # Only follow symlinks if readlink supports it 48 | if readlink -f "$0" > /dev/null 2>&1 49 | then 50 | ZOOBIN=`readlink -f "$0"` 51 | else 52 | ZOOBIN="$0" 53 | fi 54 | ZOOBINDIR=`dirname "$ZOOBIN"` 55 | 56 | . $ZOOBINDIR/zkEnv.sh 57 | 58 | if [ "x$2" != "x" ] 59 | then 60 | ZOOCFG=$ZOOCFGDIR/$2 61 | fi 62 | echo "Using config: $ZOOCFG" 63 | 64 | ZOODATADIR=$(grep dataDir $ZOOCFG | sed -e 's/.*=//') 65 | ZOODATADIR=$(echo $ZOODATADIR | sed -e "s|^[^/]|$ZOOBINDIR/../&|") 66 | ZOOPIDFILE=$ZOODATADIR/zookeeper_server.pid 67 | 68 | case $1 in 69 | start) 70 | echo "Starting zookeeper ... " 71 | $JAVA_CMD "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ 72 | -cp $CLASSPATH $JVMFLAGS $ZOOMAIN $ZOOCFG & 73 | echo $! > $ZOOPIDFILE 74 | echo STARTED 75 | ;; 76 | stop) 77 | echo "Stopping zookeeper ... " 78 | if [ ! -f $ZOOPIDFILE ] 79 | then 80 | echo "error: count not find file $ZOOPIDFILE" 81 | exit 1 82 | else 83 | kill -9 $(cat $ZOOPIDFILE) 84 | rm $ZOOPIDFILE 85 | echo STOPPED 86 | fi 87 | ;; 88 | upgrade) 89 | shift 90 | echo "upgrading the servers to 3.*" 91 | $JAVA_CMD "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ 92 | -cp $CLASSPATH $JVMFLAGS org.apache.zookeeper.server.upgrade.UpgradeMain ${@} 93 | echo "Upgrading ... " 94 | ;; 95 | restart) 96 | shift 97 | $0 stop ${@} 98 | sleep 3 99 | $0 start ${@} 100 | ;; 101 | status) 102 | STAT=`echo stat | nc localhost $(grep clientPort $ZOOCFG | sed -e 's/.*=//') 2> /dev/null| grep Mode` 103 | if [ "x$STAT" = "x" ] 104 | then 105 | echo "Error contacting service. It is probably not running." 106 | else 107 | echo $STAT 108 | fi 109 | ;; 110 | *) 111 | echo "Usage: $0 {start|stop|restart|status}" >&2 112 | 113 | esac 114 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-server/src/cmdline/resources/conf/log4j.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010-2010 LinkedIn, Inc 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 | 17 | # 18 | # ZooKeeper Logging Configuration 19 | # 20 | 21 | # Format is " (, )+ 22 | 23 | # DEFAULT: console appender only 24 | log4j.rootLogger=INFO, ROLLINGFILE 25 | 26 | # Example with rolling log file 27 | #log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE 28 | 29 | # Example with rolling log file and tracing 30 | #log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE 31 | 32 | # 33 | # Log INFO level and above messages to the console 34 | # 35 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 36 | log4j.appender.CONSOLE.Threshold=INFO 37 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 38 | log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n 39 | 40 | # 41 | # Add ROLLINGFILE to rootLogger to get log file output 42 | # Log DEBUG level and above messages to a log file 43 | log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender 44 | log4j.appender.ROLLINGFILE.Threshold=DEBUG 45 | log4j.appender.ROLLINGFILE.File=logs/zookeeper.log 46 | 47 | # Max log file size of 10MB 48 | log4j.appender.ROLLINGFILE.MaxFileSize=10MB 49 | # uncomment the next line to limit number of backup files 50 | #log4j.appender.ROLLINGFILE.MaxBackupIndex=10 51 | 52 | log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout 53 | log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n 54 | 55 | 56 | # 57 | # Add TRACEFILE to rootLogger to get log file output 58 | # Log DEBUG level and above messages to a log file 59 | log4j.appender.TRACEFILE=org.apache.log4j.FileAppender 60 | log4j.appender.TRACEFILE.Threshold=TRACE 61 | log4j.appender.TRACEFILE.File=zookeeper_trace.log 62 | 63 | log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout 64 | ### Notice we are including log4j's NDC here (%x) 65 | log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n 66 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper-server/src/cmdline/resources/conf/zoo.cfg: -------------------------------------------------------------------------------- 1 | # The number of milliseconds of each tick 2 | tickTime=2000 3 | # The number of ticks that the initial 4 | # synchronization phase can take 5 | initLimit=10 6 | # The number of ticks that can pass between 7 | # sending a request and getting an acknowledgement 8 | syncLimit=5 9 | # the directory where the snapshot is stored. 10 | dataDir=data 11 | # the port at which the clients will connect 12 | clientPort=2181 13 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper.log4j-test-config/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Yan Pujante 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 | 17 | apply plugin: 'java' 18 | apply plugin: 'org.linkedin.release' 19 | 20 | dependencies { 21 | runtime spec.external.slf4jLog4j 22 | runtime spec.external.log4j 23 | } 24 | -------------------------------------------------------------------------------- /org.linkedin.zookeeper.log4j-test-config/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /project-spec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2010 LinkedIn, Inc 3 | * Portions Copyright (c) 2012-2013 Yan Pujante 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | * use this file except in compliance with the License. You may obtain a copy of 7 | * 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, WITHOUT 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | * License for the specific language governing permissions and limitations under 15 | * the License. 16 | */ 17 | 18 | spec = [ 19 | name: 'linkedin-zookeeper', 20 | group: 'org.linkedin', 21 | version: '1.5.1', 22 | 23 | versions: [ 24 | jdk: '1.6', 25 | groovy: '2.0.7', 26 | linkedinUtils: '1.9.0', 27 | slf4j: '1.6.2' // to be compatible with grails 2.2.1 28 | ], 29 | 30 | // information about the build framework itself 31 | build: [ 32 | type: "gradle", 33 | commands: [ 34 | "snapshot": "./gradlew xxx", 35 | "release": "./gradlew -Prelease=true xxx" 36 | ] 37 | ] 38 | ] 39 | 40 | spec.scmUrl = "git@github.com:linkedin/${spec.name}.git" 41 | 42 | /** 43 | * External dependencies 44 | */ 45 | spec.external = [ 46 | commonsCli: 'commons-cli:commons-cli:1.2', 47 | groovy: "org.codehaus.groovy:groovy-all:${spec.versions.groovy}", 48 | groovyTest: "org.codehaus.groovy:groovy-test:${spec.versions.groovy}", 49 | junit: 'junit:junit:4.10', 50 | linkedinUtilsCore: "org.linkedin:org.linkedin.util-core:${spec.versions.linkedinUtils}", 51 | linkedinUtilsGroovy: "org.linkedin:org.linkedin.util-groovy:${spec.versions.linkedinUtils}", 52 | log4j: 'log4j:log4j:1.2.16', 53 | slf4j: "org.slf4j:slf4j-api:${spec.versions.slf4j}", 54 | slf4jLog4j: "org.slf4j:slf4j-log4j12:${spec.versions.slf4j}", 55 | slf4jJul: "org.slf4j:jul-to-slf4j:${spec.versions.slf4j}", 56 | zookeeper: 'org.apache.zookeeper:zookeeper:3.4.5' 57 | ] 58 | -------------------------------------------------------------------------------- /repositories.gradle: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2010-2010 LinkedIn, Inc 4 | * Portions Copyright (c) 2013 Yan Pujante 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | * use this file except in compliance with the License. You may obtain a copy of 8 | * the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | * License for the specific language governing permissions and limitations under 16 | * the License. 17 | */ 18 | 19 | def topReleaseDir = new File(userConfig.top.release.dir ?: "${topBuildDir}/release").canonicalFile 20 | def topPublishDir = new File(userConfig.top.publish.dir ?: "${topBuildDir}/publish").canonicalFile 21 | 22 | /** 23 | * Repositories for build 24 | */ 25 | allRepositories.build = { 26 | add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) { 27 | name = "build-release" 28 | addIvyPattern "${topReleaseDir}/[organisation]/[module]/[revision]/[module]-[revision].ivy" 29 | addArtifactPattern "${topReleaseDir}/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]" 30 | m2compatible = true 31 | checkmodified = true 32 | } 33 | 34 | mavenRepo name: 'build-publish', url: topPublishDir.toURI() 35 | 36 | mavenCentral() 37 | } 38 | 39 | /** 40 | * Repositories for buildScript 41 | */ 42 | allRepositories.buildscript = allRepositories.build 43 | 44 | /** 45 | * Repositories for release 46 | */ 47 | allRepositories.snapshotRelease = { 48 | add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) { 49 | name = 'local-release' 50 | addIvyPattern "${topReleaseDir}/[organisation]/[module]/[revision]/[module]-[revision].ivy" 51 | addArtifactPattern "${topReleaseDir}/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]" 52 | m2compatible = true 53 | } 54 | } 55 | 56 | allRepositories.release = allRepositories.snapshotRelease 57 | 58 | /** 59 | * Repositories for publish 60 | */ 61 | allRepositories.snapshotPublish = { 62 | mavenDeployer { 63 | repository(url: "file://localhost${topPublishDir}") 64 | pom.whenConfigured { pomToConfigure -> 65 | pomToConfigure.project { 66 | name project.name 67 | description 'The project represents a set of utility classes and wrappers around ZooKeeper' 68 | url 'http://github.com/linkedin/linkedin-utils' 69 | licenses { 70 | license { 71 | name 'The Apache Software License, Version 2.0' 72 | url 'http://www.apache.org/licenses/LICENSE-2.0' 73 | } 74 | } 75 | developers { 76 | developer { 77 | id 'ypujante' 78 | name 'Yan Pujante' 79 | email 'yan@pongasoft.com' 80 | } 81 | } 82 | scm { 83 | connection "scm:git:${spec.scmUrl}" 84 | developerConnection "scm:git:${spec.scmUrl}" 85 | url spec.scmUrl 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | allRepositories.publish = allRepositories.snapshotPublish 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010-2010 LinkedIn, Inc 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 | 17 | include ':org.linkedin.zookeeper-impl', 18 | ':org.linkedin.zookeeper-cli-impl', 19 | ':org.linkedin.zookeeper-cli', 20 | ':org.linkedin.zookeeper-server', 21 | ':org.linkedin.zookeeper.log4j-test-config' 22 | --------------------------------------------------------------------------------