pax_global_header00006660000000000000000000000064144200442360014511gustar00rootroot0000000000000052 comment=40885adbbc0f2e51a41cd9119cad305d9ac32760 curator-apache-curator-5.5.0/000077500000000000000000000000001442004423600160735ustar00rootroot00000000000000curator-apache-curator-5.5.0/.asf.yaml000066400000000000000000000023541442004423600176120ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. github: description: "Apache Curator" homepage: https://curator.apache.org/ labels: - java - database - curator - zookeeper - consensus features: wiki: false issues: false projects: false enabled_merge_buttons: squash: true merge: false rebase: true protected_branches: master: {} notifications: commits: commits@curator.apache.org pullrequests: commits@curator.apache.org jira_options: link label curator-apache-curator-5.5.0/.github/000077500000000000000000000000001442004423600174335ustar00rootroot00000000000000curator-apache-curator-5.5.0/.github/workflows/000077500000000000000000000000001442004423600214705ustar00rootroot00000000000000curator-apache-curator-5.5.0/.github/workflows/ci.yml000066400000000000000000000047361442004423600226200ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. name: CI on: push: branches: [master] pull_request: branches: [master] env: MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryHandler.class=standard -Dmaven.wagon.http.retryHandler.count=3 jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Check license header run: docker run --rm -v $(pwd):/github/workspace -u $(id -u):$(id -g) ghcr.io/korandoru/hawkeye-native:v1 check unittest: name: Unit tests runs-on: ubuntu-latest timeout-minutes: 120 strategy: fail-fast: false matrix: java: [8, 11] steps: - uses: actions/checkout@v3 - name: Cache Local Maven Repository uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v3 with: java-version: ${{ matrix.java }} distribution: 'temurin' - name: Build with ${{ matrix.java }} run: ./mvnw clean install -DskipTests - name: Test with ${{ matrix.java }} run: ./mvnw verify required: name: Required runs-on: ubuntu-latest if: ${{ always() }} needs: - check - unittest steps: - name: Guardian run: | if [[ ! ( \ "${{ needs.check.result }}" == "success" \ && "${{ needs.unittest.result }}" == "success" \ ) ]]; then echo "Required jobs haven't been completed successfully." exit -1 fi curator-apache-curator-5.5.0/.gitignore000066400000000000000000000013751442004423600200710ustar00rootroot00000000000000# Compiled source # ################### *.com *.class *.dll *.exe *.o *.so # Packages # ############ # it's better to unpack these files and commit the raw source # git has its own built in compression methods *.7z *.dmg *.gz *.iso *.jar *.rar *.tar *.zip # Logs and databases # ###################### *.log # OS generated files # ###################### .DS_Store* ehthumbs.db Icon? Thumbs.db # Editor Files # ################ *~ *.swp # Gradle Files # ################ .gradle # Build output directies /target */target /build */build */bin # IntelliJ specific files/directories out .idea *.ipr *.iws *.iml atlassian-ide-plugin.xml # Eclipse specific files/directories .classpath .project .settings .metadata # NetBeans specific files/directories .nbattrs curator-apache-curator-5.5.0/.mvn/000077500000000000000000000000001442004423600167515ustar00rootroot00000000000000curator-apache-curator-5.5.0/.mvn/wrapper/000077500000000000000000000000001442004423600204315ustar00rootroot00000000000000curator-apache-curator-5.5.0/.mvn/wrapper/maven-wrapper.properties000066400000000000000000000017731442004423600253430ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar curator-apache-curator-5.5.0/DEPENDENCIES000066400000000000000000000000351442004423600176420ustar00rootroot00000000000000* intentionally left blank * curator-apache-curator-5.5.0/LICENSE000066400000000000000000000261361442004423600171100ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. curator-apache-curator-5.5.0/NOTICE000066400000000000000000000002501442004423600167740ustar00rootroot00000000000000Apache Curator Copyright 2013-2023 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). curator-apache-curator-5.5.0/README.md000066400000000000000000000045511442004423600173570ustar00rootroot00000000000000# Apache Curator [![Maven Central](https://img.shields.io/maven-central/v/org.apache.curator/apache-curator.svg?logo=Apache+Maven&logoColor=blue)](http://search.maven.org/#search%7Cga%7C1%7Capache-curator) [![Curator Website](https://img.shields.io/badge/curator-Curator_Website-red?logo=Apache&logoColor=red)](https://curator.apache.org) [![Stack Overflow](https://img.shields.io/badge/stackoverflow-Curator_Help-orange?logo=Stack+Overflow&logoColor=orange)](https://stackoverflow.com/questions/tagged/apache-curator) [![Twitter](https://img.shields.io/badge/Follow-@Curator-55acee?logo=Twitter&logoColor=55acee)](https://twitter.com/intent/follow?original_referer=https%3A%2F%2Fgithub.com%2Fapache%2Fcurator&ref_src=twsrc%5Etfw®ion=follow_link&screen_name=ApacheCurator&tw_p=followbutton) ## What is Apache Curator? Apache Curator is a Java/JVM client library for [Apache ZooKeeper](https://zookeeper.apache.org/), a distributed coordination service. Apache Curator includes a high-level API framework and utilities to make using Apache ZooKeeper much easier and more reliable. It also includes recipes for common use cases and extensions such as service discovery and a Java 8 asynchronous DSL. For more details: - Apache Curator Website: http://curator.apache.org/ - Binaries on Maven Central: [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Capache-curator) - Best source of help for Curator: [Stack Overflow's Curator tag](https://stackoverflow.com/questions/tagged/apache-curator) - Apache ZooKeeper Website: https://zookeeper.apache.org - Curator's Wiki: http://cwiki.apache.org/confluence/display/CURATOR curator-apache-curator-5.5.0/curator-client/000077500000000000000000000000001442004423600210265ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/LICENSE000066400000000000000000000261361442004423600220430ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. curator-apache-curator-5.5.0/curator-client/NOTICE000066400000000000000000000002501442004423600217270ustar00rootroot00000000000000Apache Curator Copyright 2013-2023 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). curator-apache-curator-5.5.0/curator-client/pom.xml000066400000000000000000000120371442004423600223460ustar00rootroot00000000000000 4.0.0 org.apache.curator apache-curator 5.5.0 curator-client 5.5.0 bundle Curator Client Low-level API 2011 org.apache.zookeeper.*;version="[3.4,4.0)", * org.apache.curator.*;version="${project.version}";-noimport:=true org.apache.zookeeper zookeeper com.google.guava guava org.slf4j slf4j-api org.mockito mockito-core test org.apache.curator curator-test test org.junit.jupiter junit-jupiter-api test org.slf4j slf4j-log4j12 test org.awaitility awaitility test org.assertj assertj-core test org.apache.maven.plugins maven-shade-plugin apache-curator-guava-shader shade package com.google.guava:guava com.google.guava:listenablefuture com.google.guava:failureaccess com.google.guava:guava META-INF/** org.apache.maven.plugins maven-jar-plugin test-jar curator-apache-curator-5.5.0/curator-client/src/000077500000000000000000000000001442004423600216155ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/000077500000000000000000000000001442004423600225415ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/000077500000000000000000000000001442004423600234625ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/000077500000000000000000000000001442004423600242515ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/000077500000000000000000000000001442004423600254725ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/000077500000000000000000000000001442004423600271515ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/ConnectionState.java000066400000000000000000000240121442004423600331130ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.curator.drivers.EventTrace; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.drivers.TracerDriver; import org.apache.curator.ensemble.EnsembleProvider; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.DebugUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZookeeperFactory; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; class ConnectionState implements Watcher, Closeable { private static final int MAX_BACKGROUND_EXCEPTIONS = 10; private static final boolean LOG_EVENTS = Boolean.getBoolean(DebugUtils.PROPERTY_LOG_EVENTS); private static final Logger log = LoggerFactory.getLogger(ConnectionState.class); private final HandleHolder handleHolder; private final AtomicBoolean isConnected = new AtomicBoolean(false); private final AtomicInteger lastNegotiatedSessionTimeoutMs = new AtomicInteger(0); private final EnsembleProvider ensembleProvider; private final AtomicReference tracer; private final Queue backgroundExceptions = new ConcurrentLinkedQueue(); private final Queue parentWatchers = new ConcurrentLinkedQueue(); private final AtomicLong instanceIndex = new AtomicLong(); private volatile long connectionStartMs = 0; ConnectionState(ZookeeperFactory zookeeperFactory, EnsembleProvider ensembleProvider, int sessionTimeoutMs, Watcher parentWatcher, AtomicReference tracer, boolean canBeReadOnly) { this.ensembleProvider = ensembleProvider; this.tracer = tracer; if ( parentWatcher != null ) { parentWatchers.offer(parentWatcher); } handleHolder = new HandleHolder(zookeeperFactory, this, ensembleProvider, sessionTimeoutMs, canBeReadOnly); } ZooKeeper getZooKeeper() throws Exception { if ( SessionFailRetryLoop.sessionForThreadHasFailed() ) { throw new SessionFailRetryLoop.SessionFailedException(); } Exception exception = backgroundExceptions.poll(); if ( exception != null ) { new EventTrace("background-exceptions", tracer.get()).commit(); throw exception; } boolean localIsConnected = isConnected.get(); if ( !localIsConnected ) { checkNewConnectionString(); } return handleHolder.getZooKeeper(); } boolean isConnected() { return isConnected.get(); } void start() throws Exception { log.debug("Starting"); ensembleProvider.start(); reset(); } @Override public void close() throws IOException { close(0); } public void close(int waitForShutdownTimeoutMs) throws IOException { log.debug("Closing"); CloseableUtils.closeQuietly(ensembleProvider); try { handleHolder.closeAndClear(waitForShutdownTimeoutMs); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new IOException(e); } finally { isConnected.set(false); } } void addParentWatcher(Watcher watcher) { parentWatchers.offer(watcher); } void removeParentWatcher(Watcher watcher) { parentWatchers.remove(watcher); } long getInstanceIndex() { return instanceIndex.get(); } int getLastNegotiatedSessionTimeoutMs() { return lastNegotiatedSessionTimeoutMs.get(); } @Override public void process(WatchedEvent event) { if ( LOG_EVENTS ) { log.debug("ConnectState watcher: " + event); } if ( event.getType() == Watcher.Event.EventType.None ) { boolean wasConnected = isConnected.get(); boolean newIsConnected = checkState(event.getState(), wasConnected); if ( newIsConnected != wasConnected ) { isConnected.set(newIsConnected); connectionStartMs = System.currentTimeMillis(); if ( newIsConnected ) { lastNegotiatedSessionTimeoutMs.set(handleHolder.getNegotiatedSessionTimeoutMs()); log.debug("Negotiated session timeout: " + lastNegotiatedSessionTimeoutMs.get()); } } } for ( Watcher parentWatcher : parentWatchers ) { OperationTrace trace = new OperationTrace("connection-state-parent-process", tracer.get(), getSessionId()); parentWatcher.process(event); trace.commit(); } } EnsembleProvider getEnsembleProvider() { return ensembleProvider; } synchronized void reset() throws Exception { log.debug("reset"); instanceIndex.incrementAndGet(); isConnected.set(false); connectionStartMs = System.currentTimeMillis(); handleHolder.closeAndReset(); handleHolder.getZooKeeper(); // initiate connection } private synchronized void checkNewConnectionString() { final String newConnectionString = handleHolder.getNewConnectionString(); if (newConnectionString != null) { handleNewConnectionString(newConnectionString); } } /** * Return the current session id */ public long getSessionId() { long sessionId = 0; try { ZooKeeper zk = handleHolder.getZooKeeper(); if (zk != null) { sessionId = zk.getSessionId(); } } catch (Exception e) { // Ignore the exception } return sessionId; } private boolean checkState(Event.KeeperState state, boolean wasConnected) { boolean isConnected = wasConnected; boolean checkNewConnectionString = true; switch ( state ) { default: case Disconnected: { isConnected = false; break; } case SyncConnected: case ConnectedReadOnly: { isConnected = true; break; } case AuthFailed: { isConnected = false; log.error("Authentication failed"); break; } case Expired: { isConnected = false; checkNewConnectionString = false; handleExpiredSession(); break; } case SaslAuthenticated: { // NOP break; } } // the session expired is logged in handleExpiredSession, so not log here if (state != Event.KeeperState.Expired) { new EventTrace(state.toString(), tracer.get(), getSessionId()).commit(); } if ( checkNewConnectionString ) { String newConnectionString = handleHolder.getNewConnectionString(); if ( newConnectionString != null ) { handleNewConnectionString(newConnectionString); } } return isConnected; } private void handleNewConnectionString(String newConnectionString) { log.info("Connection string changed to: " + newConnectionString); new EventTrace("connection-string-changed", tracer.get(), getSessionId()).commit(); try { ZooKeeper zooKeeper = handleHolder.getZooKeeper(); if ( zooKeeper == null ) { log.warn("Could not update the connection string because getZooKeeper() returned null."); } else { if ( ensembleProvider.updateServerListEnabled() ) { zooKeeper.updateServerList(newConnectionString); handleHolder.resetConnectionString(newConnectionString); } else { reset(); } } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); queueBackgroundException(e); } } private void handleExpiredSession() { log.warn("Session expired event received"); new EventTrace("session-expired", tracer.get(), getSessionId()).commit(); try { reset(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); queueBackgroundException(e); } } @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) private void queueBackgroundException(Exception e) { while ( backgroundExceptions.size() >= MAX_BACKGROUND_EXCEPTIONS ) { backgroundExceptions.poll(); } backgroundExceptions.offer(e); } } CuratorConnectionLossException.java000066400000000000000000000020071442004423600361130ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.zookeeper.KeeperException; public class CuratorConnectionLossException extends KeeperException.ConnectionLossException { private static final long serialVersionUID = 1L; } CuratorZookeeperClient.java000066400000000000000000000324341442004423600344050ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import com.google.common.base.Preconditions; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.drivers.TracerDriver; import org.apache.curator.ensemble.EnsembleProvider; import org.apache.curator.ensemble.fixed.FixedEnsembleProvider; import org.apache.curator.utils.DefaultTracerDriver; import org.apache.curator.utils.DefaultZookeeperFactory; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZookeeperFactory; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; /** * A wrapper around Zookeeper that takes care of some low-level housekeeping */ @SuppressWarnings("UnusedDeclaration") public class CuratorZookeeperClient implements Closeable { private final Logger log = LoggerFactory.getLogger(getClass()); private final ConnectionState state; private final AtomicReference retryPolicy = new AtomicReference(); private final int connectionTimeoutMs; private final int waitForShutdownTimeoutMs; private final AtomicBoolean started = new AtomicBoolean(false); private final AtomicReference tracer = new AtomicReference(new DefaultTracerDriver()); /** * * @param connectString list of servers to connect to * @param sessionTimeoutMs session timeout * @param connectionTimeoutMs connection timeout * @param watcher default watcher or null * @param retryPolicy the retry policy to use */ public CuratorZookeeperClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, Watcher watcher, RetryPolicy retryPolicy) { this(new DefaultZookeeperFactory(), new FixedEnsembleProvider(connectString), sessionTimeoutMs, connectionTimeoutMs, watcher, retryPolicy, false); } /** * @param ensembleProvider the ensemble provider * @param sessionTimeoutMs session timeout * @param connectionTimeoutMs connection timeout * @param watcher default watcher or null * @param retryPolicy the retry policy to use */ public CuratorZookeeperClient(EnsembleProvider ensembleProvider, int sessionTimeoutMs, int connectionTimeoutMs, Watcher watcher, RetryPolicy retryPolicy) { this(new DefaultZookeeperFactory(), ensembleProvider, sessionTimeoutMs, connectionTimeoutMs, watcher, retryPolicy, false); } /** * @param zookeeperFactory factory for creating {@link ZooKeeper} instances * @param ensembleProvider the ensemble provider * @param sessionTimeoutMs session timeout * @param connectionTimeoutMs connection timeout * @param watcher default watcher or null * @param retryPolicy the retry policy to use * @param canBeReadOnly if true, allow ZooKeeper client to enter * read only mode in case of a network partition. See * {@link ZooKeeper#ZooKeeper(String, int, Watcher, long, byte[], boolean)} * for details */ public CuratorZookeeperClient(ZookeeperFactory zookeeperFactory, EnsembleProvider ensembleProvider, int sessionTimeoutMs, int connectionTimeoutMs, Watcher watcher, RetryPolicy retryPolicy, boolean canBeReadOnly) { this(zookeeperFactory, ensembleProvider, sessionTimeoutMs, connectionTimeoutMs, 0, watcher, retryPolicy, canBeReadOnly); } /** * @param zookeeperFactory factory for creating {@link ZooKeeper} instances * @param ensembleProvider the ensemble provider * @param sessionTimeoutMs session timeout * @param connectionTimeoutMs connection timeout * @param waitForShutdownTimeoutMs default timeout fo close operation * @param watcher default watcher or null * @param retryPolicy the retry policy to use * @param canBeReadOnly if true, allow ZooKeeper client to enter * read only mode in case of a network partition. See * {@link ZooKeeper#ZooKeeper(String, int, Watcher, long, byte[], boolean)} * for details * @since 4.0.2 */ public CuratorZookeeperClient(ZookeeperFactory zookeeperFactory, EnsembleProvider ensembleProvider, int sessionTimeoutMs, int connectionTimeoutMs, int waitForShutdownTimeoutMs, Watcher watcher, RetryPolicy retryPolicy, boolean canBeReadOnly) { if ( sessionTimeoutMs < connectionTimeoutMs ) { log.warn(String.format("session timeout [%d] is less than connection timeout [%d]", sessionTimeoutMs, connectionTimeoutMs)); } retryPolicy = Preconditions.checkNotNull(retryPolicy, "retryPolicy cannot be null"); ensembleProvider = Preconditions.checkNotNull(ensembleProvider, "ensembleProvider cannot be null"); this.connectionTimeoutMs = connectionTimeoutMs; this.waitForShutdownTimeoutMs = waitForShutdownTimeoutMs; state = new ConnectionState(zookeeperFactory, ensembleProvider, sessionTimeoutMs, watcher, tracer, canBeReadOnly); setRetryPolicy(retryPolicy); } /** * Return the managed ZK instance. * * @return client the client * @throws Exception if the connection timeout has elapsed or an exception occurs in a background process */ public ZooKeeper getZooKeeper() throws Exception { Preconditions.checkState(started.get(), "Client is not started"); return state.getZooKeeper(); } /** * Return a new retry loop. All operations should be performed in a retry loop * * @return new retry loop */ public RetryLoop newRetryLoop() { return new RetryLoopImpl(retryPolicy.get(), tracer); } /** * Return a new "session fail" retry loop. See {@link SessionFailRetryLoop} for details * on when to use it. * * @param mode failure mode * @return new retry loop */ public SessionFailRetryLoop newSessionFailRetryLoop(SessionFailRetryLoop.Mode mode) { return new SessionFailRetryLoop(this, mode); } /** * Returns true if the client is current connected * * @return true/false */ public boolean isConnected() { return state.isConnected(); } /** * This method blocks until the connection to ZK succeeds. Use with caution. The block * will timeout after the connection timeout (as passed to the constructor) has elapsed * * @return true if the connection succeeded, false if not * @throws InterruptedException interrupted while waiting */ public boolean blockUntilConnectedOrTimedOut() throws InterruptedException { Preconditions.checkState(started.get(), "Client is not started"); log.debug("blockUntilConnectedOrTimedOut() start"); OperationTrace trace = startAdvancedTracer("blockUntilConnectedOrTimedOut"); internalBlockUntilConnectedOrTimedOut(); trace.commit(); boolean localIsConnected = state.isConnected(); log.debug("blockUntilConnectedOrTimedOut() end. isConnected: " + localIsConnected); return localIsConnected; } /** * Must be called after construction * * @throws IOException errors */ public void start() throws Exception { log.debug("Starting"); if ( !started.compareAndSet(false, true) ) { throw new IllegalStateException("Already started"); } state.start(); } /** * Close the client. * * Same as {@link #close(int) } using the timeout set at construction time. * * @see #close(int) */ @Override public void close() { close(waitForShutdownTimeoutMs); } /** * Close this client object as the {@link #close() } method. * This method will wait for internal resources to be released. * * @param waitForShutdownTimeoutMs timeout (in milliseconds) to wait for resources to be released. * Use zero or a negative value to skip the wait. */ public void close(int waitForShutdownTimeoutMs) { log.debug("Closing, waitForShutdownTimeoutMs {}", waitForShutdownTimeoutMs); started.set(false); try { state.close(waitForShutdownTimeoutMs); } catch ( IOException e ) { ThreadUtils.checkInterrupted(e); log.error("", e); } } /** * Change the retry policy * * @param policy new policy */ public void setRetryPolicy(RetryPolicy policy) { Preconditions.checkNotNull(policy, "policy cannot be null"); retryPolicy.set(policy); } /** * Return the current retry policy * * @return policy */ public RetryPolicy getRetryPolicy() { return retryPolicy.get(); } /** * Start a new tracer * @param name name of the event * @return the new tracer ({@link TimeTrace#commit()} must be called) */ public TimeTrace startTracer(String name) { return new TimeTrace(name, tracer.get()); } /** * Start a new advanced tracer with more metrics being recorded * @param name name of the event * @return the new tracer ({@link OperationTrace#commit()} must be called) */ public OperationTrace startAdvancedTracer(String name) { return new OperationTrace(name, tracer.get(), state.getSessionId()); } /** * Return the current tracing driver * * @return tracing driver */ public TracerDriver getTracerDriver() { return tracer.get(); } /** * Change the tracing driver * * @param tracer new tracing driver */ public void setTracerDriver(TracerDriver tracer) { this.tracer.set(tracer); } /** * Returns the current known connection string - not guaranteed to be correct * value at any point in the future. * * @return connection string */ public String getCurrentConnectionString() { return state.getEnsembleProvider().getConnectionString(); } /** * Return the configured connection timeout * * @return timeout */ public int getConnectionTimeoutMs() { return connectionTimeoutMs; } /** * For internal use only - reset the internally managed ZK handle * * @throws Exception errors */ public void reset() throws Exception { state.reset(); } /** * Every time a new {@link ZooKeeper} instance is allocated, the "instance index" * is incremented. * * @return the current instance index */ public long getInstanceIndex() { return state.getInstanceIndex(); } /** * Return the most recent value of {@link ZooKeeper#getSessionTimeout()} or 0 * * @return session timeout or 0 */ public int getLastNegotiatedSessionTimeoutMs() { return state.getLastNegotiatedSessionTimeoutMs(); } void addParentWatcher(Watcher watcher) { state.addParentWatcher(watcher); } void removeParentWatcher(Watcher watcher) { state.removeParentWatcher(watcher); } /** * For internal use only * * @throws InterruptedException interruptions */ public void internalBlockUntilConnectedOrTimedOut() throws InterruptedException { long waitTimeMs = connectionTimeoutMs; while ( !state.isConnected() && (waitTimeMs > 0) ) { final CountDownLatch latch = new CountDownLatch(1); Watcher tempWatcher = new Watcher() { @Override public void process(WatchedEvent event) { latch.countDown(); } }; state.addParentWatcher(tempWatcher); long startTimeMs = System.currentTimeMillis(); long timeoutMs = Math.min(waitTimeMs, 1000); try { latch.await(timeoutMs, TimeUnit.MILLISECONDS); } finally { state.removeParentWatcher(tempWatcher); } long elapsed = Math.max(1, System.currentTimeMillis() - startTimeMs); waitTimeMs -= elapsed; } } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/HandleHolder.java000066400000000000000000000115541442004423600323530ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.curator.ensemble.EnsembleProvider; import org.apache.curator.utils.ZookeeperFactory; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; class HandleHolder { private final ZookeeperFactory zookeeperFactory; private final Watcher watcher; private final EnsembleProvider ensembleProvider; private final int sessionTimeout; private final boolean canBeReadOnly; private volatile Helper helper; HandleHolder(ZookeeperFactory zookeeperFactory, Watcher watcher, EnsembleProvider ensembleProvider, int sessionTimeout, boolean canBeReadOnly) { this.zookeeperFactory = zookeeperFactory; this.watcher = watcher; this.ensembleProvider = ensembleProvider; this.sessionTimeout = sessionTimeout; this.canBeReadOnly = canBeReadOnly; } ZooKeeper getZooKeeper() throws Exception { return (helper != null) ? helper.getZooKeeper() : null; } int getNegotiatedSessionTimeoutMs() { return (helper != null) ? helper.getNegotiatedSessionTimeoutMs() : 0; } String getConnectionString() { return (helper != null) ? helper.getConnectionString() : null; } String getNewConnectionString() { String helperConnectionString = (helper != null) ? helper.getConnectionString() : null; String ensembleProviderConnectionString = ensembleProvider.getConnectionString(); return ((helperConnectionString != null) && !ensembleProviderConnectionString.equals(helperConnectionString)) ? ensembleProviderConnectionString : null; } void resetConnectionString(String connectionString) { if ( helper != null ) { helper.resetConnectionString(connectionString); } } void closeAndClear(int waitForShutdownTimeoutMs) throws Exception { internalClose(waitForShutdownTimeoutMs); helper = null; } void closeAndReset() throws Exception { internalClose(0); Helper.Data data = new Helper.Data(); // data shared between initial Helper and the un-synchronized Helper // first helper is synchronized when getZooKeeper is called. Subsequent calls // are not synchronized. //noinspection NonAtomicOperationOnVolatileField helper = new Helper(data) { @Override ZooKeeper getZooKeeper() throws Exception { synchronized(this) { if ( data.zooKeeperHandle == null ) { resetConnectionString(ensembleProvider.getConnectionString()); data.zooKeeperHandle = zookeeperFactory.newZooKeeper(data.connectionString, sessionTimeout, watcher, canBeReadOnly); } helper = new Helper(data); return super.getZooKeeper(); } } }; } private void internalClose(int waitForShutdownTimeoutMs) throws Exception { try { ZooKeeper zooKeeper = (helper != null) ? helper.getZooKeeper() : null; if ( zooKeeper != null ) { Watcher dummyWatcher = new Watcher() { @Override public void process(WatchedEvent event) { } }; zooKeeper.register(dummyWatcher); // clear the default watcher so that no new events get processed by mistake if ( waitForShutdownTimeoutMs == 0 ) { zooKeeper.close(); // coming from closeAndReset() which is executed in ZK's event thread. Cannot use zooKeeper.close(n) otherwise we'd get a dead lock } else { zooKeeper.close(waitForShutdownTimeoutMs); } } } catch ( InterruptedException dummy ) { Thread.currentThread().interrupt(); } } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/Helper.java000066400000000000000000000030361442004423600312350ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.zookeeper.ZooKeeper; class Helper { private final Data data; static class Data { volatile ZooKeeper zooKeeperHandle = null; volatile String connectionString = null; } Helper(Data data) { this.data = data; } ZooKeeper getZooKeeper() throws Exception { return data.zooKeeperHandle; } String getConnectionString() { return data.connectionString; } int getNegotiatedSessionTimeoutMs() { return (data.zooKeeperHandle != null) ? data.zooKeeperHandle.getSessionTimeout() : 0; } void resetConnectionString(String connectionString) { data.connectionString = connectionString; } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/RetryLoop.java000066400000000000000000000076771442004423600317740ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.curator.connection.ThreadLocalRetryLoop; import org.apache.curator.utils.ThreadUtils; import java.util.concurrent.Callable; /** *

Mechanism to perform an operation on Zookeeper that is safe against * disconnections and "recoverable" errors.

* *

* If an exception occurs during the operation, the RetryLoop will process it, * check with the current retry policy and either attempt to reconnect or re-throw * the exception *

* * Canonical usage:
*
 * RetryLoop retryLoop = client.newRetryLoop();
 * while ( retryLoop.shouldContinue() )
 * {
 *     try
 *     {
 *         // do your work
 *         ZooKeeper      zk = client.getZooKeeper();    // it's important to re-get the ZK instance in case there was an error and the instance was re-created
 *
 *         retryLoop.markComplete();
 *     }
 *     catch ( Exception e )
 *     {
 *         retryLoop.takeException(e);
 *     }
 * }
 * 
* *

* Note: this an {@code abstract class} instead of an {@code interface} for historical reasons. It was originally a class * and if it becomes an interface we risk {@link java.lang.IncompatibleClassChangeError}s with clients. *

*/ public abstract class RetryLoop { /** * Returns the default retry sleeper * * @return sleeper */ public static RetrySleeper getDefaultRetrySleeper() { return RetryLoopImpl.getRetrySleeper(); } /** * Convenience utility: creates a retry loop calling the given proc and retrying if needed * * @param client Zookeeper * @param proc procedure to call with retry * @param return type * @return procedure result * @throws Exception any non-retriable errors */ public static T callWithRetry(CuratorZookeeperClient client, Callable proc) throws Exception { client.internalBlockUntilConnectedOrTimedOut(); T result = null; ThreadLocalRetryLoop threadLocalRetryLoop = new ThreadLocalRetryLoop(); RetryLoop retryLoop = threadLocalRetryLoop.getRetryLoop(client::newRetryLoop); try { while ( retryLoop.shouldContinue() ) { try { result = proc.call(); retryLoop.markComplete(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); retryLoop.takeException(e); } } } finally { threadLocalRetryLoop.release(); } return result; } /** * If true is returned, make an attempt at the operation * * @return true/false */ public abstract boolean shouldContinue(); /** * Call this when your operation has successfully completed */ public abstract void markComplete(); /** * Pass any caught exceptions here * * @param exception the exception * @throws Exception if not retry-able or the retry policy returned negative */ public abstract void takeException(Exception exception) throws Exception; } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/RetryLoopImpl.java000066400000000000000000000060551442004423600326030ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.curator.drivers.EventTrace; import org.apache.curator.drivers.TracerDriver; import org.apache.curator.utils.DebugUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.atomic.AtomicReference; class RetryLoopImpl extends RetryLoop { private boolean isDone = false; private int retryCount = 0; private final Logger log = LoggerFactory.getLogger(getClass()); private final long startTimeMs = System.currentTimeMillis(); private final RetryPolicy retryPolicy; private final AtomicReference tracer; private static final RetrySleeper sleeper = (time, unit) -> unit.sleep(time); RetryLoopImpl(RetryPolicy retryPolicy, AtomicReference tracer) { this.retryPolicy = retryPolicy; this.tracer = tracer; } static RetrySleeper getRetrySleeper() { return sleeper; } @Override public boolean shouldContinue() { return !isDone; } @Override public void markComplete() { isDone = true; } @Override public void takeException(Exception exception) throws Exception { boolean rethrow = true; if ( retryPolicy.allowRetry(exception) ) { if ( !Boolean.getBoolean(DebugUtils.PROPERTY_DONT_LOG_CONNECTION_ISSUES) ) { log.debug("Retry-able exception received", exception); } if ( retryPolicy.allowRetry(retryCount++, System.currentTimeMillis() - startTimeMs, sleeper) ) { new EventTrace("retries-allowed", tracer.get()).commit(); if ( !Boolean.getBoolean(DebugUtils.PROPERTY_DONT_LOG_CONNECTION_ISSUES) ) { log.debug("Retrying operation"); } rethrow = false; } else { new EventTrace("retries-disallowed", tracer.get()).commit(); if ( !Boolean.getBoolean(DebugUtils.PROPERTY_DONT_LOG_CONNECTION_ISSUES) ) { log.debug("Retry policy not allowing retry"); } } } if ( rethrow ) { throw exception; } } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/RetryPolicy.java000066400000000000000000000043241442004423600323040ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.zookeeper.KeeperException; /** * Abstracts the policy to use when retrying connections */ public interface RetryPolicy { /** * Called when an operation has failed for some reason. This method should return * true to make another attempt. * * * @param retryCount the number of times retried so far (0 the first time) * @param elapsedTimeMs the elapsed time in ms since the operation was attempted * @param sleeper use this to sleep - DO NOT call Thread.sleep * @return true/false */ boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper); /** * Called when an operation has failed with a specific exception. This method * should return true to make another attempt. * * @param exception the cause that this operation failed * @return true/false */ default boolean allowRetry(Throwable exception) { if ( exception instanceof KeeperException) { final int rc = ((KeeperException) exception).code().intValue(); return (rc == KeeperException.Code.CONNECTIONLOSS.intValue()) || (rc == KeeperException.Code.OPERATIONTIMEOUT.intValue()) || (rc == KeeperException.Code.SESSIONMOVED.intValue()) || (rc == KeeperException.Code.SESSIONEXPIRED.intValue()); } return false; } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/RetrySleeper.java000066400000000000000000000023021442004423600324360ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import java.util.concurrent.TimeUnit; /** * Abstraction for retry policies to sleep */ public interface RetrySleeper { /** * Sleep for the given time * * @param time time * @param unit time unit * @throws InterruptedException if the sleep is interrupted */ public void sleepFor(long time, TimeUnit unit) throws InterruptedException; } SessionFailRetryLoop.java000066400000000000000000000200441442004423600340340ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.apache.curator.utils.ThreadUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import java.io.Closeable; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; /** *

* See {@link RetryLoop} for the main details on retry loops. All Curator/ZooKeeper operations * should be done in a retry loop. *

* *

* The standard retry loop treats session failure as a type of connection failure. i.e. the fact * that it is a session failure isn't considered. This can be problematic if you are performing * a series of operations that rely on ephemeral nodes. If the session fails after the ephemeral * node has been created, future Curator/ZooKeeper operations may succeed even though the * ephemeral node has been removed by ZooKeeper. *

* *

* Here's an example: *

*
    *
  • You create an ephemeral/sequential node as a kind of lock/marker
  • *
  • You perform some other operations
  • *
  • The session fails for some reason
  • *
  • You attempt to create a node assuming that the lock/marker still exists *
      *
    • Curator will notice the session failure and try to reconnect
    • *
    • In most cases, the reconnect will succeed and, thus, the node creation will succeed * even though the ephemeral node will have been deleted by ZooKeeper.
    • *
    *
  • *
* *

* The SessionFailRetryLoop prevents this type of scenario. When a session failure is detected, * the thread is marked as failed which will cause all future Curator operations to fail. The * SessionFailRetryLoop will then either retry the entire * set of operations or fail (depending on {@link SessionFailRetryLoop.Mode}) *

* * Canonical usage:
*
 * SessionFailRetryLoop    retryLoop = client.newSessionFailRetryLoop(mode);
 * retryLoop.start();
 * try
 * {
 *     while ( retryLoop.shouldContinue() )
 *     {
 *         try
 *         {
 *             // do work
 *         }
 *         catch ( Exception e )
 *         {
 *             retryLoop.takeException(e);
 *         }
 *     }
 * }
 * finally
 * {
 *     retryLoop.close();
 * }
 * 
*/ public class SessionFailRetryLoop implements Closeable { private final CuratorZookeeperClient client; private final Mode mode; private final Thread ourThread = Thread.currentThread(); private final AtomicBoolean sessionHasFailed = new AtomicBoolean(false); private final AtomicBoolean isDone = new AtomicBoolean(false); private final RetryLoop retryLoop; private final Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if ( event.getState() == Event.KeeperState.Expired ) { sessionHasFailed.set(true); failedSessionThreads.add(ourThread); } } }; private static final Set failedSessionThreads = Sets.newSetFromMap(Maps.newConcurrentMap()); public static class SessionFailedException extends Exception { private static final long serialVersionUID = 1L; } public enum Mode { /** * If the session fails, retry the entire set of operations when {@link SessionFailRetryLoop#shouldContinue()} * is called */ RETRY, /** * If the session fails, throw {@link KeeperException.SessionExpiredException} when * {@link SessionFailRetryLoop#shouldContinue()} is called */ FAIL } /** * Convenience utility: creates a "session fail" retry loop calling the given proc * * @param client Zookeeper * @param mode how to handle session failures * @param proc procedure to call with retry * @param return type * @return procedure result * @throws Exception any non-retriable errors */ public static T callWithRetry(CuratorZookeeperClient client, Mode mode, Callable proc) throws Exception { T result = null; SessionFailRetryLoop retryLoop = client.newSessionFailRetryLoop(mode); retryLoop.start(); try { while ( retryLoop.shouldContinue() ) { try { result = proc.call(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); retryLoop.takeException(e); } } } finally { retryLoop.close(); } return result; } SessionFailRetryLoop(CuratorZookeeperClient client, Mode mode) { this.client = client; this.mode = mode; retryLoop = client.newRetryLoop(); } static boolean sessionForThreadHasFailed() { return failedSessionThreads.contains(Thread.currentThread()); } /** * SessionFailRetryLoop must be started */ public void start() { Preconditions.checkState(Thread.currentThread().equals(ourThread), "Not in the correct thread"); client.addParentWatcher(watcher); } /** * If true is returned, make an attempt at the set of operations * * @return true/false */ public boolean shouldContinue() { boolean localIsDone = isDone.getAndSet(true); return !localIsDone; } /** * Must be called in a finally handler when done with the loop */ @Override public void close() { Preconditions.checkState(Thread.currentThread().equals(ourThread), "Not in the correct thread"); failedSessionThreads.remove(ourThread); client.removeParentWatcher(watcher); } /** * Pass any caught exceptions here * * @param exception the exception * @throws Exception if not retry-able or the retry policy returned negative */ public void takeException(Exception exception) throws Exception { Preconditions.checkState(Thread.currentThread().equals(ourThread), "Not in the correct thread"); boolean passUp = true; if ( sessionHasFailed.get() ) { switch ( mode ) { case RETRY: { sessionHasFailed.set(false); failedSessionThreads.remove(ourThread); if ( exception instanceof SessionFailedException ) { isDone.set(false); passUp = false; } break; } case FAIL: { break; } } } if ( passUp ) { retryLoop.takeException(exception); } } } SessionFailedRetryPolicy.java000066400000000000000000000032201442004423600346700ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.zookeeper.KeeperException; /** * {@link RetryPolicy} implementation that failed on session expired. */ public class SessionFailedRetryPolicy implements RetryPolicy { private final RetryPolicy delegatePolicy; public SessionFailedRetryPolicy(RetryPolicy delegatePolicy) { this.delegatePolicy = delegatePolicy; } @Override public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper) { return delegatePolicy.allowRetry(retryCount, elapsedTimeMs, sleeper); } @Override public boolean allowRetry(Throwable exception) { if ( exception instanceof KeeperException.SessionExpiredException ) { return false; } else { return delegatePolicy.allowRetry(exception); } } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/TimeTrace.java000066400000000000000000000030711442004423600316720ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.curator.drivers.TracerDriver; import java.util.concurrent.TimeUnit; /** * Utility to time a method or portion of code */ public class TimeTrace { private final String name; private final TracerDriver driver; private final long startTimeNanos = System.nanoTime(); /** * Create and start a timer * * @param name name of the event * @param driver driver */ public TimeTrace(String name, TracerDriver driver) { this.name = name; this.driver = driver; } /** * Record the elapsed time */ public void commit() { long elapsed = System.nanoTime() - startTimeNanos; driver.addTrace(name, elapsed, TimeUnit.NANOSECONDS); } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/connection/000077500000000000000000000000001442004423600313105ustar00rootroot00000000000000ThreadLocalRetryLoop.java000066400000000000000000000112531442004423600361400ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/connection/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.connection; import org.apache.curator.RetryLoop; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Objects; import java.util.function.Supplier; /** *

* Retry loops can easily end up getting nested which can cause exponential calls of the retry policy * (see https://issues.apache.org/jira/browse/CURATOR-559). This utility works around that by using * an internal ThreadLocal to hold a retry loop. E.g. if the retry loop fails anywhere in the chain * of nested calls it will fail for the rest of the nested calls instead. *

* *

* Example usage: * *

 * ThreadLocalRetryLoop threadLocalRetryLoop = new ThreadLocalRetryLoop();
 * RetryLoop retryLoop = threadLocalRetryLoop.getRetryLoop(client::newRetryLoop);
 * try
 * {
 *     while ( retryLoop.shouldContinue() )
 *     {
 *         try
 *         {
 *             // do work
 *             retryLoop.markComplete();
 *         }
 *         catch ( Exception e )
 *         {
 *             ThreadUtils.checkInterrupted(e);
 *             retryLoop.takeException(e);
 *         }
 *     }
 * }
 * finally
 * {
 *     threadLocalRetryLoop.release();
 * }
 * 
*

*/ public class ThreadLocalRetryLoop { private static final Logger log = LoggerFactory.getLogger(ThreadLocalRetryLoop.class); private static final ThreadLocal threadLocal = new ThreadLocal<>(); private static class Entry { private final RetryLoop retryLoop; private int counter; Entry(RetryLoop retryLoop) { this.retryLoop = retryLoop; } } private static class WrappedRetryLoop extends RetryLoop { private final RetryLoop retryLoop; private Exception takenException; public WrappedRetryLoop(RetryLoop retryLoop) { this.retryLoop = retryLoop; } @Override public boolean shouldContinue() { return retryLoop.shouldContinue() && (takenException == null); } @Override public void markComplete() { retryLoop.markComplete(); } @Override public void takeException(Exception exception) throws Exception { if ( takenException != null ) { if ( exception.getClass() != takenException.getClass() ) { log.error("Multiple exceptions in retry loop", exception); } throw takenException; } try { retryLoop.takeException(exception); } catch ( Exception e ) { takenException = e; throw e; } } } /** * Call to get the current retry loop. If there isn't one, one is allocated * via {@code newRetryLoopSupplier}. * * @param newRetryLoopSupplier supply a new retry loop when needed. Normally you should use {@link org.apache.curator.CuratorZookeeperClient#newRetryLoop()} * @return retry loop to use */ public RetryLoop getRetryLoop(Supplier newRetryLoopSupplier) { Entry entry = threadLocal.get(); if ( entry == null ) { entry = new Entry(new WrappedRetryLoop(newRetryLoopSupplier.get())); threadLocal.set(entry); } ++entry.counter; return entry.retryLoop; } /** * Must be called to release the retry loop. See {@link RetryLoop#callWithRetry(org.apache.curator.CuratorZookeeperClient, java.util.concurrent.Callable)} * for an example usage. */ public void release() { Entry entry = Objects.requireNonNull(threadLocal.get(), "No retry loop was set - unbalanced call to release()"); if ( --entry.counter <= 0 ) { threadLocal.remove(); } } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/drivers/000077500000000000000000000000001442004423600306275ustar00rootroot00000000000000AdvancedTracerDriver.java000066400000000000000000000030621442004423600354360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/drivers/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.drivers; import java.util.concurrent.TimeUnit; /** * Expose more metrics for the operations and events */ public abstract class AdvancedTracerDriver implements TracerDriver { /** * Record the given trace event * * @param trace the metrics of the operation */ public abstract void addTrace(OperationTrace trace); /** * Add to a named counter * * @param name name of the counter * @param increment amount to increment */ public abstract void addEvent(EventTrace trace); @Deprecated @Override public final void addTrace(String name, long time, TimeUnit unit) {} @Deprecated @Override public final void addCount(String name, int increment) {} } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/drivers/EventTrace.java000066400000000000000000000030061442004423600335310ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.drivers; public class EventTrace { private final String name; private final TracerDriver driver; private final long sessionId; public EventTrace(String name, TracerDriver driver) { this(name, driver, -1); } public EventTrace(String name, TracerDriver driver, long sessionId) { this.name = name; this.driver = driver; this.sessionId = sessionId; } public String getName() { return this.name; } public long getSessionId() { return this.sessionId; } public void commit() { if (this.driver instanceof AdvancedTracerDriver) { ((AdvancedTracerDriver) this.driver).addEvent(this); } else { this.driver.addCount(this.name, 1); } } } OperationTrace.java000066400000000000000000000102361442004423600343340ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/drivers/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.drivers; import org.apache.curator.drivers.TracerDriver; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; import java.io.UnsupportedEncodingException; import java.util.concurrent.TimeUnit; /** * Used to trace the metrics of a certain Zookeeper operation. */ public class OperationTrace { private final String name; private final TracerDriver driver; private int returnCode = KeeperException.Code.OK.intValue(); private long latencyMs; private long requestBytesLength; private long responseBytesLength; private String path; private boolean withWatcher; private long sessionId; private Stat stat; private final long startTimeNanos = System.nanoTime(); public OperationTrace(String name, TracerDriver driver) { this(name, driver, -1); } public OperationTrace(String name, TracerDriver driver, long sessionId) { this.name = name; this.driver = driver; this.sessionId = sessionId; } public OperationTrace setReturnCode(int returnCode) { this.returnCode = returnCode; return this; } public OperationTrace setRequestBytesLength(long length) { this.requestBytesLength = length; return this; } public OperationTrace setRequestBytesLength(String data) { if (data == null) { return this; } try { this.setRequestBytesLength(data.getBytes("UTF-8").length); } catch (UnsupportedEncodingException e) { // Ignore the exception. } return this; } public OperationTrace setRequestBytesLength(byte[] data) { if (data == null) { return this; } return this.setRequestBytesLength(data.length); } public OperationTrace setResponseBytesLength(long length) { this.responseBytesLength = length; return this; } public OperationTrace setResponseBytesLength(byte[] data) { if (data == null) { return this; } return this.setResponseBytesLength(data.length); } public OperationTrace setPath(String path) { this.path = path; return this; } public OperationTrace setWithWatcher(boolean withWatcher) { this.withWatcher = withWatcher; return this; } public OperationTrace setStat(Stat stat) { this.stat = stat; return this; } public String getName() { return this.name; } public int getReturnCode() { return this.returnCode; } public long getLatencyMs() { return this.latencyMs; } public long getRequestBytesLength() { return this.requestBytesLength; } public long getResponseBytesLength() { return this.responseBytesLength; } public long getSessionId() { return this.sessionId; } public String getPath() { return this.path; } public boolean isWithWatcher() { return this.withWatcher; } public Stat getStat() { return this.stat; } public void commit() { long elapsed = System.nanoTime() - startTimeNanos; this.latencyMs = TimeUnit.MILLISECONDS.convert(elapsed, TimeUnit.NANOSECONDS); if (this.driver instanceof AdvancedTracerDriver) { ((AdvancedTracerDriver) this.driver).addTrace(this); } else { this.driver.addTrace(this.name, elapsed, TimeUnit.NANOSECONDS); } } } TracerDriver.java000066400000000000000000000025711442004423600340140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/drivers/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.drivers; import java.util.concurrent.TimeUnit; /** * Mechanism for timing methods and recording counters */ public interface TracerDriver { /** * Record the given trace event * * @param name of the event * @param time time event took * @param unit time unit */ public void addTrace(String name, long time, TimeUnit unit); /** * Add to a named counter * * @param name name of the counter * @param increment amount to increment */ public void addCount(String name, int increment); } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/ensemble/000077500000000000000000000000001442004423600307435ustar00rootroot00000000000000EnsembleProvider.java000066400000000000000000000043031442004423600347740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/ensemble/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.ensemble; import org.apache.curator.CuratorZookeeperClient; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import java.io.Closeable; import java.io.IOException; /** * Abstraction that provides the ZooKeeper connection string */ public interface EnsembleProvider extends Closeable { /** * Curator will call this method when {@link CuratorZookeeperClient#start()} is * called * * @throws Exception errors */ public void start() throws Exception; /** * Return the current connection string to use. Curator will call this each * time it needs to create a ZooKeeper instance * * @return connection string (per {@link ZooKeeper#ZooKeeper(String, int, Watcher)} etc.) */ public String getConnectionString(); /** * Curator will call this method when {@link CuratorZookeeperClient#close()} is called * * @throws IOException errors */ public void close() throws IOException; /** * A new connection string event was received * * @param connectionString the new connection string */ public void setConnectionString(String connectionString); /** * Return true if this ensemble provider supports {@link ZooKeeper#updateServerList(String)} * * @return true/false */ public boolean updateServerListEnabled(); } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/ensemble/fixed/000077500000000000000000000000001442004423600320425ustar00rootroot00000000000000FixedEnsembleProvider.java000066400000000000000000000052111442004423600370520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/ensemble/fixed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.ensemble.fixed; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import org.apache.curator.ensemble.EnsembleProvider; import org.apache.zookeeper.ZooKeeper; import java.io.IOException; import java.util.concurrent.atomic.AtomicReference; /** * Standard ensemble provider that wraps a fixed connection string */ public class FixedEnsembleProvider implements EnsembleProvider { private final AtomicReference connectionString = new AtomicReference<>(); private final boolean updateServerListEnabled; /** * The connection string to use * * @param connectionString connection string */ public FixedEnsembleProvider(String connectionString) { this(connectionString, true); } /** * The connection string to use * * @param connectionString connection string * @param updateServerListEnabled if true, allow Curator to call {@link ZooKeeper#updateServerList(String)} */ public FixedEnsembleProvider(String connectionString, boolean updateServerListEnabled) { this.updateServerListEnabled = updateServerListEnabled; Preconditions.checkArgument(!Strings.isNullOrEmpty(connectionString), "connectionString cannot be null or empty"); this.connectionString.set(connectionString); } @Override public void start() throws Exception { // NOP } @Override public void close() throws IOException { // NOP } @Override public void setConnectionString(String connectionString) { this.connectionString.set(connectionString); } @Override public String getConnectionString() { return connectionString.get(); } @Override public boolean updateServerListEnabled() { return updateServerListEnabled; } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/retry/000077500000000000000000000000001442004423600303165ustar00rootroot00000000000000BoundedExponentialBackoffRetry.java000066400000000000000000000035111442004423600371730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/retry/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.retry; import com.google.common.annotations.VisibleForTesting; /** * Retry policy that retries a set number of times with an increasing (up to a maximum bound) sleep time between retries */ public class BoundedExponentialBackoffRetry extends ExponentialBackoffRetry { private final int maxSleepTimeMs; /** * @param baseSleepTimeMs initial amount of time to wait between retries * @param maxSleepTimeMs maximum amount of time to wait between retries * @param maxRetries maximum number of times to retry */ public BoundedExponentialBackoffRetry(int baseSleepTimeMs, int maxSleepTimeMs, int maxRetries) { super(baseSleepTimeMs, maxRetries); this.maxSleepTimeMs = maxSleepTimeMs; } @VisibleForTesting public int getMaxSleepTimeMs() { return maxSleepTimeMs; } @Override protected long getSleepTimeMs(int retryCount, long elapsedTimeMs) { return Math.min(maxSleepTimeMs, super.getSleepTimeMs(retryCount, elapsedTimeMs)); } }ExponentialBackoffRetry.java000066400000000000000000000060601442004423600356740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/retry/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.retry; import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Random; /** * Retry policy that retries a set number of times with increasing sleep time between retries */ public class ExponentialBackoffRetry extends SleepingRetry { private static final Logger log = LoggerFactory.getLogger(ExponentialBackoffRetry.class); private static final int MAX_RETRIES_LIMIT = 29; private static final int DEFAULT_MAX_SLEEP_MS = Integer.MAX_VALUE; private final Random random = new Random(); private final int baseSleepTimeMs; private final int maxSleepMs; /** * @param baseSleepTimeMs initial amount of time to wait between retries * @param maxRetries max number of times to retry */ public ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries) { this(baseSleepTimeMs, maxRetries, DEFAULT_MAX_SLEEP_MS); } /** * @param baseSleepTimeMs initial amount of time to wait between retries * @param maxRetries max number of times to retry * @param maxSleepMs max time in ms to sleep on each retry */ public ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries, int maxSleepMs) { super(validateMaxRetries(maxRetries)); this.baseSleepTimeMs = baseSleepTimeMs; this.maxSleepMs = maxSleepMs; } @VisibleForTesting public int getBaseSleepTimeMs() { return baseSleepTimeMs; } @Override protected long getSleepTimeMs(int retryCount, long elapsedTimeMs) { // copied from Hadoop's RetryPolicies.java long sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1))); if ( sleepMs > maxSleepMs ) { log.warn(String.format("Sleep extension too large (%d). Pinning to %d", sleepMs, maxSleepMs)); sleepMs = maxSleepMs; } return sleepMs; } private static int validateMaxRetries(int maxRetries) { if ( maxRetries > MAX_RETRIES_LIMIT ) { log.warn(String.format("maxRetries too large (%d). Pinning to %d", maxRetries, MAX_RETRIES_LIMIT)); maxRetries = MAX_RETRIES_LIMIT; } return maxRetries; } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/retry/RetryForever.java000066400000000000000000000036001442004423600336160ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.retry; import org.apache.curator.RetryPolicy; import org.apache.curator.RetrySleeper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkArgument; /** * {@link RetryPolicy} implementation that always allowsRetry. */ public class RetryForever implements RetryPolicy { private static final Logger log = LoggerFactory.getLogger(RetryForever.class); private final int retryIntervalMs; public RetryForever(int retryIntervalMs) { checkArgument(retryIntervalMs > 0); this.retryIntervalMs = retryIntervalMs; } @Override public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper) { try { sleeper.sleepFor(retryIntervalMs, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.warn("Error occurred while sleeping", e); return false; } return true; } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/retry/RetryNTimes.java000066400000000000000000000024021442004423600334040ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.retry; /** * Retry policy that retries a max number of times */ public class RetryNTimes extends SleepingRetry { private final int sleepMsBetweenRetries; public RetryNTimes(int n, int sleepMsBetweenRetries) { super(n); this.sleepMsBetweenRetries = sleepMsBetweenRetries; } @Override protected long getSleepTimeMs(int retryCount, long elapsedTimeMs) { return sleepMsBetweenRetries; } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/retry/RetryOneTime.java000066400000000000000000000020231442004423600335440ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.retry; /** * A retry policy that retries only once */ public class RetryOneTime extends RetryNTimes { public RetryOneTime(int sleepMsBetweenRetry) { super(1, sleepMsBetweenRetry); } } RetryUntilElapsed.java000066400000000000000000000032401442004423600345200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/retry/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.retry; import org.apache.curator.RetrySleeper; /** * A retry policy that retries until a given amount of time elapses */ public class RetryUntilElapsed extends SleepingRetry { private final int maxElapsedTimeMs; private final int sleepMsBetweenRetries; public RetryUntilElapsed(int maxElapsedTimeMs, int sleepMsBetweenRetries) { super(Integer.MAX_VALUE); this.maxElapsedTimeMs = maxElapsedTimeMs; this.sleepMsBetweenRetries = sleepMsBetweenRetries; } @Override public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper) { return super.allowRetry(retryCount, elapsedTimeMs, sleeper) && (elapsedTimeMs < maxElapsedTimeMs); } @Override protected long getSleepTimeMs(int retryCount, long elapsedTimeMs) { return sleepMsBetweenRetries; } } SleepingRetry.java000066400000000000000000000033571442004423600337060ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/retry/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.retry; import org.apache.curator.RetryPolicy; import org.apache.curator.RetrySleeper; import java.util.concurrent.TimeUnit; abstract class SleepingRetry implements RetryPolicy { private final int n; protected SleepingRetry(int n) { this.n = n; } // made public for testing public int getN() { return n; } public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper) { if ( retryCount < n ) { try { sleeper.sleepFor(getSleepTimeMs(retryCount, elapsedTimeMs), TimeUnit.MILLISECONDS); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); return false; } return true; } return false; } protected abstract long getSleepTimeMs(int retryCount, long elapsedTimeMs); } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/000077500000000000000000000000001442004423600303115ustar00rootroot00000000000000CloseableExecutorService.java000066400000000000000000000135321442004423600360320ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.util.Iterator; import java.util.Set; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; /** * Decoration on an ExecutorService that tracks created futures and provides * a method to close futures created via this class */ public class CloseableExecutorService implements Closeable { private final Logger log = LoggerFactory.getLogger(CloseableExecutorService.class); private final Set> futures = Sets.newSetFromMap(Maps., Boolean>newConcurrentMap()); private final ExecutorService executorService; private final boolean shutdownOnClose; protected final AtomicBoolean isOpen = new AtomicBoolean(true); protected class InternalScheduledFutureTask implements Future { private final ScheduledFuture scheduledFuture; public InternalScheduledFutureTask(ScheduledFuture scheduledFuture) { this.scheduledFuture = scheduledFuture; futures.add(scheduledFuture); } @Override public boolean cancel(boolean mayInterruptIfRunning) { futures.remove(scheduledFuture); return scheduledFuture.cancel(mayInterruptIfRunning); } @Override public boolean isCancelled() { return scheduledFuture.isCancelled(); } @Override public boolean isDone() { return scheduledFuture.isDone(); } @Override public Void get() throws InterruptedException, ExecutionException { return null; } @Override public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return null; } } protected class InternalFutureTask extends FutureTask { private final RunnableFuture task; InternalFutureTask(RunnableFuture task) { super(task, null); this.task = task; futures.add(task); } protected void done() { futures.remove(task); } } /** * @param executorService the service to decorate */ public CloseableExecutorService(ExecutorService executorService) { this(executorService, false); } /** * @param executorService the service to decorate * @param shutdownOnClose if true, shutdown the executor service when this is closed */ public CloseableExecutorService(ExecutorService executorService, boolean shutdownOnClose) { this.executorService = Preconditions.checkNotNull(executorService, "executorService cannot be null"); this.shutdownOnClose = shutdownOnClose; } /** * Returns true if this executor has been shut down. * * @return true if this executor has been shut down */ public boolean isShutdown() { return !isOpen.get(); } @VisibleForTesting int size() { return futures.size(); } /** * Closes any tasks currently in progress */ @Override public void close() { isOpen.set(false); Iterator> iterator = futures.iterator(); while ( iterator.hasNext() ) { Future future = iterator.next(); iterator.remove(); if ( !future.isDone() && !future.isCancelled() && !future.cancel(true) ) { log.warn("Could not cancel " + future); } } if (shutdownOnClose) { executorService.shutdownNow(); } } /** * Submits a value-returning task for execution and returns a Future * representing the pending results of the task. Upon completion, * this task may be taken or polled. * * @param task the task to submit * @return a future to watch the task */ public Future submit(Callable task) { Preconditions.checkState(isOpen.get(), "CloseableExecutorService is closed"); InternalFutureTask futureTask = new InternalFutureTask(new FutureTask(task)); executorService.execute(futureTask); return futureTask; } /** * Submits a Runnable task for execution and returns a Future * representing that task. Upon completion, this task may be * taken or polled. * * @param task the task to submit * @return a future to watch the task */ public Future submit(Runnable task) { Preconditions.checkState(isOpen.get(), "CloseableExecutorService is closed"); InternalFutureTask futureTask = new InternalFutureTask(new FutureTask(task, null)); executorService.execute(futureTask); return futureTask; } } CloseableScheduledExecutorService.java000066400000000000000000000103301442004423600376440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import com.google.common.base.Preconditions; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** * Decoration on an ScheduledExecutorService that tracks created futures and provides * a method to close futures created via this class */ public class CloseableScheduledExecutorService extends CloseableExecutorService { private final ScheduledExecutorService scheduledExecutorService; /** * @param scheduledExecutorService the service to decorate */ public CloseableScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) { super(scheduledExecutorService, false); this.scheduledExecutorService = scheduledExecutorService; } /** * @param scheduledExecutorService the service to decorate * @param shutdownOnClose if true, shutdown the executor service when this is closed */ public CloseableScheduledExecutorService(ScheduledExecutorService scheduledExecutorService, boolean shutdownOnClose) { super(scheduledExecutorService, shutdownOnClose); this.scheduledExecutorService = scheduledExecutorService; } /** * Creates and executes a one-shot action that becomes enabled * after the given delay. * * @param task the task to execute * @param delay the time from now to delay execution * @param unit the time unit of the delay parameter * @return a Future representing pending completion of * the task and whose get() method will return * null upon completion */ public Future schedule(Runnable task, long delay, TimeUnit unit) { Preconditions.checkState(isOpen.get(), "CloseableExecutorService is closed"); InternalFutureTask futureTask = new InternalFutureTask(new FutureTask(task, null)); scheduledExecutorService.schedule(futureTask, delay, unit); return futureTask; } /** * Creates and executes a periodic action that becomes enabled first * after the given initial delay, and subsequently with the * given delay between the termination of one execution and the * commencement of the next. If any execution of the task * encounters an exception, subsequent executions are suppressed. * Otherwise, the task will only terminate via cancellation or * termination of the executor. * * @param task the task to execute * @param initialDelay the time to delay first execution * @param delay the delay between the termination of one * execution and the commencement of the next * @param unit the time unit of the initialDelay and delay parameters * @return a Future representing pending completion of * the task, and whose get() method will throw an * exception upon cancellation */ public Future scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) { Preconditions.checkState(isOpen.get(), "CloseableExecutorService is closed"); ScheduledFuture scheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(task, initialDelay, delay, unit); return new InternalScheduledFutureTask(scheduledFuture); } } CloseableUtils.java000066400000000000000000000047771442004423600340260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import com.google.common.io.Closeables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; /** * This class adds back functionality that was removed in Guava v16.0. */ public class CloseableUtils { private static final Logger log = LoggerFactory.getLogger(CloseableUtils.class); /** *

* This method has been added because Guava has removed the * {@code closeQuietly()} method from {@code Closeables} in v16.0. It's * tempting simply to replace calls to {@code closeQuietly(closeable)} * with calls to {@code close(closeable, true)} to close * {@code Closeable}s while swallowing {@code IOException}s, but * {@code close()} is declared as {@code throws IOException} whereas * {@code closeQuietly()} is not, so it's not a drop-in replacement. *

*

* On the whole, Guava is very backwards compatible. By fixing this nit, * Curator can continue to support newer versions of Guava without having * to bump its own dependency version. *

*

* See https://issues.apache.org/jira/browse/CURATOR-85 *

*/ public static void closeQuietly(Closeable closeable) { try { // Here we've instructed Guava to swallow the IOException Closeables.close(closeable, true); } catch ( IOException e ) { // We instructed Guava to swallow the IOException, so this should // never happen. Since it did, log it. log.error("IOException should not have been thrown.", e); } } } Compatibility.java000066400000000000000000000075641442004423600337220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.InetSocketAddress; /** * Utils to help with ZK version compatibility */ public class Compatibility { private static final Logger log = LoggerFactory.getLogger(Compatibility.class); private static final Method getReachableOrOneMethod; private static final Field addrField; private static final boolean hasPersistentWatchers; static { Method localGetReachableOrOneMethod; try { Class multipleAddressesClass = Class.forName("org.apache.zookeeper.server.quorum.MultipleAddresses"); localGetReachableOrOneMethod = multipleAddressesClass.getMethod("getReachableOrOne"); log.info("Using org.apache.zookeeper.server.quorum.MultipleAddresses"); } catch ( ReflectiveOperationException ignore ) { localGetReachableOrOneMethod = null; } getReachableOrOneMethod = localGetReachableOrOneMethod; Field localAddrField; try { localAddrField = QuorumPeer.QuorumServer.class.getField("addr"); } catch ( NoSuchFieldException e ) { localAddrField = null; log.error("Could not get addr field! Reconfiguration fail!"); } addrField = localAddrField; boolean localHasPersistentWatchers; try { Class.forName("org.apache.zookeeper.AddWatchMode"); localHasPersistentWatchers = true; } catch ( ClassNotFoundException e ) { localHasPersistentWatchers = false; log.info("Persistent Watchers are not available in the version of the ZooKeeper library being used"); } hasPersistentWatchers = localHasPersistentWatchers; } public static boolean hasGetReachableOrOneMethod() { return (getReachableOrOneMethod != null); } public static boolean hasAddrField() { return (addrField != null); } public static String getHostString(QuorumPeer.QuorumServer server) { InetSocketAddress address = null; if ( getReachableOrOneMethod != null ) { try { address = (InetSocketAddress)getReachableOrOneMethod.invoke(server.addr); } catch ( Exception e ) { log.error("Could not call getReachableOrOneMethod.invoke({})", server.addr, e); } } else if (addrField != null) { try { address = (InetSocketAddress)addrField.get(server); } catch ( Exception e ) { log.error("Could not call addrField.get({})", server, e); } } return address != null ? address.getHostString() : "unknown"; } public static boolean hasPersistentWatchers() { return hasPersistentWatchers; } } ConfigurableZookeeperFactory.java000066400000000000000000000027071442004423600367170ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.client.ZKClientConfig; /** * Configurable ZookeeperFactory, by using org.apache.zookeeper.client.ZKClientConfig. * */ public class ConfigurableZookeeperFactory extends DefaultZookeeperFactory { public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly, ZKClientConfig zkClientConfig) throws Exception { return new ZooKeeperAdmin(connectString, sessionTimeout, watcher, canBeReadOnly, zkClientConfig); } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/DebugUtils.java000066400000000000000000000030751442004423600332300ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; public class DebugUtils { public static final String PROPERTY_LOG_EVENTS = "curator-log-events"; public static final String PROPERTY_DONT_LOG_CONNECTION_ISSUES = "curator-dont-log-connection-problems"; public static final String PROPERTY_LOG_ONLY_FIRST_CONNECTION_ISSUE_AS_ERROR_LEVEL = "curator-log-only-first-connection-issue-as-error-level"; public static final String PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND = "curator-remove-watchers-in-foreground"; public static final String PROPERTY_VALIDATE_NAMESPACE_WATCHER_MAP_EMPTY = "curator-validate-namespace-watcher-map-empty"; public static final String PROPERTY_VALIDATE_NO_REMAINING_WATCHERS = "curator-validate-no-remaining-watchers"; private DebugUtils() { } } DefaultTracerDriver.java000066400000000000000000000030731442004423600350010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import org.apache.curator.drivers.TracerDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.TimeUnit; /** * Default tracer driver */ public class DefaultTracerDriver implements TracerDriver { private final Logger log = LoggerFactory.getLogger(getClass()); @Override public void addTrace(String name, long time, TimeUnit unit) { if ( log.isTraceEnabled() ) { log.trace("Trace: " + name + " - " + TimeUnit.MILLISECONDS.convert(time, unit) + " ms"); } } @Override public void addCount(String name, int increment) { if ( log.isTraceEnabled() ) { log.trace("Counter " + name + ": " + increment); } } } DefaultZookeeperFactory.java000066400000000000000000000024111442004423600356730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; public class DefaultZookeeperFactory implements ZookeeperFactory { @Override public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws Exception { return new ZooKeeperAdmin(connectString, sessionTimeout, watcher, canBeReadOnly); } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/EnsurePath.java000066400000000000000000000123631442004423600332370ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.RetryLoop; import org.apache.zookeeper.ZooKeeper; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; /** *

* Utility to ensure that a particular path is created. *

*

* The first time it is used, a synchronized call to {@link ZKPaths#mkdirs(ZooKeeper, String)} is made to * ensure that the entire path has been created (with an empty byte array if needed). Subsequent * calls with the instance are un-synchronized NOPs. *

*

* Usage:
*

*
 *         EnsurePath       ensurePath = new EnsurePath(aFullPathToEnsure);
 *         ...
 *         String           nodePath = aFullPathToEnsure + "/foo";
 *         ensurePath.ensure(zk);   // first time syncs and creates if needed
 *         zk.create(nodePath, ...);
 *         ...
 *         ensurePath.ensure(zk);   // subsequent times are NOPs
 *         zk.create(nodePath, ...);
 * 
* * @deprecated Since 2.9.0 - Prefer CuratorFramework.create().creatingParentContainersIfNeeded() or CuratorFramework.exists().creatingParentContainersIfNeeded() */ @Deprecated public class EnsurePath { private final String path; private final boolean makeLastNode; private final InternalACLProvider aclProvider; private final AtomicReference helper; private static final Helper doNothingHelper = new Helper() { @Override public void ensure(CuratorZookeeperClient client, String path, final boolean makeLastNode) throws Exception { // NOP } }; interface Helper { public void ensure(CuratorZookeeperClient client, String path, final boolean makeLastNode) throws Exception; } /** * @param path the full path to ensure */ public EnsurePath(String path) { this(path, null, true, null); } /** * @param path the full path to ensure * @param aclProvider if not null, the ACL provider to use when creating parent nodes */ public EnsurePath(String path, InternalACLProvider aclProvider) { this(path, null, true, aclProvider); } /** * First time, synchronizes and makes sure all nodes in the path are created. Subsequent calls * with this instance are NOPs. * * @param client ZK client * @throws Exception ZK errors */ public void ensure(CuratorZookeeperClient client) throws Exception { Helper localHelper = helper.get(); localHelper.ensure(client, path, makeLastNode); } /** * Returns a view of this EnsurePath instance that does not make the last node. * i.e. if the path is "/a/b/c" only "/a/b" will be ensured * * @return view */ public EnsurePath excludingLast() { return new EnsurePath(path, helper, false, aclProvider); } protected EnsurePath(String path, AtomicReference helper, boolean makeLastNode, InternalACLProvider aclProvider) { this.path = path; this.makeLastNode = makeLastNode; this.aclProvider = aclProvider; this.helper = (helper != null) ? helper : new AtomicReference(new InitialHelper()); } /** * Returns the path being Ensured * * @return the path being ensured */ public String getPath() { return this.path; } protected boolean asContainers() { return false; } private class InitialHelper implements Helper { private boolean isSet = false; // guarded by synchronization @Override public synchronized void ensure(final CuratorZookeeperClient client, final String path, final boolean makeLastNode) throws Exception { if ( !isSet ) { RetryLoop.callWithRetry ( client, new Callable() { @Override public Object call() throws Exception { ZKPaths.mkdirs(client.getZooKeeper(), path, makeLastNode, aclProvider, asContainers()); helper.set(doNothingHelper); isSet = true; return null; } } ); } } } } ExceptionAccumulator.java000066400000000000000000000036231442004423600352370ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import com.google.common.base.Throwables; /** * Utility to accumulate multiple potential exceptions into one that * is thrown at the end */ public class ExceptionAccumulator { private volatile Throwable mainEx = null; /** * If there is an accumulated exception, throw it */ public void propagate() { if ( mainEx != null ) { Throwables.propagate(mainEx); } } /** * Add an exception into the accumulated exceptions. Note: * if the exception is {@link java.lang.InterruptedException} * then Thread.currentThread().interrupt() is called. * * @param e the exception */ public void add(Throwable e) { if ( e instanceof InterruptedException ) { if ( mainEx != null ) { e.addSuppressed(mainEx); } Thread.currentThread().interrupt(); } if ( mainEx == null ) { mainEx = e; } else { mainEx.addSuppressed(e); } } } InternalACLProvider.java000066400000000000000000000025341442004423600347100ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import java.util.List; public interface InternalACLProvider { /** * Return the ACL list to use by default (usually {@link ZooDefs.Ids#OPEN_ACL_UNSAFE}). * * @return default ACL list */ public List getDefaultAcl(); /** * Return the ACL list to use for the given path * * @param path path (NOTE: might be null) * @return ACL list */ public List getAclForPath(String path); } NonAdminZookeeperFactory.java000066400000000000000000000023231442004423600360140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; public class NonAdminZookeeperFactory implements ZookeeperFactory { @Override public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws Exception { return new ZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly); } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/PathUtils.java000066400000000000000000000100411442004423600330650ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; /** * This class is copied from Apache ZooKeeper. * The original class is not exported by ZooKeeper bundle and thus it can't be used in OSGi. * See issue: https://issues.apache.org/jira/browse/ZOOKEEPER-1627 * A temporary workaround till the issue is resolved is to keep a copy of this class locally. */ public class PathUtils { /** validate the provided znode path string * @param path znode path string * @param isSequential if the path is being created * with a sequential flag * @throws IllegalArgumentException if the path is invalid */ public static void validatePath(String path, boolean isSequential) throws IllegalArgumentException { validatePath(isSequential? path + "1": path); } /** * Validate the provided znode path string * @param path znode path string * @return The given path if it was valid, for fluent chaining * @throws IllegalArgumentException if the path is invalid */ public static String validatePath(String path) throws IllegalArgumentException { if (path == null) { throw new IllegalArgumentException("Path cannot be null"); } if (path.length() == 0) { throw new IllegalArgumentException("Path length must be > 0"); } if (path.charAt(0) != '/') { throw new IllegalArgumentException( "Path must start with / character"); } if (path.length() == 1) { // done checking - it's the root return path; } if (path.charAt(path.length() - 1) == '/') { throw new IllegalArgumentException( "Path must not end with / character"); } String reason = null; char lastc = '/'; char chars[] = path.toCharArray(); char c; for (int i = 1; i < chars.length; lastc = chars[i], i++) { c = chars[i]; if (c == 0) { reason = "null character not allowed @" + i; break; } else if (c == '/' && lastc == '/') { reason = "empty node name specified @" + i; break; } else if (c == '.' && lastc == '.') { if (chars[i-2] == '/' && ((i + 1 == chars.length) || chars[i+1] == '/')) { reason = "relative paths not allowed @" + i; break; } } else if (c == '.') { if (chars[i-1] == '/' && ((i + 1 == chars.length) || chars[i+1] == '/')) { reason = "relative paths not allowed @" + i; break; } } else if (c > '\u0000' && c < '\u001f' || c > '\u007f' && c < '\u009F' || c > '\ud800' && c < '\uf8ff' || c > '\ufff0' && c < '\uffff') { reason = "invalid charater @" + i; break; } } if (reason != null) { throw new IllegalArgumentException( "Invalid path string \"" + path + "\" caused by " + reason); } return path; } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/ThreadUtils.java000066400000000000000000000063471442004423600334160ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import com.google.common.base.Throwables; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; public class ThreadUtils { private static final Logger log = LoggerFactory.getLogger(ThreadUtils.class); public static boolean checkInterrupted(Throwable e) { if ( e instanceof InterruptedException ) { Thread.currentThread().interrupt(); return true; } return false; } public static ExecutorService newSingleThreadExecutor(String processName) { return Executors.newSingleThreadExecutor(newThreadFactory(processName)); } public static ExecutorService newFixedThreadPool(int qty, String processName) { return Executors.newFixedThreadPool(qty, newThreadFactory(processName)); } public static ScheduledExecutorService newSingleThreadScheduledExecutor(String processName) { return Executors.newSingleThreadScheduledExecutor(newThreadFactory(processName)); } public static ScheduledExecutorService newFixedThreadScheduledPool(int qty, String processName) { return Executors.newScheduledThreadPool(qty, newThreadFactory(processName)); } public static ThreadFactory newThreadFactory(String processName) { return newGenericThreadFactory("Curator-" + processName); } public static ThreadFactory newGenericThreadFactory(String processName) { Thread.UncaughtExceptionHandler uncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { log.error("Unexpected exception in thread: " + t, e); Throwables.propagate(e); } }; return new ThreadFactoryBuilder() .setNameFormat(processName + "-%d") .setDaemon(true) .setUncaughtExceptionHandler(uncaughtExceptionHandler) .build(); } public static String getProcessName(Class clazz) { if ( clazz.isAnonymousClass() ) { return getProcessName(clazz.getEnclosingClass()); } return clazz.getSimpleName(); } } curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/ZKPaths.java000066400000000000000000000441271442004423600325100ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import java.util.Collections; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZKPaths { /** * Zookeeper's path separator character. */ public static final String PATH_SEPARATOR = "/"; private static final char PATH_SEPARATOR_CHAR = '/'; private static final CreateMode NON_CONTAINER_MODE = CreateMode.PERSISTENT; /** * @return {@link CreateMode#CONTAINER} if the ZK JAR supports it. Otherwise {@link CreateMode#PERSISTENT} */ public static CreateMode getContainerCreateMode() { return CreateModeHolder.containerCreateMode; } /** * Returns true if the version of ZooKeeper client in use supports containers * * @return true/false */ public static boolean hasContainerSupport() { return getContainerCreateMode() != NON_CONTAINER_MODE; } private static class CreateModeHolder { private static final Logger log = LoggerFactory.getLogger(ZKPaths.class); private static final CreateMode containerCreateMode; static { CreateMode localCreateMode; try { localCreateMode = CreateMode.valueOf("CONTAINER"); } catch ( IllegalArgumentException ignore ) { localCreateMode = NON_CONTAINER_MODE; log.warn("The version of ZooKeeper being used doesn't support Container nodes. CreateMode.PERSISTENT will be used instead."); } containerCreateMode = localCreateMode; } } /** * Apply the namespace to the given path * * @param namespace namespace (can be null) * @param path path * @return adjusted path */ public static String fixForNamespace(String namespace, String path) { return fixForNamespace(namespace, path, false); } /** * Apply the namespace to the given path * * @param namespace namespace (can be null) * @param path path * @param isSequential if the path is being created with a sequential flag * @return adjusted path */ public static String fixForNamespace(String namespace, String path, boolean isSequential) { // Child path must be valid in and of itself. PathUtils.validatePath(path, isSequential); if ( namespace != null ) { return makePath(namespace, path); } return path; } /** * Given a full path, return the node name. i.e. "/one/two/three" will return "three" * * @param path the path * @return the node */ public static String getNodeFromPath(String path) { PathUtils.validatePath(path); int i = path.lastIndexOf(PATH_SEPARATOR_CHAR); if ( i < 0 ) { return path; } if ( (i + 1) >= path.length() ) { return ""; } return path.substring(i + 1); } public static class PathAndNode { private final String path; private final String node; public PathAndNode(String path, String node) { this.path = path; this.node = node; } public String getPath() { return path; } public String getNode() { return node; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + node.hashCode(); result = prime * result + path.hashCode(); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } PathAndNode other = (PathAndNode) obj; if (!node.equals(other.node)) { return false; } if (!path.equals(other.path)) { return false; } return true; } @Override public String toString() { return "PathAndNode [path=" + path + ", node=" + node + "]"; } } /** * Given a full path, return the node name and its path. i.e. "/one/two/three" will return {"/one/two", "three"} * * @param path the path * @return the node */ public static PathAndNode getPathAndNode(String path) { PathUtils.validatePath(path); int i = path.lastIndexOf(PATH_SEPARATOR_CHAR); if ( i < 0 ) { return new PathAndNode(path, ""); } if ( (i + 1) >= path.length() ) { return new PathAndNode(PATH_SEPARATOR, ""); } String node = path.substring(i + 1); String parentPath = (i > 0) ? path.substring(0, i) : PATH_SEPARATOR; return new PathAndNode(parentPath, node); } // Hardcoded in {@link org.apache.zookeeper.server.PrepRequestProcessor} static final int SEQUENTIAL_SUFFIX_DIGITS = 10; /** * Extracts the ten-digit suffix from a sequential znode path. Does not currently perform validation on the * provided path; it will just return a string comprising the last ten characters. * * @param path the path of a sequential znodes * @return the sequential suffix */ public static String extractSequentialSuffix(String path) { int length = path.length(); return length > SEQUENTIAL_SUFFIX_DIGITS ? path.substring(length - SEQUENTIAL_SUFFIX_DIGITS) : path; } private static final Splitter PATH_SPLITTER = Splitter.on(PATH_SEPARATOR_CHAR).omitEmptyStrings(); /** * Given a full path, return the the individual parts, without slashes. * The root path will return an empty list. * * @param path the path * @return an array of parts */ public static List split(String path) { PathUtils.validatePath(path); return PATH_SPLITTER.splitToList(path); } /** * Make sure all the nodes in the path are created. NOTE: Unlike File.mkdirs(), Zookeeper doesn't distinguish * between directories and files. So, every node in the path is created. The data for each node is an empty blob * * @param zookeeper the client * @param path path to ensure * @throws InterruptedException thread interruption * @throws org.apache.zookeeper.KeeperException Zookeeper errors */ public static void mkdirs(ZooKeeper zookeeper, String path) throws InterruptedException, KeeperException { mkdirs(zookeeper, path, true, null, false); } /** * Make sure all the nodes in the path are created. NOTE: Unlike File.mkdirs(), Zookeeper doesn't distinguish * between directories and files. So, every node in the path is created. The data for each node is an empty blob * * @param zookeeper the client * @param path path to ensure * @param makeLastNode if true, all nodes are created. If false, only the parent nodes are created * @throws InterruptedException thread interruption * @throws org.apache.zookeeper.KeeperException Zookeeper errors */ public static void mkdirs(ZooKeeper zookeeper, String path, boolean makeLastNode) throws InterruptedException, KeeperException { mkdirs(zookeeper, path, makeLastNode, null, false); } /** * Make sure all the nodes in the path are created. NOTE: Unlike File.mkdirs(), Zookeeper doesn't distinguish * between directories and files. So, every node in the path is created. The data for each node is an empty blob * * @param zookeeper the client * @param path path to ensure * @param makeLastNode if true, all nodes are created. If false, only the parent nodes are created * @param aclProvider if not null, the ACL provider to use when creating parent nodes * @throws InterruptedException thread interruption * @throws org.apache.zookeeper.KeeperException Zookeeper errors */ public static void mkdirs(ZooKeeper zookeeper, String path, boolean makeLastNode, InternalACLProvider aclProvider) throws InterruptedException, KeeperException { mkdirs(zookeeper, path, makeLastNode, aclProvider, false); } /** * Make sure all the nodes in the path are created. NOTE: Unlike File.mkdirs(), Zookeeper doesn't distinguish * between directories and files. So, every node in the path is created. The data for each node is an empty blob * * @param zookeeper the client * @param path path to ensure * @param makeLastNode if true, all nodes are created. If false, only the parent nodes are created * @param aclProvider if not null, the ACL provider to use when creating parent nodes * @param asContainers if true, nodes are created as {@link CreateMode#CONTAINER} * @throws InterruptedException thread interruption * @throws org.apache.zookeeper.KeeperException Zookeeper errors */ public static void mkdirs(ZooKeeper zookeeper, String path, boolean makeLastNode, InternalACLProvider aclProvider, boolean asContainers) throws InterruptedException, KeeperException { PathUtils.validatePath(path); int pos = 1; // skip first slash, root is guaranteed to exist do { pos = path.indexOf(PATH_SEPARATOR_CHAR, pos + 1); if ( pos == -1 ) { if ( makeLastNode ) { pos = path.length(); } else { break; } } String subPath = path.substring(0, pos); if ( zookeeper.exists(subPath, false) == null ) { try { List acl = null; if ( aclProvider != null ) { acl = aclProvider.getAclForPath(subPath); if ( acl == null ) { acl = aclProvider.getDefaultAcl(); } } if ( acl == null ) { acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; } zookeeper.create(subPath, new byte[0], acl, getCreateMode(asContainers)); } catch (KeeperException.NodeExistsException ignore) { // ignore... someone else has created it since we checked } } } while ( pos < path.length() ); } /** * Recursively deletes children of a node. * * @param zookeeper the client * @param path path of the node to delete * @param deleteSelf flag that indicates that the node should also get deleted * @throws InterruptedException * @throws KeeperException */ public static void deleteChildren(ZooKeeper zookeeper, String path, boolean deleteSelf) throws InterruptedException, KeeperException { PathUtils.validatePath(path); List children; try { children = zookeeper.getChildren(path, null); } catch (KeeperException.NoNodeException e) { // someone else has deleted the node since we checked return; } for ( String child : children ) { String fullPath = makePath(path, child); deleteChildren(zookeeper, fullPath, true); } if ( deleteSelf ) { try { zookeeper.delete(path, -1); } catch ( KeeperException.NotEmptyException e ) { //someone has created a new child since we checked ... delete again. deleteChildren(zookeeper, path, true); } catch ( KeeperException.NoNodeException e ) { // ignore... someone else has deleted the node since we checked } } } /** * Return the children of the given path sorted by sequence number * * @param zookeeper the client * @param path the path * @return sorted list of children * @throws InterruptedException thread interruption * @throws org.apache.zookeeper.KeeperException zookeeper errors */ public static List getSortedChildren(ZooKeeper zookeeper, String path) throws InterruptedException, KeeperException { List children = zookeeper.getChildren(path, false); List sortedList = Lists.newArrayList(children); Collections.sort(sortedList); return sortedList; } /** * Given a parent path and a child node, create a combined full path * * @param parent the parent * @param child the child * @return full path */ public static String makePath(String parent, String child) { // 2 is the maximum number of additional path separators inserted int maxPathLength = nullableStringLength(parent) + nullableStringLength(child) + 2; // Avoid internal StringBuilder's buffer reallocation by specifying the max path length StringBuilder path = new StringBuilder(maxPathLength); joinPath(path, parent, child); return path.toString(); } /** * Given a parent path and a list of children nodes, create a combined full path * * @param parent the parent * @param firstChild the first children in the path * @param restChildren the rest of the children in the path * @return full path */ public static String makePath(String parent, String firstChild, String... restChildren) { // 2 is the maximum number of additional path separators inserted int maxPathLength = nullableStringLength(parent) + nullableStringLength(firstChild) + 2; if ( restChildren != null ) { for ( String child : restChildren ) { // 1 is for possible additional separator maxPathLength += nullableStringLength(child) + 1; } } // Avoid internal StringBuilder's buffer reallocation by specifying the max path length StringBuilder path = new StringBuilder(maxPathLength); joinPath(path, parent, firstChild); if ( restChildren == null ) { return path.toString(); } else { for ( String child : restChildren ) { joinPath(path, "", child); } return path.toString(); } } private static int nullableStringLength(String s) { return s != null ? s.length() : 0; } /** * Given a parent and a child node, join them in the given {@link StringBuilder path} * * @param path the {@link StringBuilder} used to make the path * @param parent the parent * @param child the child */ private static void joinPath(StringBuilder path, String parent, String child) { // Add parent piece, with no trailing slash. if ( (parent != null) && (parent.length() > 0) ) { if ( parent.charAt(0) != PATH_SEPARATOR_CHAR ) { path.append(PATH_SEPARATOR_CHAR); } if ( parent.charAt(parent.length() - 1) == PATH_SEPARATOR_CHAR ) { path.append(parent, 0, parent.length() - 1); } else { path.append(parent); } } if ( (child == null) || (child.length() == 0) || (child.length() == 1 && child.charAt(0) == PATH_SEPARATOR_CHAR) ) { // Special case, empty parent and child if ( path.length() == 0 ) { path.append(PATH_SEPARATOR_CHAR); } return; } // Now add the separator between parent and child. path.append(PATH_SEPARATOR_CHAR); int childAppendBeginIndex; if ( child.charAt(0) == PATH_SEPARATOR_CHAR ) { childAppendBeginIndex = 1; } else { childAppendBeginIndex = 0; } int childAppendEndIndex; if ( child.charAt(child.length() - 1) == PATH_SEPARATOR_CHAR ) { childAppendEndIndex = child.length() - 1; } else { childAppendEndIndex = child.length(); } // Finally, add the child. path.append(child, childAppendBeginIndex, childAppendEndIndex); } private ZKPaths() { } private static CreateMode getCreateMode(boolean asContainers) { return asContainers ? getContainerCreateMode() : CreateMode.PERSISTENT; } } ZookeeperFactory.java000066400000000000000000000053551442004423600344000ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/main/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.client.ZKClientConfig; public interface ZookeeperFactory { /** * Allocate a new ZooKeeper instance * * * @param connectString the connection string * @param sessionTimeout session timeout in milliseconds * @param watcher optional watcher * @param canBeReadOnly if true, allow ZooKeeper client to enter * read only mode in case of a network partition. See * {@link ZooKeeper#ZooKeeper(String, int, Watcher, long, byte[], boolean)} * for details * @return the instance * @throws Exception errors */ public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws Exception; /** * Allocate a new ZooKeeper instance * * * @param connectString the connection string * @param sessionTimeout session timeout in milliseconds * @param watcher optional watcher * @param canBeReadOnly if true, allow ZooKeeper client to enter * read only mode in case of a network partition. See * {@link ZooKeeper#ZooKeeper(String, int, Watcher, long, byte[], boolean)} * for details * @param zkClientConfig ZooKeeper client config * @return the instance * @throws Exception errors */ public default ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly, ZKClientConfig zkClientConfig) throws Exception { if (zkClientConfig == null) { return newZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly); } return new ZooKeeperAdmin(connectString, sessionTimeout, watcher, canBeReadOnly, zkClientConfig); } } curator-apache-curator-5.5.0/curator-client/src/site/000077500000000000000000000000001442004423600225615ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/site/confluence/000077500000000000000000000000001442004423600247025ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/site/confluence/index.confluence000066400000000000000000000074651442004423600300700ustar00rootroot00000000000000h1. Client h2. IMPORTANT NOTE The Curator Client is a low\-level API. It is strongly recommended that you use the Curator [[Framework|../curator-framework/index.html]] instead of directly using CuratorZookeeperClient. h2. Background CuratorZookeeperClient is a wrapper around ZooKeeper's Java client that makes client access to ZooKeeper much simpler and less error prone. It provides the following features: * Continuous connection management \- ZooKeeper has many caveats regarding connection management (see the ZooKeeper FAQ for details). CuratorZookeeperClient takes care of most of them automatically. * Operation retry utilities \- a method for retrying operations is provided * Test ZooKeeper server \- an in\-process, self\-contained ZooKeeper test server is provided that can be used for test cases and experimentation h2. Method Documentation ||Method||Description|| |_Constructor_|Create a connection to a given ZooKeeper cluster. You can pass in an optional watcher. You must provide a Retry Policy.| |getZooKeeper()|Returns the managed ZooKeeper instance. *IMPORTANT NOTES:* a) It takes time for a connection to complete, you should validate that the connection is complete before using other methods. b) The managed ZooKeeper instance can change due to certain events. Do not hold on to the instance for long periods. Always get a fresh reference from getZooKeeper().| |isConnected()|Returns true if the ZooKeeper client is currently connected (this can always change due to errors)| |blockUntilConnectedOrTimedOut()|A convenience method that blocks until the initial connection is successful or times out| |close()|Close the connection| |setRetryPolicy()|Change the retry policy| |newRetryLoop()|Allocate a new Retry Loop \- see details below| h2. Retry Loop For a variety of reasons, operations on a ZooKeeper cluster can fail. Best practices dictate that these operations should be retried. The Retry Loop mechanism provides the means to do this. *Every operation should be wrapped in a retry loop*. Here's the canonical usage: {code} RetryLoop retryLoop = client.newRetryLoop(); while ( retryLoop.shouldContinue() ) { try { // perform your work ... // it's important to re\-get the ZK instance as there may have been an error and the instance was re\-created ZooKeeper zk = client.getZookeeper(); retryLoop.markComplete(); } catch ( Exception e ) { retryLoop.takeException(e); } } {code} The Retry Loop maintains a count of retries and determines if a given error is retry\-able. If an operation is a candidate for retrying, the Retry Policy is invoked to determine if the retry should proceed. As a convenience, RetryLoop has a static method that takes a Callable to perform a complete retry loop on. E.g. {code} RetryLoop.callWithRetry(client, new Callable() { @Override public Void call() throws Exception { // do your work here - it will get retried if needed return null; } }); {code} h2. Retry Policies A retry policy is a mechanism to alter retry behavior. It is abstracted by the RetryPolicy interface which has one method: {{public boolean allowRetry(int retryCount, long elapsedTimeMs);}} Before a retry is attempted, allowRetry() is called with the current retry count and the elapsed time of the operation. If the policy allows it, the retry is attempted. If not, an exception is thrown. Several retry policy implementations are provided (in the package {{com.netflix.curator.retry}}): ||Policy Name||Description|| |ExponentialBackoffRetry|Retry policy that retries a set number of times with increasing sleep time between retries| |RetryNTimes|Retry policy that retries a max number of times| |RetryOneTime|A retry policy that retries only once| |RetryUntilElapsed|A retry policy that retries until a given amount of time elapses| curator-apache-curator-5.5.0/curator-client/src/site/site.xml000066400000000000000000000026401442004423600242510ustar00rootroot00000000000000 ]]> curator-apache-curator-5.5.0/curator-client/src/test/000077500000000000000000000000001442004423600225745ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/test/java/000077500000000000000000000000001442004423600235155ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/test/java/org/000077500000000000000000000000001442004423600243045ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/000077500000000000000000000000001442004423600255255ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/000077500000000000000000000000001442004423600272045ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/BasicTests.java000066400000000000000000000144021442004423600321140ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Duration; import org.apache.curator.ensemble.fixed.FixedEnsembleProvider; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.ZookeeperFactory; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.awaitility.Awaitility; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; public class BasicTests extends BaseClassForTests { @Test public void testFactory() throws Exception { final ZooKeeper mockZookeeper = Mockito.mock(ZooKeeper.class); ZookeeperFactory zookeeperFactory = (connectString, sessionTimeout, watcher, canBeReadOnly) -> mockZookeeper; CuratorZookeeperClient client = new CuratorZookeeperClient(zookeeperFactory, new FixedEnsembleProvider(server.getConnectString()), 10000, 10000, null, new RetryOneTime(1), false); client.start(); assertEquals(client.getZooKeeper(), mockZookeeper); } @Test public void testExpiredSession() throws Exception { // see http://wiki.apache.org/hadoop/ZooKeeper/FAQ#A4 final Timing timing = new Timing(); final CountDownLatch latch = new CountDownLatch(1); Watcher watcher = event -> { if ( event.getState() == Watcher.Event.KeeperState.Expired ) { latch.countDown(); } }; final CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), timing.session(), timing.connection(), watcher, new RetryOneTime(2)); client.start(); try { final AtomicBoolean firstTime = new AtomicBoolean(true); RetryLoop.callWithRetry ( client, () -> { if ( firstTime.compareAndSet(true, false) ) { try { client.getZooKeeper().create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch ( KeeperException.NodeExistsException ignore ) { // ignore } client.getZooKeeper().getTestable().injectSessionExpiration(); assertTrue(timing.awaitLatch(latch)); } ZooKeeper zooKeeper = client.getZooKeeper(); client.blockUntilConnectedOrTimedOut(); assertNotNull(zooKeeper.exists("/foo", false)); return null; } ); } finally { client.close(); } } @Test public void testReconnect() throws Exception { CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), 10000, 10000, null, new RetryOneTime(1)); client.start(); try { client.blockUntilConnectedOrTimedOut(); byte[] writtenData = {1, 2, 3}; client.getZooKeeper().create("/test", writtenData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Thread.sleep(1000); server.stop(); Thread.sleep(1000); server.restart(); assertTrue(client.blockUntilConnectedOrTimedOut()); byte[] readData = client.getZooKeeper().getData("/test", false, null); assertArrayEquals(readData, writtenData); } finally { client.close(); } } @Test public void testSimple() throws Exception { CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), 10000, 10000, null, new RetryOneTime(1)); client.start(); try { client.blockUntilConnectedOrTimedOut(); String path = client.getZooKeeper().create("/test", new byte[]{1,2,3}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(path, "/test"); } finally { client.close(); } } @Test public void testBackgroundConnect() throws Exception { final int CONNECTION_TIMEOUT_MS = 4000; try (CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), 10000, CONNECTION_TIMEOUT_MS, null, new RetryOneTime(1))) { assertFalse(client.isConnected()); client.start(); Awaitility.await() .atMost(Duration.ofMillis(CONNECTION_TIMEOUT_MS)) .untilAsserted(() -> Assertions.assertTrue(client.isConnected())); } } } curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/TestEnsurePath.java000066400000000000000000000117441442004423600327740ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.utils.EnsurePath; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class TestEnsurePath { @Test public void testBasic() throws Exception { ZooKeeper client = mock(ZooKeeper.class, Mockito.RETURNS_MOCKS); CuratorZookeeperClient curator = mock(CuratorZookeeperClient.class); RetryPolicy retryPolicy = new RetryOneTime(1); RetryLoop retryLoop = new RetryLoopImpl(retryPolicy, null); when(curator.getZooKeeper()).thenReturn(client); when(curator.getRetryPolicy()).thenReturn(retryPolicy); when(curator.newRetryLoop()).thenReturn(retryLoop); Stat fakeStat = mock(Stat.class); when(client.exists(Mockito.any(), anyBoolean())).thenReturn(fakeStat); EnsurePath ensurePath = new EnsurePath("/one/two/three"); ensurePath.ensure(curator); verify(client, times(3)).exists(Mockito.any(), anyBoolean()); ensurePath.ensure(curator); verifyNoMoreInteractions(client); ensurePath.ensure(curator); verifyNoMoreInteractions(client); } @Test public void testSimultaneous() throws Exception { ZooKeeper client = mock(ZooKeeper.class, Mockito.RETURNS_MOCKS); RetryPolicy retryPolicy = new RetryOneTime(1); RetryLoop retryLoop = new RetryLoopImpl(retryPolicy, null); final CuratorZookeeperClient curator = mock(CuratorZookeeperClient.class); when(curator.getZooKeeper()).thenReturn(client); when(curator.getRetryPolicy()).thenReturn(retryPolicy); when(curator.newRetryLoop()).thenReturn(retryLoop); final Stat fakeStat = mock(Stat.class); final CountDownLatch startedLatch = new CountDownLatch(2); final CountDownLatch finishedLatch = new CountDownLatch(2); final Semaphore semaphore = new Semaphore(0); when(client.exists(Mockito.any(), anyBoolean())).thenAnswer ( new Answer() { @Override public Stat answer(InvocationOnMock invocation) throws Throwable { semaphore.acquire(); return fakeStat; } } ); final EnsurePath ensurePath = new EnsurePath("/one/two/three"); ExecutorService service = Executors.newCachedThreadPool(); for ( int i = 0; i < 2; ++i ) { service.submit ( new Callable() { @Override public Void call() throws Exception { startedLatch.countDown(); ensurePath.ensure(curator); finishedLatch.countDown(); return null; } } ); } assertTrue(startedLatch.await(10, TimeUnit.SECONDS)); semaphore.release(3); assertTrue(finishedLatch.await(10, TimeUnit.SECONDS)); verify(client, times(3)).exists(Mockito.any(), anyBoolean()); ensurePath.ensure(curator); verifyNoMoreInteractions(client); ensurePath.ensure(curator); verifyNoMoreInteractions(client); } } curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/TestIs37.java000066400000000000000000000032121442004423600314320ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.proto.WhoAmIResponse; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; public class TestIs37 extends CuratorTestBase { /** * Ensure that ZooKeeper is 3.7 or above. * *

It uses reflection to get {@link WhoAmIResponse} which was introduced in 3.7.0. * * @see ZOOKEEPER-3969 */ @Test @Tag(zk37Group) public void testIsZk37() throws Exception { assertNotNull(Class.forName("org.apache.zookeeper.proto.WhoAmIResponse")); } @Override protected void createServer() { // NOP } } curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/TestRetryLoop.java000066400000000000000000000156411442004423600326550ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.RetryForever; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.times; public class TestRetryLoop extends BaseClassForTests { @Test public void testExponentialBackoffRetryLimit() { RetrySleeper sleeper = new RetrySleeper() { @Override public void sleepFor(long time, TimeUnit unit) throws InterruptedException { assertTrue(unit.toMillis(time) <= 100); } }; ExponentialBackoffRetry retry = new ExponentialBackoffRetry(1, Integer.MAX_VALUE, 100); for ( int i = 0; i >= 0; ++i ) { retry.allowRetry(i, 0, sleeper); } } @Test public void testRetryLoopWithFailure() throws Exception { CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), 5000, 5000, null, new RetryOneTime(1)); client.start(); try { int loopCount = 0; RetryLoop retryLoop = client.newRetryLoop(); outer: while ( retryLoop.shouldContinue() ) { ++loopCount; switch ( loopCount ) { case 1: { server.stop(); break; } case 2: { server.restart(); break; } case 3: case 4: { // ignore break; } default: { fail(); break outer; } } try { client.blockUntilConnectedOrTimedOut(); client.getZooKeeper().create("/test", new byte[]{1,2,3}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); retryLoop.markComplete(); } catch ( Exception e ) { retryLoop.takeException(e); } } assertThat(loopCount).isGreaterThanOrEqualTo(2); } finally { client.close(); } } @Test public void testRetryLoop() throws Exception { CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), 10000, 10000, null, new RetryOneTime(1)); client.start(); try { int loopCount = 0; RetryLoop retryLoop = client.newRetryLoop(); while ( retryLoop.shouldContinue() ) { if ( ++loopCount > 2 ) { fail(); break; } try { client.getZooKeeper().create("/test", new byte[]{1,2,3}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); retryLoop.markComplete(); } catch ( Exception e ) { retryLoop.takeException(e); } } assertTrue(loopCount > 0); } finally { client.close(); } } @Test public void testRetryForever() throws Exception { int retryIntervalMs = 1; RetrySleeper sleeper = Mockito.mock(RetrySleeper.class); RetryForever retryForever = new RetryForever(retryIntervalMs); for (int i = 0; i < 10; i++) { boolean allowed = retryForever.allowRetry(i, 0, sleeper); assertTrue(allowed); Mockito.verify(sleeper, times(i + 1)).sleepFor(retryIntervalMs, TimeUnit.MILLISECONDS); } } @Test public void testRetryForeverWithSessionFailed() throws Exception { final Timing timing = new Timing(); final RetryPolicy retryPolicy = new SessionFailedRetryPolicy(new RetryForever(1000)); final CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), timing.session(), timing.connection(), null, retryPolicy); client.start(); try { int loopCount = 0; final RetryLoop retryLoop = client.newRetryLoop(); while ( retryLoop.shouldContinue() ) { if ( ++loopCount > 1 ) { break; } try { client.getZooKeeper().getTestable().injectSessionExpiration(); client.getZooKeeper().create("/test", new byte[]{1,2,3}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); retryLoop.markComplete(); } catch ( Exception e ) { retryLoop.takeException(e); } } fail("Should failed with SessionExpiredException."); } catch ( Exception e ) { if ( e instanceof KeeperException ) { int rc = ((KeeperException) e).code().intValue(); assertEquals(rc, KeeperException.Code.SESSIONEXPIRED.intValue()); } else { throw e; } } finally { client.close(); } } } TestSessionFailRetryLoop.java000066400000000000000000000263741442004423600347430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.test.Timing; import org.junit.jupiter.api.Test; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; public class TestSessionFailRetryLoop extends BaseClassForTests { @Test public void testRetry() throws Exception { Timing timing = new Timing(); final CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), timing.session(), timing.connection(), null, new ExponentialBackoffRetry(100, 3)); SessionFailRetryLoop retryLoop = client.newSessionFailRetryLoop(SessionFailRetryLoop.Mode.RETRY); retryLoop.start(); try { client.start(); final AtomicBoolean secondWasDone = new AtomicBoolean(false); final AtomicBoolean firstTime = new AtomicBoolean(true); while ( retryLoop.shouldContinue() ) { try { RetryLoop.callWithRetry ( client, new Callable() { @Override public Void call() throws Exception { if ( firstTime.compareAndSet(true, false) ) { assertNull(client.getZooKeeper().exists("/foo/bar", false)); client.getZooKeeper().getTestable().injectSessionExpiration(); client.getZooKeeper(); client.blockUntilConnectedOrTimedOut(); } assertNull(client.getZooKeeper().exists("/foo/bar", false)); return null; } } ); RetryLoop.callWithRetry ( client, new Callable() { @Override public Void call() throws Exception { assertFalse(firstTime.get()); assertNull(client.getZooKeeper().exists("/foo/bar", false)); secondWasDone.set(true); return null; } } ); } catch ( Exception e ) { retryLoop.takeException(e); } } assertTrue(secondWasDone.get()); } finally { retryLoop.close(); CloseableUtils.closeQuietly(client); } } @Test public void testRetryStatic() throws Exception { Timing timing = new Timing(); final CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), timing.session(), timing.connection(), null, new ExponentialBackoffRetry(100, 3)); SessionFailRetryLoop retryLoop = client.newSessionFailRetryLoop(SessionFailRetryLoop.Mode.RETRY); retryLoop.start(); try { client.start(); final AtomicBoolean secondWasDone = new AtomicBoolean(false); final AtomicBoolean firstTime = new AtomicBoolean(true); SessionFailRetryLoop.callWithRetry ( client, SessionFailRetryLoop.Mode.RETRY, new Callable() { @Override public Object call() throws Exception { RetryLoop.callWithRetry ( client, new Callable() { @Override public Void call() throws Exception { if ( firstTime.compareAndSet(true, false) ) { assertNull(client.getZooKeeper().exists("/foo/bar", false)); client.getZooKeeper().getTestable().injectSessionExpiration(); client.getZooKeeper(); client.blockUntilConnectedOrTimedOut(); } assertNull(client.getZooKeeper().exists("/foo/bar", false)); return null; } } ); RetryLoop.callWithRetry ( client, new Callable() { @Override public Void call() throws Exception { assertFalse(firstTime.get()); assertNull(client.getZooKeeper().exists("/foo/bar", false)); secondWasDone.set(true); return null; } } ); return null; } } ); assertTrue(secondWasDone.get()); } finally { retryLoop.close(); CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { final Timing timing = new Timing(); final CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), timing.session(), timing.connection(), null, new ExponentialBackoffRetry(100, 3)); SessionFailRetryLoop retryLoop = client.newSessionFailRetryLoop(SessionFailRetryLoop.Mode.FAIL); retryLoop.start(); try { client.start(); try { while ( retryLoop.shouldContinue() ) { try { RetryLoop.callWithRetry ( client, new Callable() { @Override public Void call() throws Exception { assertNull(client.getZooKeeper().exists("/foo/bar", false)); client.getZooKeeper().getTestable().injectSessionExpiration(); timing.sleepABit(); client.getZooKeeper(); client.blockUntilConnectedOrTimedOut(); assertNull(client.getZooKeeper().exists("/foo/bar", false)); return null; } } ); } catch ( Exception e ) { retryLoop.takeException(e); } } fail(); } catch ( SessionFailRetryLoop.SessionFailedException dummy ) { // correct } } finally { retryLoop.close(); CloseableUtils.closeQuietly(client); } } @Test public void testBasicStatic() throws Exception { Timing timing = new Timing(); final CuratorZookeeperClient client = new CuratorZookeeperClient(server.getConnectString(), timing.session(), timing.connection(), null, new ExponentialBackoffRetry(100, 3)); SessionFailRetryLoop retryLoop = client.newSessionFailRetryLoop(SessionFailRetryLoop.Mode.FAIL); retryLoop.start(); try { client.start(); try { SessionFailRetryLoop.callWithRetry ( client, SessionFailRetryLoop.Mode.FAIL, new Callable() { @Override public Object call() throws Exception { RetryLoop.callWithRetry ( client, new Callable() { @Override public Void call() throws Exception { assertNull(client.getZooKeeper().exists("/foo/bar", false)); client.getZooKeeper().getTestable().injectSessionExpiration(); client.getZooKeeper(); client.blockUntilConnectedOrTimedOut(); assertNull(client.getZooKeeper().exists("/foo/bar", false)); return null; } } ); return null; } } ); } catch ( SessionFailRetryLoop.SessionFailedException dummy ) { // correct } } finally { retryLoop.close(); CloseableUtils.closeQuietly(client); } } } curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/utils/000077500000000000000000000000001442004423600303445ustar00rootroot00000000000000TestCloseableExecutorService.java000066400000000000000000000173221442004423600367260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class TestCloseableExecutorService { private static final int QTY = 10; private volatile ExecutorService executorService; @BeforeEach public void setup() { executorService = Executors.newFixedThreadPool(QTY * 2); } @AfterEach public void tearDown() { executorService.shutdownNow(); } @Test public void testBasicRunnable() throws InterruptedException { try { CloseableExecutorService service = new CloseableExecutorService(executorService); CountDownLatch startLatch = new CountDownLatch(QTY); CountDownLatch latch = new CountDownLatch(QTY); for ( int i = 0; i < QTY; ++i ) { submitRunnable(service, startLatch, latch); } assertTrue(startLatch.await(3, TimeUnit.SECONDS)); service.close(); assertTrue(latch.await(3, TimeUnit.SECONDS)); } catch ( AssertionError e ) { throw e; } catch ( Throwable e ) { e.printStackTrace(); } } @Test public void testBasicCallable() throws InterruptedException { CloseableExecutorService service = new CloseableExecutorService(executorService); final CountDownLatch startLatch = new CountDownLatch(QTY); final CountDownLatch latch = new CountDownLatch(QTY); for ( int i = 0; i < QTY; ++i ) { service.submit ( (Callable) () -> { try { startLatch.countDown(); Thread.currentThread().join(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } finally { latch.countDown(); } return null; } ); } assertTrue(startLatch.await(3, TimeUnit.SECONDS)); service.close(); assertTrue(latch.await(3, TimeUnit.SECONDS)); } @Test public void testListeningRunnable() throws InterruptedException { CloseableExecutorService service = new CloseableExecutorService(executorService); List> futures = Lists.newArrayList(); final CountDownLatch startLatch = new CountDownLatch(QTY); for ( int i = 0; i < QTY; ++i ) { Future future = service.submit ( () -> { try { startLatch.countDown(); Thread.currentThread().join(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } } ); futures.add(future); } assertTrue(startLatch.await(3, TimeUnit.SECONDS)); for ( Future future : futures ) { future.cancel(true); } assertEquals(service.size(), 0); } @Test public void testListeningCallable() throws InterruptedException { CloseableExecutorService service = new CloseableExecutorService(executorService); final CountDownLatch startLatch = new CountDownLatch(QTY); List> futures = Lists.newArrayList(); for ( int i = 0; i < QTY; ++i ) { Future future = service.submit ( (Callable) () -> { try { startLatch.countDown(); Thread.currentThread().join(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } return null; } ); futures.add(future); } assertTrue(startLatch.await(3, TimeUnit.SECONDS)); for ( Future future : futures ) { future.cancel(true); } assertEquals(service.size(), 0); } @Test public void testPartialRunnable() throws InterruptedException { final CountDownLatch outsideLatch = new CountDownLatch(1); executorService.submit ( () -> { try { Thread.currentThread().join(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } finally { outsideLatch.countDown(); } } ); CloseableExecutorService service = new CloseableExecutorService(executorService); CountDownLatch startLatch = new CountDownLatch(QTY); CountDownLatch latch = new CountDownLatch(QTY); for ( int i = 0; i < QTY; ++i ) { submitRunnable(service, startLatch, latch); } Awaitility.await().until(()-> service.size() >= QTY); assertTrue(startLatch.await(3, TimeUnit.SECONDS)); service.close(); assertTrue(latch.await(3, TimeUnit.SECONDS)); assertEquals(outsideLatch.getCount(), 1); } private void submitRunnable(CloseableExecutorService service, final CountDownLatch startLatch, final CountDownLatch latch) { service.submit ( () -> { try { startLatch.countDown(); Thread.sleep(100000); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } finally { latch.countDown(); } } ); } } TestCloseableScheduledExecutorService.java000066400000000000000000000075351442004423600405540ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/utils/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class TestCloseableScheduledExecutorService { private static final int QTY = 10; private static final int DELAY_MS = 100; private volatile ScheduledExecutorService executorService; @BeforeEach public void setup() { executorService = Executors.newScheduledThreadPool(QTY * 2); } @AfterEach public void tearDown() { executorService.shutdownNow(); } @Test public void testCloseableScheduleWithFixedDelay() throws InterruptedException { CloseableScheduledExecutorService service = new CloseableScheduledExecutorService(executorService); final CountDownLatch latch = new CountDownLatch(QTY); service.scheduleWithFixedDelay( new Runnable() { @Override public void run() { latch.countDown(); } }, DELAY_MS, DELAY_MS, TimeUnit.MILLISECONDS ); assertTrue(latch.await((QTY * 2) * DELAY_MS, TimeUnit.MILLISECONDS)); } @Test public void testCloseableScheduleWithFixedDelayAndAdditionalTasks() throws InterruptedException { final AtomicInteger outerCounter = new AtomicInteger(0); Runnable command = new Runnable() { @Override public void run() { outerCounter.incrementAndGet(); } }; executorService.scheduleWithFixedDelay(command, DELAY_MS, DELAY_MS, TimeUnit.MILLISECONDS); CloseableScheduledExecutorService service = new CloseableScheduledExecutorService(executorService); final AtomicInteger innerCounter = new AtomicInteger(0); service.scheduleWithFixedDelay(new Runnable() { @Override public void run() { innerCounter.incrementAndGet(); } }, DELAY_MS, DELAY_MS, TimeUnit.MILLISECONDS); Thread.sleep(DELAY_MS * 4); service.close(); Thread.sleep(DELAY_MS * 2); int innerValue = innerCounter.get(); assertTrue(innerValue > 0); int value = outerCounter.get(); Thread.sleep(DELAY_MS * 2); int newValue = outerCounter.get(); assertTrue(newValue > value); assertEquals(innerValue, innerCounter.get()); value = newValue; Thread.sleep(DELAY_MS * 2); newValue = outerCounter.get(); assertTrue(newValue > value); assertEquals(innerValue, innerCounter.get()); } } curator-apache-curator-5.5.0/curator-client/src/test/java/org/apache/curator/utils/TestZKPaths.java000066400000000000000000000074351442004423600334040ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.utils; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; public class TestZKPaths { @SuppressWarnings("NullArgumentToVariableArgMethod") @Test public void testMakePath() { assertEquals(ZKPaths.makePath(null, "/"), "/"); assertEquals(ZKPaths.makePath("", null), "/"); assertEquals(ZKPaths.makePath("/", null), "/"); assertEquals(ZKPaths.makePath(null, null), "/"); assertEquals(ZKPaths.makePath("/", "/"), "/"); assertEquals(ZKPaths.makePath("", "/"), "/"); assertEquals(ZKPaths.makePath("/", ""), "/"); assertEquals(ZKPaths.makePath("", ""), "/"); assertEquals(ZKPaths.makePath("foo", ""), "/foo"); assertEquals(ZKPaths.makePath("foo", "/"), "/foo"); assertEquals(ZKPaths.makePath("/foo", ""), "/foo"); assertEquals(ZKPaths.makePath("/foo", "/"), "/foo"); assertEquals(ZKPaths.makePath("foo", null), "/foo"); assertEquals(ZKPaths.makePath("foo", null), "/foo"); assertEquals(ZKPaths.makePath("/foo", null), "/foo"); assertEquals(ZKPaths.makePath("/foo", null), "/foo"); assertEquals(ZKPaths.makePath("", "bar"), "/bar"); assertEquals(ZKPaths.makePath("/", "bar"), "/bar"); assertEquals(ZKPaths.makePath("", "/bar"), "/bar"); assertEquals(ZKPaths.makePath("/", "/bar"), "/bar"); assertEquals(ZKPaths.makePath(null, "bar"), "/bar"); assertEquals(ZKPaths.makePath(null, "bar"), "/bar"); assertEquals(ZKPaths.makePath(null, "/bar"), "/bar"); assertEquals(ZKPaths.makePath(null, "/bar"), "/bar"); assertEquals(ZKPaths.makePath("foo", "bar"), "/foo/bar"); assertEquals(ZKPaths.makePath("/foo", "bar"), "/foo/bar"); assertEquals(ZKPaths.makePath("foo", "/bar"), "/foo/bar"); assertEquals(ZKPaths.makePath("/foo", "/bar"), "/foo/bar"); assertEquals(ZKPaths.makePath("/foo", "bar/"), "/foo/bar"); assertEquals(ZKPaths.makePath("/foo/", "/bar/"), "/foo/bar"); assertEquals(ZKPaths.makePath("foo", "bar", "baz"), "/foo/bar/baz"); assertEquals(ZKPaths.makePath("foo", "bar", "baz", "qux"), "/foo/bar/baz/qux"); assertEquals(ZKPaths.makePath("/foo", "/bar", "/baz"), "/foo/bar/baz"); assertEquals(ZKPaths.makePath("/foo/", "/bar/", "/baz/"), "/foo/bar/baz"); assertEquals(ZKPaths.makePath("foo", null, null), "/foo"); assertEquals(ZKPaths.makePath("foo", "bar", null), "/foo/bar"); assertEquals(ZKPaths.makePath("foo", null, "baz"), "/foo/baz"); } @Test public void testSplit() { assertEquals(ZKPaths.split("/"), Collections.emptyList()); assertEquals(ZKPaths.split("/test"), Collections.singletonList("test")); assertEquals(ZKPaths.split("/test/one"), Arrays.asList("test", "one")); assertEquals(ZKPaths.split("/test/one/two"), Arrays.asList("test", "one", "two")); } } curator-apache-curator-5.5.0/curator-client/src/test/resources/000077500000000000000000000000001442004423600246065ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-client/src/test/resources/log4j.properties000066400000000000000000000021071442004423600277430ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. log4j.rootLogger=ERROR, console log4j.logger.org.apache.curator=DEBUG, console log4j.additivity.org.apache.curator=false log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n curator-apache-curator-5.5.0/curator-examples/000077500000000000000000000000001442004423600213665ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/LICENSE000066400000000000000000000261361442004423600224030ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. curator-apache-curator-5.5.0/curator-examples/NOTICE000066400000000000000000000002501442004423600222670ustar00rootroot00000000000000Apache Curator Copyright 2013-2023 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). curator-apache-curator-5.5.0/curator-examples/pom.xml000066400000000000000000000045231442004423600227070ustar00rootroot00000000000000 4.0.0 org.apache.curator apache-curator 5.5.0 curator-examples Curator Examples Example usages of various Curator features. 2011 org.apache.curator curator-recipes org.apache.curator curator-test org.apache.curator curator-x-discovery org.apache.curator curator-x-async com.fasterxml.jackson.core jackson-databind org.slf4j slf4j-log4j12 curator-apache-curator-5.5.0/curator-examples/src/000077500000000000000000000000001442004423600221555ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/000077500000000000000000000000001442004423600231015ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/000077500000000000000000000000001442004423600240225ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/async/000077500000000000000000000000001442004423600251375ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/async/AsyncExamples.java000066400000000000000000000120531442004423600305570ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package async; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.AsyncEventException; import org.apache.curator.x.async.WatchMode; import org.apache.zookeeper.WatchedEvent; import java.util.concurrent.CompletionStage; /** * Examples using the asynchronous DSL */ public class AsyncExamples { public static AsyncCuratorFramework wrap(CuratorFramework client) { // wrap a CuratorFramework instance so that it can be used async. // do this once and re-use the returned AsyncCuratorFramework instance return AsyncCuratorFramework.wrap(client); } public static void create(CuratorFramework client, String path, byte[] payload) { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); // normally you'd wrap early in your app and reuse the instance // create a node at the given path with the given payload asynchronously async.create().forPath(path, payload).whenComplete((name, exception) -> { if ( exception != null ) { // there was a problem exception.printStackTrace(); } else { System.out.println("Created node name is: " + name); } }); } public static void createThenWatch(CuratorFramework client, String path) { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); // normally you'd wrap early in your app and reuse the instance // this example shows to asynchronously use watchers for both event // triggering and connection problems. If you don't need to be notified // of connection problems, use the simpler approach shown in createThenWatchSimple() // create a node at the given path with the given payload asynchronously // then watch the created node async.create().forPath(path).whenComplete((name, exception) -> { if ( exception != null ) { // there was a problem creating the node exception.printStackTrace(); } else { handleWatchedStage(async.watched().checkExists().forPath(path).event()); } }); } public static void createThenWatchSimple(CuratorFramework client, String path) { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); // normally you'd wrap early in your app and reuse the instance // create a node at the given path with the given payload asynchronously // then watch the created node async.create().forPath(path).whenComplete((name, exception) -> { if ( exception != null ) { // there was a problem creating the node exception.printStackTrace(); } else { // because "WatchMode.successOnly" is used the watch stage is only triggered when // the EventType is a node event async.with(WatchMode.successOnly).watched().checkExists().forPath(path).event().thenAccept(event -> { System.out.println(event.getType()); System.out.println(event); }); } }); } private static void handleWatchedStage(CompletionStage watchedStage) { // async handling of Watchers is complicated because watchers can trigger multiple times // and CompletionStage don't support this behavior // thenAccept() handles normal watcher triggering. watchedStage.thenAccept(event -> { System.out.println(event.getType()); System.out.println(event); // etc. }); // exceptionally is called if there is a connection problem in which case // watchers trigger to signal the connection problem. "reset()" must be called // to reset the watched stage watchedStage.exceptionally(exception -> { AsyncEventException asyncEx = (AsyncEventException)exception; asyncEx.printStackTrace(); // handle the error as needed handleWatchedStage(asyncEx.reset()); return null; }); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/cache/000077500000000000000000000000001442004423600250655ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/cache/CuratorCacheExample.java000066400000000000000000000076501442004423600316170ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package cache; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.CuratorCache; import org.apache.curator.framework.recipes.cache.CuratorCacheListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.TestingServer; import java.util.concurrent.ThreadLocalRandom; /** * Very simple example of creating a CuratorCache that listens to events and logs the changes * to standard out. A loop of random changes is run to exercise the cache. */ public class CuratorCacheExample { private static final String PATH = "/example/cache"; public static void main(String[] args) throws Exception { ThreadLocalRandom random = ThreadLocalRandom.current(); try (TestingServer server = new TestingServer()) { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(1000, 3))) { client.start(); try (CuratorCache cache = CuratorCache.build(client, PATH)) { // there are several ways to set a listener on a CuratorCache. You can watch for individual events // or for all events. Here, we'll use the builder to log individual cache actions CuratorCacheListener listener = CuratorCacheListener.builder() .forCreates(node -> System.out.println(String.format("Node created: [%s]", node))) .forChanges((oldNode, node) -> System.out.println(String.format("Node changed. Old: [%s] New: [%s]", oldNode, node))) .forDeletes(oldNode -> System.out.println(String.format("Node deleted. Old value: [%s]", oldNode))) .forInitialized(() -> System.out.println("Cache initialized")) .build(); // register the listener cache.listenable().addListener(listener); // the cache must be started cache.start(); // now randomly create/change/delete nodes for ( int i = 0; i < 1000; ++i ) { int depth = random.nextInt(1, 4); String path = makeRandomPath(random, depth); if ( random.nextBoolean() ) { client.create().orSetData().creatingParentsIfNeeded().forPath(path, Long.toString(random.nextLong()).getBytes()); } else { client.delete().quietly().deletingChildrenIfNeeded().forPath(path); } Thread.sleep(5); } } } } } private static String makeRandomPath(ThreadLocalRandom random, int depth) { if ( depth == 0 ) { return PATH; } return makeRandomPath(random, depth - 1) + "/" + random.nextInt(3); } }curator-apache-curator-5.5.0/curator-examples/src/main/java/cache/PathCacheExample.java000066400000000000000000000203601442004423600310650ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package cache; import com.google.common.collect.Lists; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.TestingServer; import org.apache.curator.utils.ZKPaths; import discovery.ExampleServer; import org.apache.zookeeper.KeeperException; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Arrays; import java.util.List; /** * An example of the PathChildrenCache. The example "harness" is a command processor * that allows adding/updating/removed nodes in a path. A PathChildrenCache keeps a * cache of these changes and outputs when updates occurs. */ public class PathCacheExample { private static final String PATH = "/example/cache"; public static void main(String[] args) throws Exception { TestingServer server = new TestingServer(); CuratorFramework client = null; PathChildrenCache cache = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(1000, 3)); client.start(); // in this example we will cache data. Notice that this is optional. cache = new PathChildrenCache(client, PATH, true); cache.start(); processCommands(client, cache); } finally { CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(server); } } private static void addListener(PathChildrenCache cache) { // a PathChildrenCacheListener is optional. Here, it's used just to log changes PathChildrenCacheListener listener = new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { switch ( event.getType() ) { case CHILD_ADDED: { System.out.println("Node added: " + ZKPaths.getNodeFromPath(event.getData().getPath())); break; } case CHILD_UPDATED: { System.out.println("Node changed: " + ZKPaths.getNodeFromPath(event.getData().getPath())); break; } case CHILD_REMOVED: { System.out.println("Node removed: " + ZKPaths.getNodeFromPath(event.getData().getPath())); break; } } } }; cache.getListenable().addListener(listener); } private static void processCommands(CuratorFramework client, PathChildrenCache cache) throws Exception { // More scaffolding that does a simple command line processor printHelp(); List servers = Lists.newArrayList(); try { addListener(cache); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); boolean done = false; while ( !done ) { System.out.print("> "); String line = in.readLine(); if ( line == null ) { break; } String command = line.trim(); String[] parts = command.split("\\s"); if ( parts.length == 0 ) { continue; } String operation = parts[0]; String args[] = Arrays.copyOfRange(parts, 1, parts.length); if ( operation.equalsIgnoreCase("help") || operation.equalsIgnoreCase("?") ) { printHelp(); } else if ( operation.equalsIgnoreCase("q") || operation.equalsIgnoreCase("quit") ) { done = true; } else if ( operation.equals("set") ) { setValue(client, command, args); } else if ( operation.equals("remove") ) { remove(client, command, args); } else if ( operation.equals("list") ) { list(cache); } Thread.sleep(1000); // just to allow the console output to catch up } } finally { for ( ExampleServer server : servers ) { CloseableUtils.closeQuietly(server); } } } private static void list(PathChildrenCache cache) { if ( cache.getCurrentData().size() == 0 ) { System.out.println("* empty *"); } else { for ( ChildData data : cache.getCurrentData() ) { System.out.println(data.getPath() + " = " + new String(data.getData())); } } } private static void remove(CuratorFramework client, String command, String[] args) throws Exception { if ( args.length != 1 ) { System.err.println("syntax error (expected remove ): " + command); return; } String name = args[0]; if ( name.contains("/") ) { System.err.println("Invalid node name" + name); return; } String path = ZKPaths.makePath(PATH, name); try { client.delete().forPath(path); } catch ( KeeperException.NoNodeException e ) { // ignore } } private static void setValue(CuratorFramework client, String command, String[] args) throws Exception { if ( args.length != 2 ) { System.err.println("syntax error (expected set ): " + command); return; } String name = args[0]; if ( name.contains("/") ) { System.err.println("Invalid node name" + name); return; } String path = ZKPaths.makePath(PATH, name); byte[] bytes = args[1].getBytes(); try { client.setData().forPath(path, bytes); } catch ( KeeperException.NoNodeException e ) { client.create().creatingParentContainersIfNeeded().forPath(path, bytes); } } private static void printHelp() { System.out.println("An example of using PathChildrenCache. This example is driven by entering commands at the prompt:\n"); System.out.println("set : Adds or updates a node with the given name"); System.out.println("remove : Deletes the node with the given name"); System.out.println("list: List the nodes/values in the cache"); System.out.println("quit: Quit the example"); System.out.println(); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/cache/TreeCacheExample.java000066400000000000000000000041421442004423600310700ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package cache; import framework.CreateClientExamples; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.TreeCache; import java.io.BufferedReader; import java.io.InputStreamReader; public class TreeCacheExample { public static void main(String[] args) throws Exception { CuratorFramework client = CreateClientExamples.createSimple("127.0.0.1:2181"); client.getUnhandledErrorListenable().addListener((message, e) -> { System.err.println("error=" + message); e.printStackTrace(); }); client.getConnectionStateListenable().addListener((c, newState) -> { System.out.println("state=" + newState); }); client.start(); TreeCache cache = TreeCache.newBuilder(client, "/").setCacheData(false).build(); cache.getListenable().addListener((c, event) -> { if ( event.getData() != null ) { System.out.println("type=" + event.getType() + " path=" + event.getData().getPath()); } else { System.out.println("type=" + event.getType()); } }); cache.start(); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); in.readLine(); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/discovery/000077500000000000000000000000001442004423600260315ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/discovery/DiscoveryExample.java000066400000000000000000000253031442004423600321620ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package discovery; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.TestingServer; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceProvider; import org.apache.curator.x.discovery.details.JsonInstanceSerializer; import org.apache.curator.x.discovery.strategies.RandomStrategy; import org.apache.zookeeper.KeeperException; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; public class DiscoveryExample { private static final String PATH = "/discovery/example"; public static void main(String[] args) throws Exception { // This method is scaffolding to get the example up and running TestingServer server = new TestingServer(); CuratorFramework client = null; ServiceDiscovery serviceDiscovery = null; Map> providers = Maps.newHashMap(); try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(1000, 3)); client.start(); JsonInstanceSerializer serializer = new JsonInstanceSerializer(InstanceDetails.class); serviceDiscovery = ServiceDiscoveryBuilder.builder(InstanceDetails.class).client(client).basePath(PATH).serializer(serializer).build(); serviceDiscovery.start(); processCommands(serviceDiscovery, providers, client); } finally { for ( ServiceProvider cache : providers.values() ) { CloseableUtils.closeQuietly(cache); } CloseableUtils.closeQuietly(serviceDiscovery); CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(server); } } private static void processCommands(ServiceDiscovery serviceDiscovery, Map> providers, CuratorFramework client) throws Exception { // More scaffolding that does a simple command line processor printHelp(); List servers = Lists.newArrayList(); try { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); boolean done = false; while ( !done ) { System.out.print("> "); String line = in.readLine(); if ( line == null ) { break; } String command = line.trim(); String[] parts = command.split("\\s"); if ( parts.length == 0 ) { continue; } String operation = parts[0]; String args[] = Arrays.copyOfRange(parts, 1, parts.length); if ( operation.equalsIgnoreCase("help") || operation.equalsIgnoreCase("?") ) { printHelp(); } else if ( operation.equalsIgnoreCase("q") || operation.equalsIgnoreCase("quit") ) { done = true; } else if ( operation.equals("add") ) { addInstance(args, client, command, servers); } else if ( operation.equals("delete") ) { deleteInstance(args, command, servers); } else if ( operation.equals("random") ) { listRandomInstance(args, serviceDiscovery, providers, command); } else if ( operation.equals("list") ) { listInstances(serviceDiscovery); } } } finally { for ( ExampleServer server : servers ) { CloseableUtils.closeQuietly(server); } } } private static void listRandomInstance(String[] args, ServiceDiscovery serviceDiscovery, Map> providers, String command) throws Exception { // this shows how to use a ServiceProvider // in a real application you'd create the ServiceProvider early for the service(s) you're interested in if ( args.length != 1 ) { System.err.println("syntax error (expected random ): " + command); return; } String serviceName = args[0]; ServiceProvider provider = providers.get(serviceName); if ( provider == null ) { provider = serviceDiscovery.serviceProviderBuilder().serviceName(serviceName).providerStrategy(new RandomStrategy()).build(); providers.put(serviceName, provider); provider.start(); Thread.sleep(2500); // give the provider time to warm up - in a real application you wouldn't need to do this } ServiceInstance instance = provider.getInstance(); if ( instance == null ) { System.err.println("No instances named: " + serviceName); } else { outputInstance(instance); } } private static void listInstances(ServiceDiscovery serviceDiscovery) throws Exception { // This shows how to query all the instances in service discovery try { Collection serviceNames = serviceDiscovery.queryForNames(); System.out.println(serviceNames.size() + " type(s)"); for ( String serviceName : serviceNames ) { Collection> instances = serviceDiscovery.queryForInstances(serviceName); System.out.println(serviceName); for ( ServiceInstance instance : instances ) { outputInstance(instance); } } } catch ( KeeperException.NoNodeException e ) { System.err.println("There are no registered instances."); } finally { CloseableUtils.closeQuietly(serviceDiscovery); } } private static void outputInstance(ServiceInstance instance) { System.out.println("\t" + instance.getPayload().getDescription() + ": " + instance.buildUriSpec()); } private static void deleteInstance(String[] args, String command, List servers) { // simulate a random instance going down // in a real application, this would occur due to normal operation, a crash, maintenance, etc. if ( args.length != 1 ) { System.err.println("syntax error (expected delete ): " + command); return; } final String serviceName = args[0]; ExampleServer server = Iterables.find ( servers, new Predicate() { @Override public boolean apply(ExampleServer server) { return server.getThisInstance().getName().endsWith(serviceName); } }, null ); if ( server == null ) { System.err.println("No servers found named: " + serviceName); return; } servers.remove(server); CloseableUtils.closeQuietly(server); System.out.println("Removed a random instance of: " + serviceName); } private static void addInstance(String[] args, CuratorFramework client, String command, List servers) throws Exception { // simulate a new instance coming up // in a real application, this would be a separate process if ( args.length < 2 ) { System.err.println("syntax error (expected add ): " + command); return; } StringBuilder description = new StringBuilder(); for ( int i = 1; i < args.length; ++i ) { if ( i > 1 ) { description.append(' '); } description.append(args[i]); } String serviceName = args[0]; ExampleServer server = new ExampleServer(client, PATH, serviceName, description.toString()); servers.add(server); server.start(); System.out.println(serviceName + " added"); } private static void printHelp() { System.out.println("An example of using the ServiceDiscovery APIs. This example is driven by entering commands at the prompt:\n"); System.out.println("add : Adds a mock service with the given name and description"); System.out.println("delete : Deletes one of the mock services with the given name"); System.out.println("list: Lists all the currently registered services"); System.out.println("random : Lists a random instance of the service with the given name"); System.out.println("quit: Quit the example"); System.out.println(); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/discovery/ExampleServer.java000066400000000000000000000061471442004423600314660ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package discovery; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.UriSpec; import org.apache.curator.x.discovery.details.JsonInstanceSerializer; import java.io.Closeable; import java.io.IOException; /** * This shows a very simplified method of registering an instance with the service discovery. Each individual * instance in your distributed set of applications would create an instance of something similar to ExampleServer, * start it when the application comes up and close it when the application shuts down. */ public class ExampleServer implements Closeable { private final ServiceDiscovery serviceDiscovery; private final ServiceInstance thisInstance; public ExampleServer(CuratorFramework client, String path, String serviceName, String description) throws Exception { // in a real application, you'd have a convention of some kind for the URI layout UriSpec uriSpec = new UriSpec("{scheme}://foo.com:{port}"); thisInstance = ServiceInstance.builder() .name(serviceName) .payload(new InstanceDetails(description)) .port((int)(65535 * Math.random())) // in a real application, you'd use a common port .uriSpec(uriSpec) .build(); // if you mark your payload class with @JsonRootName the provided JsonInstanceSerializer will work JsonInstanceSerializer serializer = new JsonInstanceSerializer(InstanceDetails.class); serviceDiscovery = ServiceDiscoveryBuilder.builder(InstanceDetails.class) .client(client) .basePath(path) .serializer(serializer) .thisInstance(thisInstance) .build(); } public ServiceInstance getThisInstance() { return thisInstance; } public void start() throws Exception { serviceDiscovery.start(); } @Override public void close() throws IOException { CloseableUtils.closeQuietly(serviceDiscovery); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/discovery/InstanceDetails.java000066400000000000000000000026571442004423600317600ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package discovery; import com.fasterxml.jackson.annotation.JsonRootName; /** * In a real application, the Service payload will most likely * be more detailed than this. But, this gives a good example. */ @JsonRootName("details") public class InstanceDetails { private String description; public InstanceDetails() { this(""); } public InstanceDetails(String description) { this.description = description; } public void setDescription(String description) { this.description = description; } public String getDescription() { return description; } } curator-apache-curator-5.5.0/curator-examples/src/main/java/framework/000077500000000000000000000000001442004423600260175ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/framework/CreateClientExamples.java000066400000000000000000000044541442004423600327320ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package framework; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; public class CreateClientExamples { public static CuratorFramework createSimple(String connectionString) { // these are reasonable arguments for the ExponentialBackoffRetry. The first // retry will wait 1 second - the second will wait up to 2 seconds - the // third will wait up to 4 seconds. ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3); // The simplest way to get a CuratorFramework instance. This will use default values. // The only required arguments are the connection string and the retry policy return CuratorFrameworkFactory.newClient(connectionString, retryPolicy); } public static CuratorFramework createWithOptions(String connectionString, RetryPolicy retryPolicy, int connectionTimeoutMs, int sessionTimeoutMs) { // using the CuratorFrameworkFactory.builder() gives fine grained control // over creation options. See the CuratorFrameworkFactory.Builder javadoc // details return CuratorFrameworkFactory.builder() .connectString(connectionString) .retryPolicy(retryPolicy) .connectionTimeoutMs(connectionTimeoutMs) .sessionTimeoutMs(sessionTimeoutMs) // etc. etc. .build(); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/framework/CrudExamples.java000066400000000000000000000212251442004423600312600ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package framework; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorListener; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Watcher; import java.util.List; public class CrudExamples { public static void create(CuratorFramework client, String path, byte[] payload) throws Exception { // this will create the given ZNode with the given data client.create().forPath(path, payload); } public static void createEphemeral(CuratorFramework client, String path, byte[] payload) throws Exception { // this will create the given EPHEMERAL ZNode with the given data client.create().withMode(CreateMode.EPHEMERAL).forPath(path, payload); } public static String createEphemeralSequential(CuratorFramework client, String path, byte[] payload) throws Exception { // this will create the given EPHEMERAL-SEQUENTIAL ZNode with the given data using Curator protection. /* Protection Mode: It turns out there is an edge case that exists when creating sequential-ephemeral nodes. The creation can succeed on the server, but the server can crash before the created node name is returned to the client. However, the ZK session is still valid so the ephemeral node is not deleted. Thus, there is no way for the client to determine what node was created for them. Even without sequential-ephemeral, however, the create can succeed on the sever but the client (for various reasons) will not know it. Putting the create builder into protection mode works around this. The name of the node that is created is prefixed with a GUID. If node creation fails the normal retry mechanism will occur. On the retry, the parent path is first searched for a node that has the GUID in it. If that node is found, it is assumed to be the lost node that was successfully created on the first try and is returned to the caller. */ return client.create().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, payload); } public static void createIdempotent(CuratorFramework client, String path, byte[] payload) throws Exception { /* * This will create the given ZNode with the given data idempotently, meaning that if the initial create * failed transiently, it will be retried and behave as if the first create never happened, even if the * first create actually succeeded on the server but the client didn't know it. */ client.create().idempotent().forPath(path, payload); } public static void setData(CuratorFramework client, String path, byte[] payload) throws Exception { // set data for the given node client.setData().forPath(path, payload); } public static void setDataAsync(CuratorFramework client, String path, byte[] payload) throws Exception { // this is one method of getting event/async notifications CuratorListener listener = new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { // examine event for details } }; client.getCuratorListenable().addListener(listener); // set data for the given node asynchronously. The completion notification // is done via the CuratorListener. client.setData().inBackground().forPath(path, payload); } public static void setDataAsyncWithCallback(CuratorFramework client, BackgroundCallback callback, String path, byte[] payload) throws Exception { // this is another method of getting notification of an async completion client.setData().inBackground(callback).forPath(path, payload); } public static void setDataIdempotent(CuratorFramework client, String path, byte[] payload, int currentVersion) throws Exception { /* * This will set the given ZNode with the given data idempotently, meaning that if the initial setData * failed transiently, it will be retried and behave as if the first setData never happened, even if the * first setData actually succeeded on the server but the client didn't know it. * In other words, if currentVersion == X and payload = P, this will return success if the znode ends * up in the state (version == X+1 && data == P). * If withVersion is not specified, it will end up with success so long as the data == P, no matter the znode version. */ client.setData().idempotent().withVersion(currentVersion).forPath(path, payload); client.setData().idempotent().forPath(path, payload); } public static void delete(CuratorFramework client, String path) throws Exception { // delete the given node client.delete().forPath(path); } public static void guaranteedDelete(CuratorFramework client, String path) throws Exception { // delete the given node and guarantee that it completes /* Guaranteed Delete Solves this edge case: deleting a node can fail due to connection issues. Further, if the node was ephemeral, the node will not get auto-deleted as the session is still valid. This can wreak havoc with lock implementations. When guaranteed is set, Curator will record failed node deletions and attempt to delete them in the background until successful. NOTE: you will still get an exception when the deletion fails. But, you can be assured that as long as the CuratorFramework instance is open attempts will be made to delete the node. */ client.delete().guaranteed().forPath(path); } public static void deleteIdempotent(CuratorFramework client, String path, int currentVersion) throws Exception { /* * This will delete the given ZNode with the given data idempotently, meaning that if the initial delete * failed transiently, it will be retried and behave as if the first delete never happened, even if the * first delete actually succeeded on the server but the client didn't know it. * In other words, if currentVersion == X, this will return success if the znode ends up deleted, and will retry after * connection loss if the version the znode's version is still X. * If withVersion is not specified, it will end up successful so long as the node is deleted eventually. * Kind of like guaranteed but not in the background. * For deletes this is equivalent to the older quietly() behavior, but it is also provided under idempotent() for compatibility with Create/SetData. */ client.delete().idempotent().withVersion(currentVersion).forPath(path); client.delete().idempotent().forPath(path); client.delete().quietly().withVersion(currentVersion).forPath(path); client.delete().quietly().forPath(path); } public static List watchedGetChildren(CuratorFramework client, String path) throws Exception { /** * Get children and set a watcher on the node. The watcher notification will come through the * CuratorListener (see setDataAsync() above). */ return client.getChildren().watched().forPath(path); } public static List watchedGetChildren(CuratorFramework client, String path, Watcher watcher) throws Exception { /** * Get children and set the given watcher on the node. */ return client.getChildren().usingWatcher(watcher).forPath(path); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/framework/TransactionExamples.java000066400000000000000000000040111442004423600326420ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package framework; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.TestingServer; import java.util.Collection; public class TransactionExamples { public static Collection transaction(CuratorFramework client) throws Exception { // this example shows how to use ZooKeeper's transactions CuratorOp createOp = client.transactionOp().create().forPath("/a/path", "some data".getBytes()); CuratorOp setDataOp = client.transactionOp().setData().forPath("/another/path", "other data".getBytes()); CuratorOp deleteOp = client.transactionOp().delete().forPath("/yet/another/path"); Collection results = client.transaction().forOperations(createOp, setDataOp, deleteOp); for ( CuratorTransactionResult result : results ) { System.out.println(result.getForPath() + " - " + result.getType()); } return results; } } curator-apache-curator-5.5.0/curator-examples/src/main/java/leader/000077500000000000000000000000001442004423600252565ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/leader/ExampleClient.java000066400000000000000000000064601442004423600306610ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package leader; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter; import org.apache.curator.framework.recipes.leader.LeaderSelector; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * An example leader selector client. Note that {@link LeaderSelectorListenerAdapter} which * has the recommended handling for connection state issues */ public class ExampleClient extends LeaderSelectorListenerAdapter implements Closeable { private final String name; private final LeaderSelector leaderSelector; private final AtomicInteger leaderCount = new AtomicInteger(); public ExampleClient(CuratorFramework client, String path, String name) { this.name = name; // create a leader selector using the given path for management // all participants in a given leader selection must use the same path // ExampleClient here is also a LeaderSelectorListener but this isn't required leaderSelector = new LeaderSelector(client, path, this); // for most cases you will want your instance to requeue when it relinquishes leadership leaderSelector.autoRequeue(); } public void start() throws IOException { // the selection for this instance doesn't start until the leader selector is started // leader selection is done in the background so this call to leaderSelector.start() returns immediately leaderSelector.start(); } @Override public void close() throws IOException { leaderSelector.close(); } @Override public void takeLeadership(CuratorFramework client) throws Exception { // we are now the leader. This method should not return until we want to relinquish leadership final int waitSeconds = (int)(5 * Math.random()) + 1; System.out.println(name + " is now the leader. Waiting " + waitSeconds + " seconds..."); System.out.println(name + " has been leader " + leaderCount.getAndIncrement() + " time(s) before."); try { Thread.sleep(TimeUnit.SECONDS.toMillis(waitSeconds)); } catch ( InterruptedException e ) { System.err.println(name + " was interrupted."); Thread.currentThread().interrupt(); } finally { System.out.println(name + " relinquishing leadership.\n"); } } } curator-apache-curator-5.5.0/curator-examples/src/main/java/leader/LeaderSelectorExample.java000066400000000000000000000060261442004423600323360ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package leader; import com.google.common.collect.Lists; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.TestingServer; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.List; public class LeaderSelectorExample { private static final int CLIENT_QTY = 10; private static final String PATH = "/examples/leader"; public static void main(String[] args) throws Exception { // all of the useful sample code is in ExampleClient.java System.out.println("Create " + CLIENT_QTY + " clients, have each negotiate for leadership and then wait a random number of seconds before letting another leader election occur."); System.out.println("Notice that leader election is fair: all clients will become leader and will do so the same number of times."); List clients = Lists.newArrayList(); List examples = Lists.newArrayList(); TestingServer server = new TestingServer(); try { for ( int i = 0; i < CLIENT_QTY; ++i ) { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(1000, 3)); clients.add(client); ExampleClient example = new ExampleClient(client, PATH, "Client #" + i); examples.add(example); client.start(); example.start(); } System.out.println("Press enter/return to quit\n"); new BufferedReader(new InputStreamReader(System.in)).readLine(); } finally { System.out.println("Shutting down..."); for ( ExampleClient exampleClient : examples ) { CloseableUtils.closeQuietly(exampleClient); } for ( CuratorFramework client : clients ) { CloseableUtils.closeQuietly(client); } CloseableUtils.closeQuietly(server); } } } curator-apache-curator-5.5.0/curator-examples/src/main/java/locking/000077500000000000000000000000001442004423600254505ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/locking/ExampleClientThatLocks.java000066400000000000000000000036431442004423600326700ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package locking; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import java.util.concurrent.TimeUnit; public class ExampleClientThatLocks { private final InterProcessMutex lock; private final FakeLimitedResource resource; private final String clientName; public ExampleClientThatLocks(CuratorFramework client, String lockPath, FakeLimitedResource resource, String clientName) { this.resource = resource; this.clientName = clientName; lock = new InterProcessMutex(client, lockPath); } public void doWork(long time, TimeUnit unit) throws Exception { if ( !lock.acquire(time, unit) ) { throw new IllegalStateException(clientName + " could not acquire the lock"); } try { System.out.println(clientName + " has the lock"); resource.use(); } finally { System.out.println(clientName + " releasing the lock"); lock.release(); // always release the lock in a finally block } } } curator-apache-curator-5.5.0/curator-examples/src/main/java/locking/FakeLimitedResource.java000066400000000000000000000030101442004423600321730ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package locking; import java.util.concurrent.atomic.AtomicBoolean; /** * Simulates some external resource that can only be access by one process at a time */ public class FakeLimitedResource { private final AtomicBoolean inUse = new AtomicBoolean(false); public void use() throws InterruptedException { // in a real application this would be accessing/manipulating a shared resource if ( !inUse.compareAndSet(false, true) ) { throw new IllegalStateException("Needs to be used by one client at a time"); } try { Thread.sleep((long)(3 * Math.random())); } finally { inUse.set(false); } } } curator-apache-curator-5.5.0/curator-examples/src/main/java/locking/LockingExample.java000066400000000000000000000071661442004423600312270ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package locking; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.TestingServer; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class LockingExample { private static final int QTY = 5; private static final int REPETITIONS = QTY * 10; private static final String PATH = "/examples/locks"; public static void main(String[] args) throws Exception { // all of the useful sample code is in ExampleClientThatLocks.java // FakeLimitedResource simulates some external resource that can only be access by one process at a time final FakeLimitedResource resource = new FakeLimitedResource(); ExecutorService service = Executors.newFixedThreadPool(QTY); final TestingServer server = new TestingServer(); try { for ( int i = 0; i < QTY; ++i ) { final int index = i; Callable task = new Callable() { @Override public Void call() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(1000, 3)); try { client.start(); ExampleClientThatLocks example = new ExampleClientThatLocks(client, PATH, resource, "Client " + index); for ( int j = 0; j < REPETITIONS; ++j ) { example.doWork(10, TimeUnit.SECONDS); } } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } catch ( Exception e ) { e.printStackTrace(); // log or do something } finally { CloseableUtils.closeQuietly(client); } return null; } }; service.submit(task); } service.shutdown(); service.awaitTermination(10, TimeUnit.MINUTES); } finally { CloseableUtils.closeQuietly(server); } } } curator-apache-curator-5.5.0/curator-examples/src/main/java/modeled/000077500000000000000000000000001442004423600254335ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/modeled/ContainerType.java000066400000000000000000000031011442004423600310550ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package modeled; public class ContainerType { private final int typeId; public ContainerType() { this(0); } public ContainerType(int typeId) { this.typeId = typeId; } public int getTypeId() { return typeId; } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } ContainerType that = (ContainerType)o; return typeId == that.typeId; } @Override public int hashCode() { return typeId; } @Override public String toString() { return "ContainerType{" + "typeId=" + typeId + '}'; } } curator-apache-curator-5.5.0/curator-examples/src/main/java/modeled/ModeledCuratorExamples.java000066400000000000000000000055251442004423600327150ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package modeled; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.JacksonModelSerializer; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ZPath; import java.util.function.Consumer; public class ModeledCuratorExamples { public static ModeledFramework wrap(AsyncCuratorFramework client) { JacksonModelSerializer serializer = JacksonModelSerializer.build(PersonModel.class); // build a model specification - you can pre-build all the model specifications for your app at startup ModelSpec modelSpec = ModelSpec.builder(ZPath.parse("/example/path"), serializer).build(); // wrap a CuratorFramework instance so that it can be used "modeled". // do this once and re-use the returned ModeledFramework instance. // ModeledFramework instances are tied to a given path return ModeledFramework.wrap(client, modelSpec); } public static void createOrUpdate(ModeledFramework modeled, PersonModel model) { // change the affected path to be modeled's base path plus id: i.e. "/example/path/{id}" ModeledFramework atId = modeled.child(model.getId().getId()); // by default ModeledFramework instances update the node if it already exists // so this will either create or update the node atId.set(model); // note - this is async } public static void readPerson(ModeledFramework modeled, String id, Consumer receiver) { // read the person with the given ID and asynchronously call the receiver after it is read modeled.child(id).read().whenComplete((person, exception) -> { if ( exception != null ) { exception.printStackTrace(); // handle the error } else { receiver.accept(person); } }); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/modeled/ModeledCuratorExamplesAlt.java000066400000000000000000000040601442004423600333470ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package modeled; import org.apache.curator.x.async.modeled.ModeledFramework; import java.util.function.Consumer; public class ModeledCuratorExamplesAlt { public static void createOrUpdate(PersonModelSpec modelSpec, PersonModel model) { // change the affected path to be modeled's base path plus id: i.e. "/example/path/{id}" ModeledFramework resolved = modelSpec.resolved(model.getContainerType(), model.getId()); // by default ModeledFramework instances update the node if it already exists // so this will either create or update the node resolved.set(model); // note - this is async } public static void readPerson(PersonModelSpec modelSpec, ContainerType containerType, PersonId id, Consumer receiver) { ModeledFramework resolved = modelSpec.resolved(containerType, id); // read the person with the given ID and asynchronously call the receiver after it is read resolved.read().whenComplete((person, exception) -> { if ( exception != null ) { exception.printStackTrace(); // handle the error } else { receiver.accept(person); } }); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/modeled/PersonId.java000066400000000000000000000031511442004423600300210ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package modeled; import java.util.Objects; public class PersonId { private final String id; public PersonId() { this(""); } public PersonId(String id) { this.id = Objects.requireNonNull(id, "id cannot be null"); } public String getId() { return id; } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } PersonId personId = (PersonId)o; return id.equals(personId.id); } @Override public int hashCode() { return id.hashCode(); } @Override public String toString() { return "PersonId{" + "id='" + id + '\'' + '}'; } } curator-apache-curator-5.5.0/curator-examples/src/main/java/modeled/PersonModel.java000066400000000000000000000063021442004423600305260ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package modeled; import java.util.Objects; public class PersonModel { private final PersonId id; private final ContainerType containerType; private final String firstName; private final String lastName; private final int age; public PersonModel() { this(new PersonId(), new ContainerType(), null, null, 0); } public PersonModel(PersonId id, ContainerType containerType, String firstName, String lastName, int age) { this.id = Objects.requireNonNull(id, "id cannot be null"); this.containerType = Objects.requireNonNull(containerType, "containerType cannot be null"); this.firstName = Objects.requireNonNull(firstName, "firstName cannot be null"); this.lastName = Objects.requireNonNull(lastName, "lastName cannot be null"); this.age = age; } public PersonId getId() { return id; } public ContainerType getContainerType() { return containerType; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } PersonModel that = (PersonModel)o; if ( age != that.age ) { return false; } if ( !id.equals(that.id) ) { return false; } if ( !containerType.equals(that.containerType) ) { return false; } //noinspection SimplifiableIfStatement if ( !firstName.equals(that.firstName) ) { return false; } return lastName.equals(that.lastName); } @Override public int hashCode() { int result = id.hashCode(); result = 31 * result + containerType.hashCode(); result = 31 * result + firstName.hashCode(); result = 31 * result + lastName.hashCode(); result = 31 * result + age; return result; } @Override public String toString() { return "PersonModel{" + "id=" + id + ", containerType=" + containerType + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", age=" + age + '}'; } } curator-apache-curator-5.5.0/curator-examples/src/main/java/modeled/PersonModelSpec.java000066400000000000000000000035041442004423600313420ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package modeled; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.JacksonModelSerializer; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ZPath; public class PersonModelSpec { private final AsyncCuratorFramework client; private final ModelSpec modelSpec; public PersonModelSpec(AsyncCuratorFramework client) { this.client = client; JacksonModelSerializer serializer = JacksonModelSerializer.build(PersonModel.class); ZPath path = ZPath.parseWithIds("/example/{id}/path/{id}"); modelSpec = ModelSpec.builder(path, serializer).build(); } public ModeledFramework resolved(ContainerType containerType, PersonId personId) { ModelSpec resolved = modelSpec.resolved(containerType.getTypeId(), personId.getId()); return ModeledFramework.wrap(client, resolved); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/000077500000000000000000000000001442004423600253225ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/Clients.java000066400000000000000000000055171442004423600275760ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub; import org.apache.curator.x.async.modeled.JacksonModelSerializer; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.typed.TypedModeledFramework; import org.apache.curator.x.async.modeled.typed.TypedModeledFramework2; import org.apache.zookeeper.CreateMode; import pubsub.messages.LocationAvailable; import pubsub.messages.UserCreated; import pubsub.models.Group; import pubsub.models.Instance; import pubsub.models.InstanceType; import pubsub.models.Priority; import java.util.concurrent.TimeUnit; public class Clients { /** * A client template for LocationAvailable instances */ public static final TypedModeledFramework2 locationAvailableClient = TypedModeledFramework2.from( ModeledFramework.builder(), builder(LocationAvailable.class), "/root/pubsub/messages/locations/{group}/{priority}/{id}" ); /** * A client template for UserCreated instances */ public static final TypedModeledFramework2 userCreatedClient = TypedModeledFramework2.from( ModeledFramework.builder(), builder(UserCreated.class), "/root/pubsub/messages/users/{group}/{priority}/{id}" ); /** * A client template for Instance instances */ public static final TypedModeledFramework instanceClient = TypedModeledFramework.from( ModeledFramework.builder(), builder(Instance.class), "/root/pubsub/instances/{instance-type}/{id}" ); private static ModelSpecBuilder builder(Class clazz) { return ModelSpec.builder(JacksonModelSerializer.build(clazz)) .withTtl(TimeUnit.MINUTES.toMillis(10)) // for our pub-sub example, messages are valid for 10 minutes .withCreateMode(CreateMode.PERSISTENT_WITH_TTL) ; } private Clients() { } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/Publisher.java000066400000000000000000000123041442004423600301220ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.typed.TypedModeledFramework2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pubsub.messages.LocationAvailable; import pubsub.messages.UserCreated; import pubsub.models.Group; import pubsub.models.Instance; import pubsub.models.Message; import pubsub.models.Priority; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; public class Publisher { private final Logger log = LoggerFactory.getLogger(getClass()); private final AsyncCuratorFramework client; public Publisher(AsyncCuratorFramework client) { this.client = Objects.requireNonNull(client, "client cannot be null"); } /** * Publish the given instance using the Instance client template * * @param instance instance to publish */ public void publishInstance(Instance instance) { ModeledFramework resolvedClient = Clients.instanceClient.resolved(client, instance.getType()); resolvedClient.set(instance).exceptionally(e -> { log.error("Could not publish instance: " + instance, e); return null; }); } /** * Publish the given instances using the Instance client template in a transaction * * @param instances instances to publish */ public void publishInstances(List instances) { List operations = instances.stream() .map(instance -> Clients.instanceClient .resolved(client, instance.getType()) .createOp(instance) ) .collect(Collectors.toList()); client.transaction().forOperations(operations).exceptionally(e -> { log.error("Could not publish instances: " + instances, e); return null; }); } /** * Publish the given LocationAvailable using the LocationAvailable client template * * @param group group * @param locationAvailable message to publish */ public void publishLocationAvailable(Group group, LocationAvailable locationAvailable) { publishMessage(Clients.locationAvailableClient, group, locationAvailable); } /** * Publish the given UserCreated using the UserCreated client template * * @param group group * @param userCreated message to publish */ public void publishUserCreated(Group group, UserCreated userCreated) { publishMessage(Clients.userCreatedClient, group, userCreated); } /** * Publish the given LocationAvailables using the LocationAvailable client template in a transaction * * @param group group * @param locationsAvailable messages to publish */ public void publishLocationsAvailable(Group group, List locationsAvailable) { publishMessages(Clients.locationAvailableClient, group, locationsAvailable); } /** * Publish the given UserCreateds using the UserCreated client template in a transaction * * @param group group * @param usersCreated messages to publish */ public void publishUsersCreated(Group group, List usersCreated) { publishMessages(Clients.userCreatedClient, group, usersCreated); } private void publishMessage(TypedModeledFramework2 typedClient, Group group, T message) { ModeledFramework resolvedClient = typedClient.resolved(client, group, message.getPriority()); resolvedClient.set(message).exceptionally(e -> { log.error("Could not publish message: " + message, e); return null; }); } private void publishMessages(TypedModeledFramework2 typedClient, Group group, List messages) { List operations = messages.stream() .map(message -> typedClient .resolved(client, group, message.getPriority()) .createOp(message) ) .collect(Collectors.toList()); client.transaction().forOperations(operations).exceptionally(e -> { log.error("Could not publish messages: " + messages, e); return null; }); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/README.md000066400000000000000000000120041442004423600265760ustar00rootroot00000000000000# Pub-Sub Example This example models a publish and subscribe system (note: it is not meant for production) using the strongly typed modeled APIs in Apache Curator. ## Design Notes In this example, there are three models that can be published: `Instance`, `LocationAvailable` and `UserCreated`. Instances have an `InstanceType`; LocationAvailable and UserCreated both have a `Priority` and are associated with a `Group`. (Note: these names/objects are meant for illustrative purposes only and are completely contrived) Each model is stored at a unique path in ZooKeeper: * Instance: `/root/pubsub/instances/TYPE/ID` * LocationAvailable: `/root/pubsub/messages/locations/GROUP/PRIORITY/ID` * UserCreated: `/root/pubsub/messages/users/GROUP/PRIORITY/ID` All models are stored using a TTL so that they automatically get deleted after 10 minutes. ## Clients This example uses the "typed" models (`TypedModelSpec`, etc.). The typed paths, models and clients are meant to be created early in your application and re-used as needed. Thus, you can model your ZooKeeper usage and the rest of your application can use them without worrying about correct paths, types, etc. `TypedModeledFramework` is a template that produces a `ModeledFramework` by applying parameters to the `TypedZPath` in the contained `TypedModelSpec`. Curator provides variants that accept from 1 to 10 parameters (`TypedModeledFramework`, `TypedModeledFramework2`, `TypedModeledFramework3`, etc.). In this example, the TypedModeledFrameworks are defined in `Clients.java`. E.g. ``` public static final TypedModeledFramework2 locationAvailableClient = TypedModeledFramework2.from( ModeledFramework.builder(), builder(LocationAvailable.class), "/root/pubsub/messages/locations/{group}/{priority}" ); ``` ## Publisher `Publisher.java` shows how to use the ModeledFramework to write models. There are methods to write single instances and to write lists of instances in a transaction. Each publish method resolves the appropriate typed client and then calls its `set()` method with the given model. ## Subscriber `Subscriber.java` uses CachedModeledFrameworks to listen for changes on the parent nodes for all of the models in this example. Each of the methods resolves the appropriate typed client and then starts the cache (via `cached()`). ## SubPubTest `SubPubTest.java` is a class that exercises this example. * `start()` uses `Subscriber` to start a `CachedModeledFramework` for each combination of the Instance + InstanceType, LocationAvailable + Group + Priority, and UserCreated + Group + Priority. It then adds a simple listener to each cache that merely prints the class name and path whenever an update occurs (see `generalListener()`). * `start()` also starts a scheduled task that runs every second. This task calls `publishSomething()` * `publishSomething()` randomly publishes either a single Instance, LocationAvailable, UserCreated or a list of those. `SubPubTest.java` has a `main()` method. When you run you should see something similar to this: ``` Publishing 9 instances Subscribed Instance @ /root/pubsub/instances/proxy/1 Subscribed Instance @ /root/pubsub/instances/web/2 Subscribed Instance @ /root/pubsub/instances/cache/4 Subscribed Instance @ /root/pubsub/instances/proxy/9 Subscribed Instance @ /root/pubsub/instances/database/3 Subscribed Instance @ /root/pubsub/instances/cache/5 Subscribed Instance @ /root/pubsub/instances/database/6 Subscribed Instance @ /root/pubsub/instances/cache/7 Subscribed Instance @ /root/pubsub/instances/cache/8 Publishing 1 userCreated Subscribed UserCreated @ /root/pubsub/messages/users/main/high/10 Publishing 9 locationsAvailable Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/low/11 Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/12 Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/13 Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/14 Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/16 Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/high/15 Subscribed LocationAvailable @ /root/pubsub/messages/locations/admin/medium/17 ... ``` It runs for 1 minute and then exits. curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/SubPubTest.java000066400000000000000000000217061442004423600302330ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.TestingServer; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.cached.CachedModeledFramework; import org.apache.curator.x.async.modeled.cached.ModeledCacheListener; import pubsub.messages.LocationAvailable; import pubsub.messages.UserCreated; import pubsub.models.Group; import pubsub.models.Instance; import pubsub.models.InstanceType; import pubsub.models.Priority; import java.io.Closeable; import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.IntStream; public class SubPubTest implements Closeable { private final TestingServer testingServer; private final AsyncCuratorFramework client; private final ScheduledExecutorService executorService; private final List> instanceSubscribers = new ArrayList<>(); private final List> locationAvailableSubscribers = new ArrayList<>(); private final List> userCreatedSubscribers = new ArrayList<>(); private static final AtomicLong nextId = new AtomicLong(1); // arrays of random values used for this example private static final Group[] groups = {new Group("main"), new Group("admin")}; private static final String[] hostnames = {"host1", "host2", "host3"}; private static final Integer[] ports = {80, 443, 9999}; private static final String[] locations = {"dc1", "dc2", "eu", "us"}; private static final Duration[] durations = {Duration.ofSeconds(1), Duration.ofMinutes(1), Duration.ofHours(1)}; private static final String[] positions = {"worker", "manager", "executive"}; public static void main(String[] args) { try ( SubPubTest subPubTest = new SubPubTest() ) { subPubTest.start(); TimeUnit.MINUTES.sleep(1); // run the test for a minute then exit } catch ( Exception e ) { e.printStackTrace(); } } public SubPubTest() throws Exception { this.testingServer = new TestingServer(); client = AsyncCuratorFramework.wrap(CuratorFrameworkFactory.newClient(testingServer.getConnectString(), new RetryOneTime(1))); executorService = Executors.newSingleThreadScheduledExecutor(); } public void start() { client.unwrap().start(); Publisher publisher = new Publisher(client); Subscriber subscriber = new Subscriber(client); // start a subscriber/cache for Instances of each InstanceType instanceSubscribers.addAll( Arrays.stream(InstanceType.values()) .map(subscriber::startInstanceSubscriber) .collect(Collectors.toList()) ); // start a subscriber/cache for LocationAvailables of each combination of Group and Priority locationAvailableSubscribers.addAll( Arrays.stream(Priority.values()) .flatMap(priority -> Arrays.stream(groups).map(group -> subscriber.startLocationAvailableSubscriber(group, priority))) .collect(Collectors.toList()) ); // start a subscriber/cache for UserCreateds of each combination of Group and Priority userCreatedSubscribers.addAll( Arrays.stream(Priority.values()) .flatMap(priority -> Arrays.stream(groups).map(group -> subscriber.startUserCreatedSubscriber(group, priority))) .collect(Collectors.toList()) ); // add listeners for each of the caches instanceSubscribers.forEach(s -> s.listenable().addListener(generalListener())); locationAvailableSubscribers.forEach(s -> s.listenable().addListener(generalListener())); userCreatedSubscribers.forEach(s -> s.listenable().addListener(generalListener())); // schedule the publisher task once a second executorService.scheduleAtFixedRate(() -> publishSomething(publisher), 1, 1, TimeUnit.SECONDS); } @Override public void close() throws IOException { executorService.shutdownNow(); try { executorService.awaitTermination(5, TimeUnit.SECONDS); } catch ( InterruptedException ignore ) { Thread.currentThread().interrupt(); } userCreatedSubscribers.forEach(CachedModeledFramework::close); locationAvailableSubscribers.forEach(CachedModeledFramework::close); instanceSubscribers.forEach(CachedModeledFramework::close); client.unwrap().close(); testingServer.close(); } private void publishSomething(Publisher publisher) { // randomly do some publishing - either single items or lists of items in a transaction switch ( ThreadLocalRandom.current().nextInt(6) ) { case 0: { Instance instance = new Instance(nextId(), random(InstanceType.values()), random(hostnames), random(ports)); System.out.println("Publishing 1 instance"); publisher.publishInstance(instance); break; } case 1: { List instances = IntStream.range(1, 10) .mapToObj(__ -> new Instance(nextId(), random(InstanceType.values()), random(hostnames), random(ports))) .collect(Collectors.toList()); System.out.println(String.format("Publishing %d instances", instances.size())); publisher.publishInstances(instances); break; } case 2: { LocationAvailable locationAvailable = new LocationAvailable(nextId(), random(Priority.values()), random(locations), random(durations)); System.out.println("Publishing 1 locationAvailable"); publisher.publishLocationAvailable(random(groups), locationAvailable); break; } case 3: { List locationsAvailable = IntStream.range(1, 10) .mapToObj(__ -> new LocationAvailable(nextId(), random(Priority.values()), random(locations), random(durations))) .collect(Collectors.toList()); System.out.println(String.format("Publishing %d locationsAvailable", locationsAvailable.size())); publisher.publishLocationsAvailable(random(groups), locationsAvailable); break; } case 4: { UserCreated userCreated = new UserCreated(nextId(), random(Priority.values()), random(locations), random(positions)); System.out.println("Publishing 1 userCreated"); publisher.publishUserCreated(random(groups), userCreated); break; } case 5: { List usersCreated = IntStream.range(1, 10) .mapToObj(__ -> new UserCreated(nextId(), random(Priority.values()), random(locations), random(positions))) .collect(Collectors.toList()); System.out.println(String.format("Publishing %d usersCreated", usersCreated.size())); publisher.publishUsersCreated(random(groups), usersCreated); break; } } } private ModeledCacheListener generalListener() { return (type, path, stat, model) -> System.out.println(String.format("Subscribed %s @ %s", model.getClass().getSimpleName(), path)); } @SafeVarargs private final T random(T... tab) { int index = ThreadLocalRandom.current().nextInt(tab.length); return tab[index]; } private String nextId() { return Long.toString(nextId.getAndIncrement()); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/Subscriber.java000066400000000000000000000062341442004423600302750ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.cached.CachedModeledFramework; import org.apache.curator.x.async.modeled.typed.TypedModeledFramework2; import pubsub.messages.LocationAvailable; import pubsub.messages.UserCreated; import pubsub.models.Group; import pubsub.models.Instance; import pubsub.models.InstanceType; import pubsub.models.Message; import pubsub.models.Priority; public class Subscriber { private final AsyncCuratorFramework client; public Subscriber(AsyncCuratorFramework client) { this.client = client; } /** * Start a subscriber (a CachedModeledFramework instance) using the LocationAvailable client template * * @param group group to listen for * @param priority priority to listen for * @return CachedModeledFramework instance (already started) */ public CachedModeledFramework startLocationAvailableSubscriber(Group group, Priority priority) { return startSubscriber(Clients.locationAvailableClient, group, priority); } /** * Start a subscriber (a CachedModeledFramework instance) using the UserCreated client template * * @param group group to listen for * @param priority priority to listen for * @return CachedModeledFramework instance (already started) */ public CachedModeledFramework startUserCreatedSubscriber(Group group, Priority priority) { return startSubscriber(Clients.userCreatedClient, group, priority); } /** * Start a subscriber (a CachedModeledFramework instance) using the Instance client template * * @param instanceType type to listen for * @return CachedModeledFramework instance (already started) */ public CachedModeledFramework startInstanceSubscriber(InstanceType instanceType) { CachedModeledFramework resolved = Clients.instanceClient.resolved(client, instanceType).cached(); resolved.start(); return resolved; } private CachedModeledFramework startSubscriber(TypedModeledFramework2 typedClient, Group group, Priority priority) { CachedModeledFramework resolved = typedClient.resolved(client, group, priority).cached(); resolved.start(); return resolved; } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/messages/000077500000000000000000000000001442004423600271315ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/messages/LocationAvailable.java000066400000000000000000000036541442004423600333550ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub.messages; import pubsub.models.Message; import pubsub.models.Priority; import java.time.Duration; import java.util.Objects; public class LocationAvailable extends Message { private final String name; private final Duration availableUntil; public LocationAvailable() { this(Priority.low, "", Duration.ZERO); } public LocationAvailable(Priority priority, String name, Duration availableUntil) { super(priority); this.name = Objects.requireNonNull(name, "name cannot be null"); this.availableUntil = Objects.requireNonNull(availableUntil, "availableUntil cannot be null"); } public LocationAvailable(String id, Priority priority, String name, Duration availableUntil) { super(id, priority); this.name = Objects.requireNonNull(name, "name cannot be null"); this.availableUntil = Objects.requireNonNull(availableUntil, "availableUntil cannot be null"); } @Override public String toString() { return "LocationAvailable{" + "name='" + name + '\'' + ", availableUntil=" + availableUntil + "} " + super.toString(); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/messages/UserCreated.java000066400000000000000000000036531442004423600322110ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub.messages; import pubsub.models.Message; import pubsub.models.Priority; import java.util.Objects; public class UserCreated extends Message { private final String name; private final String position; public UserCreated() { this(Priority.low, "",""); } public UserCreated(Priority priority, String name, String position) { super(priority); this.name = Objects.requireNonNull(name, "name cannot be null"); this.position = Objects.requireNonNull(position, "position cannot be null"); } public UserCreated(String id, Priority priority, String name, String position) { super(id, priority); this.name = Objects.requireNonNull(name, "name cannot be null"); this.position = Objects.requireNonNull(position, "position cannot be null"); } public String getName() { return name; } public String getPosition() { return position; } @Override public String toString() { return "UserCreated{" + "name='" + name + '\'' + ", position='" + position + '\'' + "} " + super.toString(); } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/models/000077500000000000000000000000001442004423600266055ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/models/Group.java000066400000000000000000000023451442004423600305500ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub.models; import org.apache.curator.x.async.modeled.NodeName; public class Group implements NodeName { private final String groupName; public Group() { this(""); } public Group(String groupName) { this.groupName = groupName; } public String getGroupName() { return groupName; } @Override public String nodeName() { return groupName; } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/models/Instance.java000066400000000000000000000040271442004423600312170ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub.models; import org.apache.curator.x.async.modeled.NodeName; import java.util.Objects; import java.util.UUID; public class Instance implements NodeName { private final String id; private final InstanceType type; private final String hostname; private final int port; public Instance() { this(UUID.randomUUID().toString(), InstanceType.proxy, "", 0); } public Instance(String id, InstanceType type, String hostname, int port) { this.id = Objects.requireNonNull(id, "id cannot be null"); this.type = Objects.requireNonNull(type, "type cannot be null"); this.hostname = Objects.requireNonNull(hostname, "hostname cannot be null"); this.port = port; } public String getId() { return id; } public InstanceType getType() { return type; } public String getHostname() { return hostname; } public int getPort() { return port; } @Override public String nodeName() { return id; } @Override public String toString() { return "Instance{" + "id='" + id + '\'' + ", type=" + type + ", hostname='" + hostname + '\'' + ", port=" + port + '}'; } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/models/InstanceType.java000066400000000000000000000016121442004423600320560ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub.models; public enum InstanceType { database, cache, web, proxy } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/models/Message.java000066400000000000000000000034341442004423600310400ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub.models; import org.apache.curator.x.async.modeled.NodeName; import java.util.Objects; import java.util.UUID; public abstract class Message implements NodeName { private final String id; private final Priority priority; protected Message() { this(UUID.randomUUID().toString(), Priority.low); } protected Message(Priority priority) { this(UUID.randomUUID().toString(), priority); } protected Message(String id, Priority priority) { this.id = Objects.requireNonNull(id, "id cannot be null"); this.priority = Objects.requireNonNull(priority, "messageType cannot be null"); } public String getId() { return id; } public Priority getPriority() { return priority; } @Override public String nodeName() { return id; } @Override public String toString() { return "Message{" + "id='" + id + '\'' + ", priority=" + priority + '}'; } } curator-apache-curator-5.5.0/curator-examples/src/main/java/pubsub/models/Priority.java000066400000000000000000000015701442004423600312740ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package pubsub.models; public enum Priority { low, medium, high } curator-apache-curator-5.5.0/curator-examples/src/main/resources/000077500000000000000000000000001442004423600251135ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/main/resources/log4j.properties000066400000000000000000000017551442004423600302600ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. log4j.rootLogger=ERROR, console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n curator-apache-curator-5.5.0/curator-examples/src/site/000077500000000000000000000000001442004423600231215ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/site/confluence/000077500000000000000000000000001442004423600252425ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-examples/src/site/confluence/index.confluence000066400000000000000000000011411442004423600304110ustar00rootroot00000000000000h1. Examples This module contains example usages of various Curator features. Each directory in the module is a separate example. |/leader|Example leader selector code| |/cache|Example CuratorCache usage| |/locking|Example of using InterProcessMutex| |/discovery|Example usage of the Curator's ServiceDiscovery| |/framework|A few examples of how to use the CuratorFramework class| |/async|Example AsyncCuratorFramework code| |/modeled|ModeledFramework and Modeled Cache examples| See the [examples source repo|https://github.com/apache/curator/tree/master/curator-examples/src/main/java] for each example. curator-apache-curator-5.5.0/curator-examples/src/site/site.xml000066400000000000000000000026441442004423600246150ustar00rootroot00000000000000 ]]> curator-apache-curator-5.5.0/curator-framework/000077500000000000000000000000001442004423600215455ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/LICENSE000066400000000000000000000261361442004423600225620ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. curator-apache-curator-5.5.0/curator-framework/NOTICE000066400000000000000000000002501442004423600224460ustar00rootroot00000000000000Apache Curator Copyright 2013-2023 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). curator-apache-curator-5.5.0/curator-framework/pom.xml000066400000000000000000000100111442004423600230530ustar00rootroot00000000000000 4.0.0 org.apache.curator apache-curator 5.5.0 curator-framework 5.5.0 bundle Curator Framework High-level API that greatly simplifies using ZooKeeper. 2011 org.apache.zookeeper.*;version="[3.4,4.0)", * org.apache.curator.framework.*;version="${project.version}";-noimport:=true org.apache.curator curator-client org.apache.curator curator-test test org.assertj assertj-core test com.fasterxml.jackson.core jackson-core provided com.fasterxml.jackson.core jackson-databind provided com.fasterxml.jackson.dataformat jackson-dataformat-yaml ${jackson-version} test org.junit.jupiter junit-jupiter-api test org.slf4j slf4j-log4j12 test org.mockito mockito-core test org.awaitility awaitility test maven-jar-plugin test-jar curator-apache-curator-5.5.0/curator-framework/src/000077500000000000000000000000001442004423600223345ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/000077500000000000000000000000001442004423600232605ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/000077500000000000000000000000001442004423600242015ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/000077500000000000000000000000001442004423600247705ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/000077500000000000000000000000001442004423600262115ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/000077500000000000000000000000001442004423600276705ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/000077500000000000000000000000001442004423600316655ustar00rootroot00000000000000AuthInfo.java000066400000000000000000000025461442004423600341750ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework; import java.util.Arrays; public class AuthInfo { final String scheme; final byte[] auth; public AuthInfo(String scheme, byte[] auth) { this.scheme = scheme; this.auth = auth; } public String getScheme() { return scheme; } public byte[] getAuth() { return auth; } @Override public String toString() { return "AuthInfo{" + "scheme='" + scheme + '\'' + ", auth=" + Arrays.toString(auth) + '}'; } } CuratorFramework.java000066400000000000000000000257011442004423600357530ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.framework.api.*; import org.apache.curator.framework.api.transaction.CuratorMultiTransaction; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.CuratorTransaction; import org.apache.curator.framework.api.transaction.TransactionOp; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.schema.SchemaSet; import org.apache.curator.framework.state.ConnectionStateErrorPolicy; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.EnsurePath; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import java.io.Closeable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; /** * Zookeeper framework-style client */ public interface CuratorFramework extends Closeable { /** * Start the client. Most mutator methods will not work until the client is started */ public void start(); /** * Stop the client */ public void close(); /** * Returns the state of this instance * * @return state */ public CuratorFrameworkState getState(); /** * Return true if the client is started, not closed, etc. * * @return true/false * @deprecated use {@link #getState()} instead */ @Deprecated public boolean isStarted(); /** * Start a create builder * * @return builder object */ public CreateBuilder create(); /** * Start a delete builder * * @return builder object */ public DeleteBuilder delete(); /** * Start an exists builder *

* The builder will return a Stat object as if org.apache.zookeeper.ZooKeeper.exists() were called. Thus, a null * means that it does not exist and an actual Stat object means it does exist. * * @return builder object */ public ExistsBuilder checkExists(); /** * Start a get data builder * * @return builder object */ public GetDataBuilder getData(); /** * Start a set data builder * * @return builder object */ public SetDataBuilder setData(); /** * Start a get children builder * * @return builder object */ public GetChildrenBuilder getChildren(); /** * Start a get ACL builder * * @return builder object */ public GetACLBuilder getACL(); /** * Start a set ACL builder * * @return builder object */ public SetACLBuilder setACL(); /** * Start a reconfig builder * * @return builder object */ public ReconfigBuilder reconfig(); /** * Start a getConfig builder * * @return builder object */ public GetConfigBuilder getConfig(); /** * Start a transaction builder * * @return builder object * @deprecated use {@link #transaction()} instead */ public CuratorTransaction inTransaction(); /** * Start a transaction builder * * @return builder object */ public CuratorMultiTransaction transaction(); /** * Allocate an operation that can be used with {@link #transaction()}. * NOTE: {@link CuratorOp} instances created by this builder are * reusable. * * @return operation builder */ public TransactionOp transactionOp(); /** * Perform a sync on the given path - syncs are always in the background * * @param path the path * @param backgroundContextObject optional context * @deprecated use {@link #sync()} instead */ @Deprecated public void sync(String path, Object backgroundContextObject); /** * Create all nodes in the specified path as containers if they don't * already exist * * @param path path to create * @throws Exception errors */ public void createContainers(String path) throws Exception; /** * Start a sync builder. Note: sync is ALWAYS in the background even * if you don't use one of the background() methods * * @return builder object */ public SyncBuilder sync(); /** * Start a remove watches builder. * * @return builder object * @deprecated use {@link #watchers()} in ZooKeeper 3.6+ */ public RemoveWatchesBuilder watches(); /** * Start a watch builder. Supported only when ZooKeeper JAR of version 3.6 or * above is used, throws {@code IllegalStateException} for ZooKeeper JAR 3.5 or below * * @return builder object * @throws IllegalStateException ZooKeeper JAR is 3.5 or below */ public WatchesBuilder watchers(); /** * Returns the listenable interface for the Connect State * * @return listenable */ public Listenable getConnectionStateListenable(); /** * Returns the listenable interface for events * * @return listenable */ public Listenable getCuratorListenable(); /** * Returns the listenable interface for unhandled errors * * @return listenable */ public Listenable getUnhandledErrorListenable(); /** * Returns a facade of the current instance that does _not_ automatically * pre-pend the namespace to all paths * * @return facade * @deprecated Since 2.9.0 - use {@link #usingNamespace} passing null */ @Deprecated public CuratorFramework nonNamespaceView(); /** * Returns a facade of the current instance that uses the specified namespace * or no namespace if newNamespace is null. * * @param newNamespace the new namespace or null for none * @return facade */ public CuratorFramework usingNamespace(String newNamespace); /** * Return the current namespace or "" if none * * @return namespace */ public String getNamespace(); /** * Return the managed zookeeper client * * @return client */ public CuratorZookeeperClient getZookeeperClient(); /** * Allocates an ensure path instance that is namespace aware * * @param path path to ensure * @return new EnsurePath instance * @deprecated Since 2.9.0 - prefer {@link CreateBuilder#creatingParentContainersIfNeeded()}, {@link ExistsBuilder#creatingParentContainersIfNeeded()} * or {@link CuratorFramework#createContainers(String)} */ @Deprecated public EnsurePath newNamespaceAwareEnsurePath(String path); /** * Curator can hold internal references to watchers that may inhibit garbage collection. * Call this method on watchers you are no longer interested in. * * @param watcher the watcher * * @deprecated As of ZooKeeper 3.5 Curators recipes will handle removing watcher references * when they are no longer used. If you write your own recipe, follow the example of Curator * recipes and use {@link #newWatcherRemoveCuratorFramework} calling {@link WatcherRemoveCuratorFramework#removeWatchers()} * when closing your instance. */ @Deprecated public void clearWatcherReferences(Watcher watcher); /** * Block until a connection to ZooKeeper is available or the maxWaitTime has been exceeded * @param maxWaitTime The maximum wait time. * 1. {@code value <= 0} and {@code units != null} to return immediately; * 2. {@code value <= 0} and {@code units == null} to wait indefinitely, * which is same as {@link #blockUntilConnected()}. * @param units The time units for the maximum wait time. * @return True if connection has been established, false otherwise. * @throws InterruptedException If interrupted while waiting */ public boolean blockUntilConnected(int maxWaitTime, TimeUnit units) throws InterruptedException; /** * Block until a connection to ZooKeeper is available. This method will not return until a * connection is available, or it is interrupted, in which case an InterruptedException will * be thrown * @throws InterruptedException If interrupted while waiting */ public void blockUntilConnected() throws InterruptedException; /** * Returns a facade of the current instance that tracks * watchers created and allows a one-shot removal of all watchers * via {@link WatcherRemoveCuratorFramework#removeWatchers()} * * @return facade */ public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework(); /** * Return the configured error policy * * @return error policy */ public ConnectionStateErrorPolicy getConnectionStateErrorPolicy(); /** * Current maintains a cached view of the Zookeeper quorum config. * * @return the current config */ public QuorumVerifier getCurrentConfig(); /** * Return this instance's schema set * * @return schema set */ SchemaSet getSchemaSet(); /** * Calls {@link #notifyAll()} on the given object after first synchronizing on it. This is * done from the {@link #runSafe(Runnable)} thread. * * @param monitorHolder object to sync on and notify * @return a CompletableFuture that can be used to monitor when the call is complete * @since 4.1.0 */ default CompletableFuture postSafeNotify(Object monitorHolder) { return runSafe(() -> { synchronized(monitorHolder) { monitorHolder.notifyAll(); } }); } /** * Curator (and user) recipes can use this to run notifyAll * and other blocking calls that might normally block ZooKeeper's event thread. * @param runnable proc to call from a safe internal thread * @return a CompletableFuture that can be used to monitor when the call is complete * @since 4.1.0 */ CompletableFuture runSafe(Runnable runnable); } CuratorFrameworkFactory.java000066400000000000000000000602501442004423600373010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.RetryPolicy; import org.apache.curator.ensemble.EnsembleProvider; import org.apache.curator.ensemble.fixed.FixedEnsembleProvider; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.api.CompressionProvider; import org.apache.curator.framework.api.CreateBuilder; import org.apache.curator.framework.api.PathAndBytesable; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.CuratorTempFrameworkImpl; import org.apache.curator.framework.imps.DefaultACLProvider; import org.apache.curator.framework.imps.GzipCompressionProvider; import org.apache.curator.framework.schema.SchemaSet; import org.apache.curator.framework.state.ConnectionStateErrorPolicy; import org.apache.curator.framework.state.ConnectionStateListenerManagerFactory; import org.apache.curator.framework.state.StandardConnectionStateErrorPolicy; import org.apache.curator.utils.DefaultZookeeperFactory; import org.apache.curator.utils.ZookeeperFactory; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; /** * Factory methods for creating framework-style clients */ public class CuratorFrameworkFactory { private static final int DEFAULT_SESSION_TIMEOUT_MS = Integer.getInteger("curator-default-session-timeout", 60 * 1000); private static final int DEFAULT_CONNECTION_TIMEOUT_MS = Integer.getInteger("curator-default-connection-timeout", 15 * 1000); private static final byte[] LOCAL_ADDRESS = getLocalAddress(); private static final CompressionProvider DEFAULT_COMPRESSION_PROVIDER = new GzipCompressionProvider(); private static final DefaultZookeeperFactory DEFAULT_ZOOKEEPER_FACTORY = new DefaultZookeeperFactory(); private static final DefaultACLProvider DEFAULT_ACL_PROVIDER = new DefaultACLProvider(); private static final long DEFAULT_INACTIVE_THRESHOLD_MS = (int)TimeUnit.MINUTES.toMillis(3); private static final int DEFAULT_CLOSE_WAIT_MS = (int)TimeUnit.SECONDS.toMillis(1); private static final boolean DEFAULT_WITH_ENSEMBLE_TRACKER = true; /** * Return a new builder that builds a CuratorFramework * * @return new builder */ public static Builder builder() { return new Builder(); } /** * Create a new client with default session timeout and default connection timeout * * @param connectString list of servers to connect to * @param retryPolicy retry policy to use * @return client */ public static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy) { return newClient(connectString, DEFAULT_SESSION_TIMEOUT_MS, DEFAULT_CONNECTION_TIMEOUT_MS, retryPolicy); } /** * Create a new client * * @param connectString list of servers to connect to * @param sessionTimeoutMs session timeout * @param connectionTimeoutMs connection timeout * @param retryPolicy retry policy to use * @return client */ public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy) { return builder(). connectString(connectString). sessionTimeoutMs(sessionTimeoutMs). connectionTimeoutMs(connectionTimeoutMs). retryPolicy(retryPolicy). build(); } /** * Create a new client * * @param connectString list of servers to connect to * @param sessionTimeoutMs session timeout * @param connectionTimeoutMs connection timeout * @param retryPolicy retry policy to use * @param zkClientConfig ZKClientConfig * @return client * * @since 5.1.1, supported from ZooKeeper 3.6.1 and above. */ public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy, ZKClientConfig zkClientConfig) { return builder(). connectString(connectString). sessionTimeoutMs(sessionTimeoutMs). connectionTimeoutMs(connectionTimeoutMs). retryPolicy(retryPolicy). zkClientConfig(zkClientConfig). build(); } /** * Return the local address as bytes that can be used as a node payload * * @return local address bytes */ public static byte[] getLocalAddress() { try { return InetAddress.getLocalHost().getHostAddress().getBytes(); } catch ( UnknownHostException ignore ) { // ignore } return new byte[0]; } public static class Builder { private EnsembleProvider ensembleProvider; private boolean withEnsembleTracker = DEFAULT_WITH_ENSEMBLE_TRACKER; private int sessionTimeoutMs = DEFAULT_SESSION_TIMEOUT_MS; private int connectionTimeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS; private int maxCloseWaitMs = DEFAULT_CLOSE_WAIT_MS; private RetryPolicy retryPolicy; private ThreadFactory threadFactory = null; private String namespace; private List authInfos = null; private byte[] defaultData = LOCAL_ADDRESS; private CompressionProvider compressionProvider = DEFAULT_COMPRESSION_PROVIDER; private ZookeeperFactory zookeeperFactory = DEFAULT_ZOOKEEPER_FACTORY; private ACLProvider aclProvider = DEFAULT_ACL_PROVIDER; private boolean canBeReadOnly = false; private boolean useContainerParentsIfAvailable = true; private ConnectionStateErrorPolicy connectionStateErrorPolicy = new StandardConnectionStateErrorPolicy(); private SchemaSet schemaSet = SchemaSet.getDefaultSchemaSet(); private int waitForShutdownTimeoutMs = 0; private Executor runSafeService = null; private ConnectionStateListenerManagerFactory connectionStateListenerManagerFactory = ConnectionStateListenerManagerFactory.standard; private int simulatedSessionExpirationPercent = 100; private ZKClientConfig zkClientConfig; /** * Apply the current values and build a new CuratorFramework * * @return new CuratorFramework */ public CuratorFramework build() { return new CuratorFrameworkImpl(this); } /** * Apply the current values and build a new temporary CuratorFramework. Temporary * CuratorFramework instances are meant for single requests to ZooKeeper ensembles * over a failure prone network such as a WAN. The APIs available from {@link CuratorTempFramework} * are limited. Further, the connection will be closed after 3 minutes of inactivity. * * @return temp instance */ public CuratorTempFramework buildTemp() { return buildTemp(DEFAULT_INACTIVE_THRESHOLD_MS, TimeUnit.MILLISECONDS); } /** * Apply the current values and build a new temporary CuratorFramework. Temporary * CuratorFramework instances are meant for single requests to ZooKeeper ensembles * over a failure prone network such as a WAN. The APIs available from {@link CuratorTempFramework} * are limited. Further, the connection will be closed after inactiveThresholdMs milliseconds of inactivity. * * @param inactiveThreshold number of milliseconds of inactivity to cause connection close * @param unit threshold unit * @return temp instance */ public CuratorTempFramework buildTemp(long inactiveThreshold, TimeUnit unit) { return new CuratorTempFrameworkImpl(this, unit.toMillis(inactiveThreshold)); } /** * Add connection authorization * * Subsequent calls to this method overwrite the prior calls. * * @param scheme the scheme * @param auth the auth bytes * @return this */ public Builder authorization(String scheme, byte[] auth) { return authorization(ImmutableList.of(new AuthInfo(scheme, (auth != null) ? Arrays.copyOf(auth, auth.length) : null))); } /** * Add connection authorization. The supplied authInfos are appended to those added via call to * {@link #authorization(java.lang.String, byte[])} for backward compatibility. *

* Subsequent calls to this method overwrite the prior calls. * * @param authInfos list of {@link AuthInfo} objects with scheme and auth * @return this */ public Builder authorization(List authInfos) { this.authInfos = ImmutableList.copyOf(authInfos); return this; } /** * Set the list of servers to connect to. IMPORTANT: use either this or {@link #ensembleProvider(EnsembleProvider)} * but not both. * * @param connectString list of servers to connect to * @return this */ public Builder connectString(String connectString) { ensembleProvider = new FixedEnsembleProvider(connectString); return this; } /** * Set the list ensemble provider. IMPORTANT: use either this or {@link #connectString(String)} * but not both. * * @param ensembleProvider the ensemble provider to use * @return this */ public Builder ensembleProvider(EnsembleProvider ensembleProvider) { this.ensembleProvider = ensembleProvider; return this; } /** * Allows to configure if the ensemble configuration changes will be watched. * The default value is {@code true}.
* * IMPORTANT: Use this method in combination with {@link #ensembleProvider(EnsembleProvider)} to provide * an instance that returns {@code false} on {@link EnsembleProvider#updateServerListEnabled()} in order * to fully achieve that ensemble server list changes are ignored
* * @param withEnsembleTracker use {@code false} if you want to avoid following ensemble configuration changes * @return this */ public Builder ensembleTracker(boolean withEnsembleTracker) { this.withEnsembleTracker = withEnsembleTracker; return this; } /** * @return {@code true} if ensemble configuration changes MUST be watched */ public boolean withEnsembleTracker() { return withEnsembleTracker; } /** * Sets the data to use when {@link PathAndBytesable#forPath(String)} is used. * This is useful for debugging purposes. For example, you could set this to be the IP of the * client. * * @param defaultData new default data to use * @return this */ public Builder defaultData(byte[] defaultData) { this.defaultData = (defaultData != null) ? Arrays.copyOf(defaultData, defaultData.length) : null; return this; } /** * As ZooKeeper is a shared space, users of a given cluster should stay within * a pre-defined namespace. If a namespace is set here, all paths will get pre-pended * with the namespace * * @param namespace the namespace * @return this */ public Builder namespace(String namespace) { this.namespace = namespace; return this; } /** * @param sessionTimeoutMs session timeout * @return this */ public Builder sessionTimeoutMs(int sessionTimeoutMs) { this.sessionTimeoutMs = sessionTimeoutMs; return this; } /** * @param connectionTimeoutMs connection timeout * @return this */ public Builder connectionTimeoutMs(int connectionTimeoutMs) { this.connectionTimeoutMs = connectionTimeoutMs; return this; } /** * @param maxCloseWaitMs time to wait during close to join background threads * @return this */ public Builder maxCloseWaitMs(int maxCloseWaitMs) { this.maxCloseWaitMs = maxCloseWaitMs; return this; } /** * @param retryPolicy retry policy to use * @return this */ public Builder retryPolicy(RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; return this; } /** * @param threadFactory thread factory used to create Executor Services * @return this */ public Builder threadFactory(ThreadFactory threadFactory) { this.threadFactory = threadFactory; return this; } /** * @param compressionProvider the compression provider * @return this */ public Builder compressionProvider(CompressionProvider compressionProvider) { this.compressionProvider = compressionProvider; return this; } /** * @param zookeeperFactory the zookeeper factory to use * @return this */ public Builder zookeeperFactory(ZookeeperFactory zookeeperFactory) { this.zookeeperFactory = zookeeperFactory; return this; } /** * @param aclProvider a provider for ACLs * @return this */ public Builder aclProvider(ACLProvider aclProvider) { this.aclProvider = aclProvider; return this; } /** * @param canBeReadOnly if true, allow ZooKeeper client to enter * read only mode in case of a network partition. See * {@link ZooKeeper#ZooKeeper(String, int, Watcher, long, byte[], boolean)} * for details * @return this */ public Builder canBeReadOnly(boolean canBeReadOnly) { this.canBeReadOnly = canBeReadOnly; return this; } /** * By default, Curator uses {@link CreateBuilder#creatingParentContainersIfNeeded()} * if the ZK JAR supports {@link CreateMode#CONTAINER}. Call this method to turn off this behavior. * * @return this */ public Builder dontUseContainerParents() { this.useContainerParentsIfAvailable = false; return this; } /** * Set the error policy to use. The default is {@link StandardConnectionStateErrorPolicy} * * @since 3.0.0 * @param connectionStateErrorPolicy new error policy * @return this */ public Builder connectionStateErrorPolicy(ConnectionStateErrorPolicy connectionStateErrorPolicy) { this.connectionStateErrorPolicy = connectionStateErrorPolicy; return this; } /** * Set a timeout for {@link CuratorZookeeperClient#close(int)} }. * The default is 0, which means that this feature is disabled. * * @since 4.0.2 * @param waitForShutdownTimeoutMs default timeout * @return this */ public Builder waitForShutdownTimeoutMs(int waitForShutdownTimeoutMs) { this.waitForShutdownTimeoutMs = waitForShutdownTimeoutMs; return this; } /** *

* Prior to 3.0.0, Curator did not try to manage session expiration * other than the functionality provided by ZooKeeper itself. Starting with * 3.0.0, Curator has the option of attempting to monitor session expiration * above what is provided by ZooKeeper. The percentage set by this method * determines how and if Curator will check for session expiration. *

* *

* The default percentage is 100. *

* *

* If it is set to 0, Curator does not do any additional checking * for session expiration. *

* *

* If a positive number is set, Curator will check for session expiration * as follows: when ZooKeeper sends a Disconnect event, Curator will start a timer. * If re-connection is not achieved before the elapsed time exceeds the negotiated * session time multiplied by the session expiration percent, Curator will simulate * a session expiration. Due to timing/network issues, it is not possible for * a client to match the server's session timeout with complete accuracy. Thus, the need * for a session expiration percentage. *

* * @param simulatedSessionExpirationPercent new simulated session expiration percentage * @return this * @since 5.0 */ public Builder simulatedSessionExpirationPercent(int simulatedSessionExpirationPercent) { Preconditions.checkArgument( (simulatedSessionExpirationPercent > 0) && (simulatedSessionExpirationPercent <= 100), "simulatedSessionExpirationPercent must be > 0 and <= 100"); this.simulatedSessionExpirationPercent = simulatedSessionExpirationPercent; return this; } public Builder zkClientConfig(ZKClientConfig zkClientConfig) { this.zkClientConfig = zkClientConfig; return this; } /** * Add an enforced schema set * * @param schemaSet the schema set * @return this * @since 3.2.0 */ public Builder schemaSet(SchemaSet schemaSet) { this.schemaSet = schemaSet; return this; } /** * Curator (and user) recipes will use this executor to call notifyAll * and other blocking calls that might normally block ZooKeeper's event thread. * By default, an executor is allocated internally using the provided (or default) * {@link #threadFactory(java.util.concurrent.ThreadFactory)}. Use this method * to set a custom executor. * * @param runSafeService executor to use for calls to notifyAll from Watcher callbacks etc * @return this * @since 4.1.0 */ public Builder runSafeService(Executor runSafeService) { this.runSafeService = runSafeService; return this; } /** * Sets the connection state listener manager factory. For example, * you can set {@link org.apache.curator.framework.state.ConnectionStateListenerManagerFactory#circuitBreaking(org.apache.curator.RetryPolicy)} * * @param connectionStateListenerManagerFactory manager factory to use * @return this * @since 4.2.0 */ public Builder connectionStateListenerManagerFactory(ConnectionStateListenerManagerFactory connectionStateListenerManagerFactory) { this.connectionStateListenerManagerFactory = Objects.requireNonNull(connectionStateListenerManagerFactory, "connectionStateListenerManagerFactory cannot be null"); return this; } public Executor getRunSafeService() { return runSafeService; } public ACLProvider getAclProvider() { return aclProvider; } public ZookeeperFactory getZookeeperFactory() { return zookeeperFactory; } public CompressionProvider getCompressionProvider() { return compressionProvider; } public ThreadFactory getThreadFactory() { return threadFactory; } public EnsembleProvider getEnsembleProvider() { return ensembleProvider; } public int getSessionTimeoutMs() { return sessionTimeoutMs; } public int getConnectionTimeoutMs() { return connectionTimeoutMs; } public int getWaitForShutdownTimeoutMs() { return waitForShutdownTimeoutMs; } public int getMaxCloseWaitMs() { return maxCloseWaitMs; } public RetryPolicy getRetryPolicy() { return retryPolicy; } public String getNamespace() { return namespace; } public boolean useContainerParentsIfAvailable() { return useContainerParentsIfAvailable; } public ConnectionStateErrorPolicy getConnectionStateErrorPolicy() { return connectionStateErrorPolicy; } public int getSimulatedSessionExpirationPercent() { return simulatedSessionExpirationPercent; } public ZKClientConfig getZkClientConfig() { return zkClientConfig; } public SchemaSet getSchemaSet() { return schemaSet; } @Deprecated public String getAuthScheme() { int qty = (authInfos != null) ? authInfos.size() : 0; switch ( qty ) { case 0: { return null; } case 1: { return authInfos.get(0).scheme; } default: { throw new IllegalStateException("More than 1 auth has been added"); } } } @Deprecated public byte[] getAuthValue() { int qty = (authInfos != null) ? authInfos.size() : 0; switch ( qty ) { case 0: { return null; } case 1: { byte[] bytes = authInfos.get(0).getAuth(); return (bytes != null) ? Arrays.copyOf(bytes, bytes.length) : null; } default: { throw new IllegalStateException("More than 1 auth has been added"); } } } public List getAuthInfos() { return authInfos; } public byte[] getDefaultData() { return defaultData; } public boolean canBeReadOnly() { return canBeReadOnly; } public ConnectionStateListenerManagerFactory getConnectionStateListenerManagerFactory() { return connectionStateListenerManagerFactory; } private Builder() { } } private CuratorFrameworkFactory() { } } CuratorTempFramework.java000066400000000000000000000040131442004423600365720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework; import org.apache.curator.framework.api.TempGetDataBuilder; import org.apache.curator.framework.api.transaction.CuratorTransaction; import java.io.Closeable; /** *

* Temporary CuratorFramework instances are meant for single requests to ZooKeeper ensembles * over a failure prone network such as a WAN. The APIs available from CuratorTempFramework * are limited. Further, the connection will be closed after a period of inactivity. *

* *

* Based on an idea mentioned in a post by Camille Fournier: * http://whilefalse.blogspot.com/2012/12/building-global-highly-available.html *

*/ public interface CuratorTempFramework extends Closeable { /** * Stop the client */ public void close(); /** * Start a transaction builder * * @return builder object * @throws Exception errors */ public CuratorTransaction inTransaction() throws Exception; /** * Start a get data builder * * @return builder object * @throws Exception errors */ public TempGetDataBuilder getData() throws Exception; } EnsureContainers.java000066400000000000000000000040411442004423600357370ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework; import java.util.concurrent.atomic.AtomicBoolean; /** * Similar to {@link org.apache.curator.utils.EnsurePath} but creates containers. * */ public class EnsureContainers { private final CuratorFramework client; private final String path; private final AtomicBoolean ensureNeeded = new AtomicBoolean(true); /** * @param client the client * @param path path to ensure is containers */ public EnsureContainers(CuratorFramework client, String path) { this.client = client; this.path = path; } /** * The first time this method is called, all nodes in the * path will be created as containers if needed * * @throws Exception errors */ public void ensure() throws Exception { if ( ensureNeeded.get() ) { internalEnsure(); } } /** * Reset so that the next call to {@link #ensure()} will attempt to create containers */ public void reset() { ensureNeeded.set(true); } private synchronized void internalEnsure() throws Exception { if ( ensureNeeded.compareAndSet(true, false) ) { client.createContainers(path); } } } WatcherRemoveCuratorFramework.java000066400000000000000000000022271442004423600404450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework; import org.apache.zookeeper.Watcher; /** * A CuratorFramework facade that tracks watchers created and allows a one-shot removal of all watchers */ public interface WatcherRemoveCuratorFramework extends CuratorFramework { /** * Remove all outstanding watchers that have been set */ void removeWatchers(); } curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/000077500000000000000000000000001442004423600324365ustar00rootroot00000000000000ACLBackgroundPathAndBytesable.java000066400000000000000000000017461442004423600407240ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ACLBackgroundPathAndBytesable extends ParentACLable>, BackgroundPathAndBytesable { } ACLCreateModeBackgroundPathAndBytesable.java000066400000000000000000000020311442004423600426410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ACLCreateModeBackgroundPathAndBytesable extends ACLBackgroundPathAndBytesable, BackgroundPathAndBytesable, CreateModable> { } ACLCreateModePathAndBytesable.java000066400000000000000000000017301442004423600406460ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ACLCreateModePathAndBytesable extends ACLPathAndBytesable, CreateModable> { } ACLCreateModeStatBackgroundPathAndBytesable.java000077500000000000000000000020111442004423600434760ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ACLCreateModeStatBackgroundPathAndBytesable extends ACLCreateModeBackgroundPathAndBytesable, Statable> { } ACLPathAndBytesable.java000066400000000000000000000017101442004423600367130ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ACLPathAndBytesable extends ParentACLable>, PathAndBytesable { } ACLProvider.java000066400000000000000000000026641442004423600353440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.curator.utils.InternalACLProvider; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import java.util.List; public interface ACLProvider extends InternalACLProvider { /** * Return the ACL list to use by default (usually {@link ZooDefs.Ids#OPEN_ACL_UNSAFE}). * * @return default ACL list */ public List getDefaultAcl(); /** * Return the ACL list to use for the given path * * @param path path (NOTE: might be null) * @return ACL list */ public List getAclForPath(String path); } ACLable.java000066400000000000000000000022261442004423600344470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import java.util.List; public interface ACLable { /** * Set an ACL list (default is {@link ZooDefs.Ids#OPEN_ACL_UNSAFE}) * * @param aclList the ACL list to use * @return this */ T withACL(List aclList); } ACLableExistBuilderMain.java000066400000000000000000000017071442004423600376030ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ACLableExistBuilderMain extends ExistsBuilderMain, ACLable { } AddStatConfigEnsembleable.java000066400000000000000000000023171442004423600401760ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; /** * An non-incremental reconfiguration builder. * This builder has access only to the non-incremental reconfiguration methods withMembers, so that we prevent * mixing concepts that can't be used together. */ public interface AddStatConfigEnsembleable extends Addable>, ConfigureEnsembleable, Statable { } AddWatchBuilder.java000066400000000000000000000022431442004423600362110ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.AddWatchMode; public interface AddWatchBuilder extends AddWatchBuilder2 { /** * The mode to use. By default, {@link org.apache.zookeeper.AddWatchMode#PERSISTENT_RECURSIVE} is used * * @param mode mode to use * @return this */ AddWatchBuilder2 withMode(AddWatchMode mode); }AddWatchBuilder2.java000066400000000000000000000017521442004423600362770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface AddWatchBuilder2 extends Backgroundable>>, WatchableBase>, Pathable { }Addable.java000066400000000000000000000030221442004423600345330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import java.util.List; public interface Addable { /** * Sets one or more members that are meant to be part of the ensemble. * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port] * * @param server The server to add as a member of the ensemble. * @return this */ T adding(String... server); /** * Sets one or more members that are meant to be part of the ensemble. * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port] * * @param servers The server to add as a member of the ensemble. * @return this */ T adding(List servers); } AsyncReconfigurable.java000066400000000000000000000021001442004423600371400ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface AsyncReconfigurable { /** * Sets the configuration version to use. * @param config The version of the configuration. * @throws Exception */ void fromConfig(long config) throws Exception; } BackgroundCallback.java000066400000000000000000000024051442004423600367170ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.curator.framework.CuratorFramework; /** * Functor for an async background operation */ public interface BackgroundCallback { /** * Called when the async background operation completes * * @param client the client * @param event operation result details * @throws Exception errors */ public void processResult(CuratorFramework client, CuratorEvent event) throws Exception; } BackgroundEnsembleable.java000066400000000000000000000017211442004423600376010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface BackgroundEnsembleable extends Backgroundable>, Ensembleable { } BackgroundPathAndBytesable.java000066400000000000000000000017351442004423600404020ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface BackgroundPathAndBytesable extends Backgroundable>, PathAndBytesable { } BackgroundPathable.java000066400000000000000000000017051442004423600367450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface BackgroundPathable extends Backgroundable>, Pathable { } BackgroundPathableQuietlyable.java000066400000000000000000000017101442004423600411420ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface BackgroundPathableQuietlyable extends BackgroundPathable, Quietly> { } BackgroundVersionable.java000066400000000000000000000017171442004423600375010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface BackgroundVersionable extends BackgroundPathable, Versionable> { } Backgroundable.java000066400000000000000000000050201442004423600361220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import java.util.concurrent.Executor; public interface Backgroundable { /** * Perform the action in the background * * @return this */ public T inBackground(); /** * Perform the action in the background * * @param context context object - will be available from the event sent to the listener * @return this */ public T inBackground(Object context); /** * Perform the action in the background * * @param callback a functor that will get called when the operation has completed * @return this */ public T inBackground(BackgroundCallback callback); /** * Perform the action in the background * * @param callback a functor that will get called when the operation has completed * @param context context object - will be available from the event sent to the listener * @return this */ public T inBackground(BackgroundCallback callback, Object context); /** * Perform the action in the background * * @param callback a functor that will get called when the operation has completed * @param executor executor to use for the background call * @return this */ public T inBackground(BackgroundCallback callback, Executor executor); /** * Perform the action in the background * * @param callback a functor that will get called when the operation has completed * @param context context object - will be available from the event sent to the listener * @param executor executor to use for the background call * @return this */ public T inBackground(BackgroundCallback callback, Object context, Executor executor); } ChildrenDeletable.java000066400000000000000000000021011442004423600365460ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ChildrenDeletable extends BackgroundVersionable { /** *

* Will also delete children if they exist. *

* @return */ public BackgroundVersionable deletingChildrenIfNeeded(); } Compressible.java000066400000000000000000000020201442004423600356430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface Compressible { /** * Cause the data to be compressed using the configured compression provider * * @return this */ public T compressed(); } CompressionProvider.java000066400000000000000000000020441442004423600372360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface CompressionProvider { public byte[] compress(String path, byte[] data) throws Exception; public byte[] decompress(String path, byte[] compressedData) throws Exception; } ConfigureEnsembleable.java000066400000000000000000000021621442004423600374430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ConfigureEnsembleable extends Ensembleable { /** * Sets the configuration version to use. * @param config The version of the configuration. * @throws Exception */ Ensembleable fromConfig(long config) throws Exception; } CreateBackgroundModeACLable.java000066400000000000000000000056251442004423600404060ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.CreateMode; public interface CreateBackgroundModeACLable extends BackgroundPathAndBytesable, CreateModable>, ACLCreateModeBackgroundPathAndBytesable { /** * Causes any parent nodes to get created if they haven't already been * * @return this */ public ACLCreateModePathAndBytesable creatingParentsIfNeeded(); /** * Causes any parent nodes to get created using {@link CreateMode#CONTAINER} if they haven't already been. * IMPORTANT NOTE: container creation is a new feature in recent versions of ZooKeeper. * If the ZooKeeper version you're using does not support containers, the parent nodes * are created as ordinary PERSISTENT nodes. * * @return this */ public ACLCreateModePathAndBytesable creatingParentContainersIfNeeded(); /** *

* Hat-tip to https://github.com/sbridges for pointing this out *

* *

* It turns out there is an edge case that exists when creating sequential-ephemeral * nodes. The creation can succeed on the server, but the server can crash before * the created node name is returned to the client. However, the ZK session is still * valid so the ephemeral node is not deleted. Thus, there is no way for the client to * determine what node was created for them. *

* *

* Putting the create builder into protected-ephemeral-sequential mode works around this. * The name of the node that is created is prefixed with a GUID. If node creation fails * the normal retry mechanism will occur. On the retry, the parent path is first searched * for a node that has the GUID in it. If that node is found, it is assumed to be the lost * node that was successfully created on the first try and is returned to the caller. *

* * @return this */ public ACLPathAndBytesable withProtectedEphemeralSequential(); } CreateBackgroundModeStatACLable.java000077500000000000000000000057041442004423600412430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.CreateMode; public interface CreateBackgroundModeStatACLable extends BackgroundPathAndBytesable, CreateModable>, ACLCreateModeBackgroundPathAndBytesable, Statable { /** * Causes any parent nodes to get created if they haven't already been * * @return this */ public ACLCreateModePathAndBytesable creatingParentsIfNeeded(); /** * Causes any parent nodes to get created using {@link CreateMode#CONTAINER} if they haven't already been. * IMPORTANT NOTE: container creation is a new feature in recent versions of ZooKeeper. * If the ZooKeeper version you're using does not support containers, the parent nodes * are created as ordinary PERSISTENT nodes. * * @return this */ public ACLCreateModePathAndBytesable creatingParentContainersIfNeeded(); /** *

* Hat-tip to https://github.com/sbridges for pointing this out *

* *

* It turns out there is an edge case that exists when creating sequential-ephemeral * nodes. The creation can succeed on the server, but the server can crash before * the created node name is returned to the client. However, the ZK session is still * valid so the ephemeral node is not deleted. Thus, there is no way for the client to * determine what node was created for them. *

* *

* Putting the create builder into protected-ephemeral-sequential mode works around this. * The name of the node that is created is prefixed with a GUID. If node creation fails * the normal retry mechanism will occur. On the retry, the parent path is first searched * for a node that has the GUID in it. If that node is found, it is assumed to be the lost * node that was successfully created on the first try and is returned to the caller. *

* * @return this */ public ACLPathAndBytesable withProtectedEphemeralSequential(); } CreateBuilder.java000066400000000000000000000034601442004423600357370ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface CreateBuilder extends CreateBuilderMain, Idempotentable { /** * Specify a TTL when mode is {@link org.apache.zookeeper.CreateMode#PERSISTENT_WITH_TTL} or * {@link org.apache.zookeeper.CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If * the znode has not been modified within the given TTL, it will be deleted once it has no * children. The TTL unit is milliseconds and must be greater than 0 and less than or equal to * EphemeralType.MAX_TTL. * * @param ttl the ttl * @return this for chaining */ CreateBuilderMain withTtl(long ttl); /** * If the ZNode already exists, Curator will instead call setData() */ CreateBuilder2 orSetData(); /** * If the ZNode already exists, Curator will instead call setData() * * @param version the version to use for {@link org.apache.curator.framework.CuratorFramework#setData()} */ CreateBuilder2 orSetData(int version); } CreateBuilder2.java000066400000000000000000000026401442004423600360200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface CreateBuilder2 extends CreateBuilderMain { /** * Specify a TTL when mode is {@link org.apache.zookeeper.CreateMode#PERSISTENT_WITH_TTL} or * {@link org.apache.zookeeper.CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If * the znode has not been modified within the given TTL, it will be deleted once it has no * children. The TTL unit is milliseconds and must be greater than 0 and less than or equal to * EphemeralType.MAX_TTL. * * @param ttl the ttl * @return this for chaining */ CreateBuilderMain withTtl(long ttl); } CreateBuilderMain.java000066400000000000000000000076551442004423600365560ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import java.util.UUID; import org.apache.curator.framework.imps.ProtectedUtils; import org.apache.zookeeper.CreateMode; public interface CreateBuilderMain extends BackgroundPathAndBytesable, CreateModable>, ACLCreateModeBackgroundPathAndBytesable, Compressible, Statable> { /** * Causes any parent nodes to get created if they haven't already been * * @return this */ public ProtectACLCreateModeStatPathAndBytesable creatingParentsIfNeeded(); /** * Causes any parent nodes to get created using {@link CreateMode#CONTAINER} if they haven't already been. * IMPORTANT NOTE: container creation is a new feature in recent versions of ZooKeeper. * If the ZooKeeper version you're using does not support containers, the parent nodes * are created as ordinary PERSISTENT nodes. * * @return this */ public ProtectACLCreateModeStatPathAndBytesable creatingParentContainersIfNeeded(); /** * @deprecated this has been generalized to support all create modes. Instead, use: *
     *     client.create().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)...
     * 
* @return this */ @Deprecated public ACLPathAndBytesable withProtectedEphemeralSequential(); /** *

* Hat-tip to https://github.com/sbridges for pointing this out *

* *

* It turns out there is an edge case that exists when creating sequential-ephemeral * nodes. The creation can succeed on the server, but the server can crash before * the created node name is returned to the client. However, the ZK session is still * valid so the ephemeral node is not deleted. Thus, there is no way for the client to * determine what node was created for them. *

* *

* Even without sequential-ephemeral, however, the create can succeed on the sever * but the client (for various reasons) will not know it. *

* *

* Putting the create builder into protection mode works around this. * The name of the node that is created is prefixed with a 40 characters string that is the concatenation of *

    *
  • {@value ProtectedUtils#PROTECTED_PREFIX} *
  • Canonical text representation of a random generated UUID as produced by {@link UUID#toString()} *
  • {@value ProtectedUtils#PROTECTED_SEPARATOR} *
* If node creation fails the normal retry mechanism will occur. On the retry, the parent path is first searched * for a node that has previous described prefix in it. If that node is found, it is assumed to be the lost * node that was successfully created on the first try and is returned to the caller. *

* * @return this */ public ACLCreateModeStatBackgroundPathAndBytesable withProtection(); } CreateModable.java000066400000000000000000000021411442004423600357070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.CreateMode; public interface CreateModable { /** * Set a create mode - the default is {@link CreateMode#PERSISTENT} * * @param mode new create mode * @return this */ public T withMode(CreateMode mode); } CreateProtectACLCreateModePathAndBytesable.java000077500000000000000000000057471442004423600433520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.CreateMode; public interface CreateProtectACLCreateModePathAndBytesable extends ProtectACLCreateModePathAndBytesable { /** * Causes any parent nodes to get created if they haven't already been * * @return this */ public ProtectACLCreateModePathAndBytesable creatingParentsIfNeeded(); /** * Causes any parent nodes to get created using {@link CreateMode#CONTAINER} if they haven't already been. * IMPORTANT NOTE: container creation is a new feature in recent versions of ZooKeeper. * If the ZooKeeper version you're using does not support containers, the parent nodes * are created as ordinary PERSISTENT nodes. * * @return this */ public ProtectACLCreateModePathAndBytesable creatingParentContainersIfNeeded(); /** *

* Hat-tip to https://github.com/sbridges for pointing this out *

* *

* It turns out there is an edge case that exists when creating sequential-ephemeral * nodes. The creation can succeed on the server, but the server can crash before * the created node name is returned to the client. However, the ZK session is still * valid so the ephemeral node is not deleted. Thus, there is no way for the client to * determine what node was created for them. *

* *

* Even without sequential-ephemeral, however, the create can succeed on the sever * but the client (for various reasons) will not know it. *

* *

* Putting the create builder into protection mode works around this. * The name of the node that is created is prefixed with a GUID. If node creation fails * the normal retry mechanism will occur. On the retry, the parent path is first searched * for a node that has the GUID in it. If that node is found, it is assumed to be the lost * node that was successfully created on the first try and is returned to the caller. *

* * @return this */ public ACLCreateModeBackgroundPathAndBytesable withProtection(); } CuratorEvent.java000066400000000000000000000050161442004423600356450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; /** * A super set of all the various Zookeeper events/background methods. * * IMPORTANT: the methods only return values as specified by the operation that generated them. Many methods * will return null */ public interface CuratorEvent { /** * check here first - this value determines the type of event and which methods will have * valid values * * @return event type */ public CuratorEventType getType(); /** * @return "rc" from async callbacks */ public int getResultCode(); /** * @return the path */ public String getPath(); /** * @return the context object passed to {@link Backgroundable#inBackground(Object)} */ public Object getContext(); /** * @return any stat */ public Stat getStat(); /** * @return any data */ public byte[] getData(); /** * @return any name */ public String getName(); /** * @return any children */ public List getChildren(); /** * @return any ACL list or null */ public List getACLList(); /** * @return any operation results or null */ public List getOpResults(); /** * If {@link #getType()} returns {@link CuratorEventType#WATCHED} this will * return the WatchedEvent * * @return any WatchedEvent */ public WatchedEvent getWatchedEvent(); } CuratorEventType.java000066400000000000000000000046001442004423600365050ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.Watcher; public enum CuratorEventType { /** * Corresponds to {@link CuratorFramework#create()} */ CREATE, /** * Corresponds to {@link CuratorFramework#delete()} */ DELETE, /** * Corresponds to {@link CuratorFramework#checkExists()} */ EXISTS, /** * Corresponds to {@link CuratorFramework#getData()} */ GET_DATA, /** * Corresponds to {@link CuratorFramework#setData()} */ SET_DATA, /** * Corresponds to {@link CuratorFramework#getChildren()} */ CHILDREN, /** * Corresponds to {@link CuratorFramework#sync(String, Object)} */ SYNC, /** * Corresponds to {@link CuratorFramework#getACL()} */ GET_ACL, /** * Corresponds to {@link CuratorFramework#setACL()} */ SET_ACL, /** * Corresponds to {@link CuratorFramework#transaction()} */ TRANSACTION, /** * Corresponds to {@link CuratorFramework#getConfig()} */ GET_CONFIG, /** * Corresponds to {@link CuratorFramework#reconfig()} */ RECONFIG, /** * Corresponds to {@link Watchable#usingWatcher(Watcher)} or {@link Watchable#watched()} */ WATCHED, /** * Corresponds to {@link CuratorFramework#watches()} ()} */ REMOVE_WATCHES, /** * Event sent when client is being closed */ CLOSING, /** * Corresponds to {@link CuratorFramework#watches()} */ ADD_WATCH } CuratorListener.java000066400000000000000000000024331442004423600363510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.curator.framework.CuratorFramework; /** * Receives notifications about errors and background events */ public interface CuratorListener { /** * Called when a background task has completed or a watch has triggered * * @param client client * @param event the event * @throws Exception any errors */ public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception; } CuratorWatcher.java000066400000000000000000000024321442004423600361600ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; /** * A version of {@link Watcher} that can throw an exception */ public interface CuratorWatcher { /** * Same as {@link Watcher#process(WatchedEvent)}. If an exception * is thrown, Curator will log it * * @param event the event * @throws Exception any exceptions to log */ public void process(WatchedEvent event) throws Exception; } DataCallbackable.java000066400000000000000000000023371442004423600363410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.AsyncCallback.DataCallback; public interface DataCallbackable { /** * Passes a callback and a context object to the config/reconfig command. * @param callback The async callback to use. * @param ctx An object that will be passed to the callback. * @return this */ T usingDataCallback(DataCallback callback, Object ctx); } Decompressible.java000066400000000000000000000020361442004423600361630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface Decompressible { /** * Cause the data to be de-compressed using the configured compression provider * * @return this */ public T decompressed(); } DeleteBuilder.java000066400000000000000000000017341442004423600357400ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface DeleteBuilder extends Quietly, DeleteBuilderMain, Idempotentable { } DeleteBuilderMain.java000066400000000000000000000016551442004423600365470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface DeleteBuilderMain extends GuaranteeableDeletable, ChildrenDeletable { } Ensembleable.java000066400000000000000000000016401442004423600356010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface Ensembleable { T forEnsemble() throws Exception; } ErrorListenerEnsembleable.java000066400000000000000000000023661442004423600403270ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ErrorListenerEnsembleable extends Ensembleable { /** * Set an error listener for this background operation. If an exception * occurs while processing the call in the background, this listener will * be called * * @param listener the listener * @return this for chaining */ Ensembleable withUnhandledErrorListener(UnhandledErrorListener listener); } ErrorListenerMultiTransactionMain.java000066400000000000000000000025451442004423600420550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.curator.framework.api.transaction.CuratorMultiTransactionMain; public interface ErrorListenerMultiTransactionMain extends CuratorMultiTransactionMain { /** * Set an error listener for this background operation. If an exception * occurs while processing the call in the background, this listener will * be called * * @param listener the listener * @return this for chaining */ CuratorMultiTransactionMain withUnhandledErrorListener(UnhandledErrorListener listener); } ErrorListenerPathAndBytesable.java000066400000000000000000000024021442004423600411120ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ErrorListenerPathAndBytesable extends PathAndBytesable { /** * Set an error listener for this background operation. If an exception * occurs while processing the call in the background, this listener will * be called * * @param listener the listener * @return this for chaining */ PathAndBytesable withUnhandledErrorListener(UnhandledErrorListener listener); } ErrorListenerPathable.java000066400000000000000000000023521442004423600374640ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ErrorListenerPathable extends Pathable { /** * Set an error listener for this background operation. If an exception * occurs while processing the call in the background, this listener will * be called * * @param listener the listener * @return this for chaining */ Pathable withUnhandledErrorListener(UnhandledErrorListener listener); } ErrorListenerReconfigBuilderMain.java000066400000000000000000000024021442004423600416100ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ErrorListenerReconfigBuilderMain extends ReconfigBuilderMain { /** * Set an error listener for this background operation. If an exception * occurs while processing the call in the background, this listener will * be called * * @param listener the listener * @return this for chaining */ ReconfigBuilderMain withUnhandledErrorListener(UnhandledErrorListener listener); } ExistsBuilder.java000066400000000000000000000030561442004423600360140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.CreateMode; public interface ExistsBuilder extends ExistsBuilderMain { /** * Causes any parent nodes to get created if they haven't already been * * @return this */ ACLableExistBuilderMain creatingParentsIfNeeded(); /** * Causes any parent nodes to get created using {@link CreateMode#CONTAINER} if they haven't already been. * IMPORTANT NOTE: container creation is a new feature in recent versions of ZooKeeper. * If the ZooKeeper version you're using does not support containers, the parent nodes * are created as ordinary PERSISTENT nodes. * * @return this */ ACLableExistBuilderMain creatingParentContainersIfNeeded(); } ExistsBuilderMain.java000066400000000000000000000017611442004423600366220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.data.Stat; public interface ExistsBuilderMain extends Watchable>, BackgroundPathable { } GetACLBuilder.java000066400000000000000000000020021442004423600355620ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.data.ACL; import java.util.List; public interface GetACLBuilder extends BackgroundPathable>, Statable>> { } GetChildrenBuilder.java000066400000000000000000000020351442004423600367210ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import java.util.List; public interface GetChildrenBuilder extends Watchable>>, BackgroundPathable>, Statable>> { } GetConfigBuilder.java000066400000000000000000000020641442004423600364000ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface GetConfigBuilder extends Ensembleable, Backgroundable>, Watchable>, Statable> { } GetDataBuilder.java000066400000000000000000000020431442004423600360410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface GetDataBuilder extends Watchable>, BackgroundPathable, Statable>, Decompressible { } GetDataWatchBackgroundStatable.java000066400000000000000000000017771442004423600412160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface GetDataWatchBackgroundStatable extends Watchable>, BackgroundPathable, Statable> { } Guaranteeable.java000066400000000000000000000022701442004423600357620ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface Guaranteeable { /** * Solves edge cases where an operation may succeed on the server but connection failure occurs before a * response can be successfully returned to the client. * * @see org.apache.curator.framework.api.GuaranteeableDeletable * * @return this */ public T guaranteed(); } GuaranteeableDeletable.java000066400000000000000000000031711442004423600375650ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; /** *

* Solves this edge case: deleting a node can fail due to connection issues. Further, * if the node was ephemeral, the node will not get auto-deleted as the session is still valid. * This can wreak havoc with lock implementations. *

* *

* When guaranteed is set, Curator will record failed node deletions and * attempt to delete them in the background until successful. NOTE: you will still get an * exception when the deletion fails. But, you can be assured that as long as the * {@link org.apache.curator.framework.CuratorFramework} instance is open attempts will be made to delete the node. *

* * @return this */ public interface GuaranteeableDeletable extends Guaranteeable, BackgroundVersionable { } Idempotentable.java000066400000000000000000000022741442004423600361630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface Idempotentable { /** * If the first try of this operation gets a transient error, curator will retry * the operation, and treat it as successful so long as the end state of the znode * is the same as if the operation had completed without error on the first try. * @return this: */ public T idempotent(); } JoinStatConfigEnsembleable.java000066400000000000000000000023151442004423600404030ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; /** * An incremental reconfiguration builder. * This builder has access only to the incremental reconfiguration methods joining and leaving, so that we prevent * mixing concepts that can't be used together. */ public interface JoinStatConfigEnsembleable extends Joinable, ConfigureEnsembleable, Statable { } Joinable.java000066400000000000000000000026741442004423600347560ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import java.util.List; public interface Joinable { /** * Adds one or more servers to joining the ensemble. * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port] * * @param server The server joining. * @return this */ T joining(String... server); /** * Adds one or more servers to joining the ensemble. * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port] * * @param servers The servers joining. * @return this */ T joining(List servers); } LeaveStatConfigEnsembleable.java000066400000000000000000000023161442004423600405410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; /** * An incremental reconfiguration builder. * This builder has access only to the incremental reconfiguration methods joining and leaving, so that we prevent * mixing concepts that can't be used together. */ public interface LeaveStatConfigEnsembleable extends Leaveable, ConfigureEnsembleable, Statable { } Leaveable.java000066400000000000000000000023461442004423600351070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import java.util.List; public interface Leaveable { /** * Sets one or more servers to leaving the ensemble. * * @param server The server ids * @return this */ T leaving(String... server); /** * Sets one or more servers to leaving the ensemble. * * @param servers The server ids * @return this */ T leaving(List servers); } Membersable.java000066400000000000000000000027411442004423600354440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import java.util.List; public interface Membersable { /** * Sets one or more members that are meant to be the ensemble. * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port] * * @param server The server joining. * @return this */ T withNewMembers(String... server); /** * Sets one or more members that are meant to be the ensemble. * The expected format is server.[id]=[hostname]:[peer port]:[election port]:[type];[client port] * * @param servers The servers joining. * @return this */ T withNewMembers(List servers); } ParentACLable.java000066400000000000000000000026611442004423600356240ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import java.util.List; public interface ParentACLable extends ACLable { /** * Set an ACL list (default is {@link ZooDefs.Ids#OPEN_ACL_UNSAFE}). * * If applyToParents is true, then the aclList is applied to the created parents. * Existing parent nodes are not affected. * * @param aclList the ACL list to use * @param applyToParents if true, then the aclList is applied to the created parents. * @return this */ T withACL(List aclList, boolean applyToParents); } PathAndBytesable.java000066400000000000000000000031661442004423600364020ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.curator.framework.CuratorFrameworkFactory; public interface PathAndBytesable { /** * Commit the currently building operation using the given path and data * * @param path the path * @param data the data * @return operation result if any * @throws Exception errors */ public T forPath(String path, byte[] data) throws Exception; /** * Commit the currently building operation using the given path and the default data * for the client (usually a byte[0] unless changed via * {@link CuratorFrameworkFactory.Builder#defaultData(byte[])}). * * @param path the path * @return operation result if any * @throws Exception errors */ public T forPath(String path) throws Exception; } Pathable.java000066400000000000000000000021551442004423600347450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface Pathable { /** * Commit the currently building operation using the given path * * @param path the path * @return operation result if any * @throws Exception errors */ public T forPath(String path) throws Exception; } ProtectACLCreateModePathAndBytesable.java000066400000000000000000000044751442004423600422200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ProtectACLCreateModePathAndBytesable extends ACLBackgroundPathAndBytesable, CreateModable> { /** *

* Hat-tip to https://github.com/sbridges for pointing this out *

* *

* It turns out there is an edge case that exists when creating sequential-ephemeral * nodes. The creation can succeed on the server, but the server can crash before * the created node name is returned to the client. However, the ZK session is still * valid so the ephemeral node is not deleted. Thus, there is no way for the client to * determine what node was created for them. *

* *

* Even without sequential-ephemeral, however, the create can succeed on the sever * but the client (for various reasons) will not know it. *

* *

* Putting the create builder into protection mode works around this. * The name of the node that is created is prefixed with a GUID. If node creation fails * the normal retry mechanism will occur. On the retry, the parent path is first searched * for a node that has the GUID in it. If that node is found, it is assumed to be the lost * node that was successfully created on the first try and is returned to the caller. *

* * @return this */ public ACLCreateModeBackgroundPathAndBytesable withProtection(); } ProtectACLCreateModeStatPathAndBytesable.java000066400000000000000000000017711442004423600430500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ProtectACLCreateModeStatPathAndBytesable extends ProtectACLCreateModePathAndBytesable, Statable> { } Quietly.java000066400000000000000000000016141442004423600346600ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface Quietly { public T quietly(); } ReconfigBuilder.java000066400000000000000000000017171442004423600362730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ReconfigBuilder extends ReconfigBuilderMain, Backgroundable { } ReconfigBuilderMain.java000066400000000000000000000020061442004423600370700ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface ReconfigBuilderMain extends Joinable, Leaveable, Membersable { } RemoveWatchesBuilder.java000066400000000000000000000026501442004423600373100ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.Watcher; /** * Builder to allow watches to be removed */ public interface RemoveWatchesBuilder { /** * Specify the watcher to be removed * @param watcher * @return */ public RemoveWatchesType remove(Watcher watcher); /** * Specify the watcher to be removed * @param watcher * @return */ public RemoveWatchesType remove(CuratorWatcher watcher); /** * Specify that all watches should be removed * @return */ public RemoveWatchesType removeAll(); } RemoveWatchesLocal.java000066400000000000000000000025561442004423600367610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; /** * Builder to allow the specification of whether it is acceptable to remove client side watch information * in the case where ZK cannot be contacted. */ public interface RemoveWatchesLocal extends BackgroundPathableQuietlyable { /** * Specify if the client should just remove client side watches if a connection to ZK * is not available. Note that the standard Curator retry loop will not be used in t * @return */ public BackgroundPathableQuietlyable locally(); } RemoveWatchesType.java000066400000000000000000000025421442004423600366430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.Watcher.WatcherType; /** * Builder to allow the specification of whether it is acceptable to remove client side watch information * in the case where ZK cannot be contacted. */ public interface RemoveWatchesType extends RemoveWatchesLocal, Guaranteeable> { /** * Specify the type of watcher to be removed. * @param watcherType * @return */ public RemoveWatchesLocal ofType(WatcherType watcherType); } SetACLBuilder.java000066400000000000000000000020011442004423600355750ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.data.Stat; public interface SetACLBuilder extends ACLable>, Versionable>> { } SetDataBackgroundVersionable.java000066400000000000000000000020161442004423600407400ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.data.Stat; public interface SetDataBackgroundVersionable extends BackgroundPathAndBytesable, Versionable> { } SetDataBuilder.java000066400000000000000000000021241442004423600360550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.data.Stat; public interface SetDataBuilder extends BackgroundPathAndBytesable, Versionable>, Compressible, Idempotentable { } StatConfigureEnsembleable.java000066400000000000000000000017111442004423600402760ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface StatConfigureEnsembleable extends Statable, ConfigureEnsembleable { }StatPathable.java000066400000000000000000000016541442004423600356040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface StatPathable extends Pathable, Statable> { } Statable.java000066400000000000000000000021251442004423600347610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.data.Stat; public interface Statable { /** * Have the operation fill the provided stat object * * @param stat the stat to have filled in * @return this */ public T storingStatIn(Stat stat); } SyncBuilder.java000066400000000000000000000016321442004423600354470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface SyncBuilder extends BackgroundPathable { } TempGetDataBuilder.java000066400000000000000000000017351442004423600366760ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface TempGetDataBuilder extends StatPathable, Decompressible>, Pathable { } UnhandledErrorListener.java000066400000000000000000000023461442004423600376510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; public interface UnhandledErrorListener { /** * Called when an exception is caught in a background thread, handler, etc. * * @param message Source message * @param e exception */ public void unhandledError(String message, Throwable e); } VersionPathAndBytesable.java000066400000000000000000000017221442004423600377440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface VersionPathAndBytesable extends Versionable>, PathAndBytesable { } Versionable.java000066400000000000000000000020441442004423600354730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface Versionable { /** * Use the given version (the default is -1) * * @param version version to use * @return this */ public T withVersion(int version); } WatchBackgroundEnsembleable.java000066400000000000000000000017301442004423600405700ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface WatchBackgroundEnsembleable extends Watchable>, BackgroundEnsembleable { } WatchPathable.java000066400000000000000000000016561442004423600357410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface WatchPathable extends Watchable>, Pathable { } Watchable.java000066400000000000000000000017611442004423600351210ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; public interface Watchable extends WatchableBase { /** * Have the operation set a watch * * @return this */ T watched(); } WatchableBase.java000066400000000000000000000023321442004423600357070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; import org.apache.zookeeper.Watcher; public interface WatchableBase { /** * Set a watcher for the operation * * @param watcher the watcher * @return this */ T usingWatcher(Watcher watcher); /** * Set a watcher for the operation * * @param watcher the watcher * @return this */ T usingWatcher(CuratorWatcher watcher); } WatchesBuilder.java000066400000000000000000000020641442004423600361310ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api; /** * Builder to allow watches to be removed */ public interface WatchesBuilder extends RemoveWatchesBuilder { /** * Start an add watch operation * * @return builder */ AddWatchBuilder add(); }transaction/000077500000000000000000000000001442004423600347045ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/apiCuratorMultiTransaction.java000066400000000000000000000021601442004423600424060ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import org.apache.curator.framework.api.Backgroundable; import org.apache.curator.framework.api.ErrorListenerMultiTransactionMain; public interface CuratorMultiTransaction extends Backgroundable, CuratorMultiTransactionMain { } CuratorMultiTransactionMain.java000066400000000000000000000035711442004423600432220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import org.apache.curator.framework.CuratorFramework; import java.util.List; public interface CuratorMultiTransactionMain { /** * Commit the given operations as a single transaction. Create the * operation instances via {@link CuratorFramework#transactionOp()} * * @param operations operations that make up the transaction. * @return result details for foreground operations or null for background operations * @throws Exception errors */ List forOperations(CuratorOp... operations) throws Exception; /** * Commit the given operations as a single transaction. Create the * operation instances via {@link CuratorFramework#transactionOp()} * * @param operations operations that make up the transaction. * @return result details for foreground operations or null for background operations * @throws Exception errors */ List forOperations(List operations) throws Exception; } CuratorOp.java000066400000000000000000000020171442004423600374650ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import org.apache.zookeeper.Op; /** * Internal representation of a transaction operation */ public interface CuratorOp { Op get(); TypeAndPath getTypeAndPath(); } CuratorTransaction.java000066400000000000000000000050311442004423600413730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.ZooKeeper; /** *

* Transactional/atomic operations. See {@link ZooKeeper#multi(Iterable)} for * details on ZooKeeper transactions. *

* *

* The general form for this interface is: *

*
 *         curator.inTransaction().operation().arguments().forPath(...).
 *             and().more-operations.
 *             and().commit();
 *     
* *

* Here's an example that creates two nodes in a transaction *

*
 *         curator.inTransaction().
 *             create().forPath("/path-one", path-one-data).
 *             and().create().forPath("/path-two", path-two-data).
 *             and().commit();
 *     
* *

* Important: the operations are not submitted until * {@link CuratorTransactionFinal#commit()} is called. *

* * @deprecated Use {@link CuratorFramework#transaction()} */ public interface CuratorTransaction { /** * Start a create builder in the transaction * * @return builder object */ public TransactionCreateBuilder create(); /** * Start a delete builder in the transaction * * @return builder object */ public TransactionDeleteBuilder delete(); /** * Start a setData builder in the transaction * * @return builder object */ public TransactionSetDataBuilder setData(); /** * Start a check builder in the transaction * * @return builder object */ public TransactionCheckBuilder check(); } CuratorTransactionBridge.java000066400000000000000000000020711442004423600425110ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; public interface CuratorTransactionBridge { /** * Syntactic sugar to make the fluent interface more readable * * @return transaction continuation */ public CuratorTransactionFinal and(); } CuratorTransactionFinal.java000066400000000000000000000026311442004423600423500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import java.util.Collection; /** * Adds commit to the transaction interface */ public interface CuratorTransactionFinal extends CuratorTransaction { /** * Commit all added operations as an atomic unit and return results * for the operations. One result is returned for each operation added. * Further, the ordering of the results matches the ordering that the * operations were added. * * @return operation results * @throws Exception errors */ public Collection commit() throws Exception; } CuratorTransactionResult.java000066400000000000000000000070521442004423600425770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import com.google.common.base.Predicate; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.data.Stat; /** * Holds the result of one transactional operation */ public class CuratorTransactionResult { private final OperationType type; private final String forPath; private final String resultPath; private final Stat resultStat; private final int error; /** * Utility that can be passed to Google Guava to find a particular result. E.g. *
     * Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, path))
     * 
* * @param type operation type * @param forPath path * @return predicate */ public static Predicate ofTypeAndPath(final OperationType type, final String forPath) { return new Predicate() { @Override public boolean apply(CuratorTransactionResult result) { return (result.getType() == type) && result.getForPath().equals(forPath); } }; } public CuratorTransactionResult(OperationType type, String forPath, String resultPath, Stat resultStat) { this(type, forPath, resultPath, resultStat, 0); } public CuratorTransactionResult(OperationType type, String forPath, String resultPath, Stat resultStat, int error) { this.forPath = forPath; this.resultPath = resultPath; this.resultStat = resultStat; this.type = type; this.error = error; } /** * Returns the operation type * * @return operation type */ public OperationType getType() { return type; } /** * Returns the path that was passed to the operation when added * * @return operation input path */ public String getForPath() { return forPath; } /** * Returns the operation generated path or null. i.e. {@link CuratorTransaction#create()} * using an EPHEMERAL mode generates the created path plus its sequence number. * * @return generated path or null */ public String getResultPath() { return resultPath; } /** * Returns the operation generated stat or null. i.e. {@link CuratorTransaction#setData()} * generates a stat object. * * @return generated stat or null */ public Stat getResultStat() { return resultStat; } /** * Returns the operation generated error or 0 i.e. {@link OpResult.ErrorResult#getErr()} * * @return error or 0 */ public int getError() { return error; } } OperationType.java000066400000000000000000000022601442004423600403510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; /** * Transaction operation types */ public enum OperationType { /** * {@link TransactionOp#create()} */ CREATE, /** * {@link TransactionOp#delete()} */ DELETE, /** * {@link TransactionOp#setData()} */ SET_DATA, /** * {@link TransactionOp#check()} */ CHECK } TransactionCheckBuilder.java000066400000000000000000000020561442004423600423040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import org.apache.curator.framework.api.Pathable; import org.apache.curator.framework.api.Versionable; public interface TransactionCheckBuilder extends Pathable, Versionable> { } TransactionCreateBuilder.java000066400000000000000000000027171442004423600424760ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; public interface TransactionCreateBuilder extends TransactionCreateBuilder2 { /** * Specify a TTL when mode is {@link org.apache.zookeeper.CreateMode#PERSISTENT_WITH_TTL} or * {@link org.apache.zookeeper.CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If * the znode has not been modified within the given TTL, it will be deleted once it has no * children. The TTL unit is milliseconds and must be greater than 0 and less than or equal to * EphemeralType.MAX_TTL. * * @param ttl the ttl * @return this for chaining */ TransactionCreateBuilder2 withTtl(long ttl); } TransactionCreateBuilder2.java000066400000000000000000000025771442004423600425640ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import org.apache.curator.framework.api.ACLCreateModePathAndBytesable; import org.apache.curator.framework.api.ACLPathAndBytesable; import org.apache.curator.framework.api.Compressible; import org.apache.curator.framework.api.CreateModable; import org.apache.curator.framework.api.PathAndBytesable; public interface TransactionCreateBuilder2 extends PathAndBytesable, CreateModable>, ACLPathAndBytesable, ACLCreateModePathAndBytesable, Compressible> { } TransactionDeleteBuilder.java000066400000000000000000000020571442004423600424720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import org.apache.curator.framework.api.Pathable; import org.apache.curator.framework.api.Versionable; public interface TransactionDeleteBuilder extends Pathable, Versionable> { } TransactionOp.java000066400000000000000000000032171442004423600403360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import org.apache.curator.framework.CuratorFramework; /** * Builds operations that can be committed as a transaction * via {@link CuratorFramework#transaction()} */ public interface TransactionOp { /** * Start a create builder in the transaction * * @return builder object */ TransactionCreateBuilder create(); /** * Start a delete builder in the transaction * * @return builder object */ TransactionDeleteBuilder delete(); /** * Start a setData builder in the transaction * * @return builder object */ TransactionSetDataBuilder setData(); /** * Start a check builder in the transaction * * @return builder object */ TransactionCheckBuilder check(); } TransactionSetDataBuilder.java000066400000000000000000000024151442004423600426130ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; import org.apache.curator.framework.api.Compressible; import org.apache.curator.framework.api.PathAndBytesable; import org.apache.curator.framework.api.VersionPathAndBytesable; import org.apache.curator.framework.api.Versionable; public interface TransactionSetDataBuilder extends PathAndBytesable, Versionable>, VersionPathAndBytesable, Compressible> { } TypeAndPath.java000066400000000000000000000023201442004423600377250ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/api/transaction/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.api.transaction; public class TypeAndPath { private final OperationType type; private final String forPath; public TypeAndPath(OperationType type, String forPath) { this.type = type; this.forPath = forPath; } public OperationType getType() { return type; } public String getForPath() { return forPath; } } curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/000077500000000000000000000000001442004423600326355ustar00rootroot00000000000000ACLing.java000066400000000000000000000045301442004423600345200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.collect.ImmutableList; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.utils.InternalACLProvider; import org.apache.zookeeper.data.ACL; import java.util.List; class ACLing implements InternalACLProvider { private final List aclList; private final ACLProvider aclProvider; private final boolean applyToParents; ACLing(ACLProvider aclProvider) { this(aclProvider, null); } ACLing(ACLProvider aclProvider, List aclList) { this(aclProvider, aclList, false); } ACLing(ACLProvider aclProvider, List aclList, boolean applyToParents) { this.aclProvider = aclProvider; this.aclList = (aclList != null) ? ImmutableList.copyOf(aclList) : null; this.applyToParents = applyToParents; } InternalACLProvider getACLProviderForParents() { return applyToParents ? this : aclProvider; } List getAclList(String path) { if ( aclList != null ) return aclList; if ( path != null ) { List localAclList = aclProvider.getAclForPath(path); if ( localAclList != null ) { return localAclList; } } return aclProvider.getDefaultAcl(); } @Override public List getDefaultAcl() { return aclProvider.getDefaultAcl(); } @Override public List getAclForPath(String path) { return getAclList(path); } } AddWatchBuilderImpl.java000066400000000000000000000153601442004423600372360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.AddWatchBuilder; import org.apache.curator.framework.api.AddWatchBuilder2; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.api.Pathable; import org.apache.curator.framework.api.WatchableBase; import org.apache.zookeeper.AddWatchMode; import org.apache.zookeeper.Watcher; import java.util.concurrent.Executor; public class AddWatchBuilderImpl implements AddWatchBuilder, Pathable, BackgroundOperation { private final CuratorFrameworkImpl client; private Watching watching; private Backgrounding backgrounding = new Backgrounding(); private AddWatchMode mode = AddWatchMode.PERSISTENT_RECURSIVE; AddWatchBuilderImpl(CuratorFrameworkImpl client) { this.client = client; watching = new Watching(client, true); } public AddWatchBuilderImpl(CuratorFrameworkImpl client, Watching watching, Backgrounding backgrounding, AddWatchMode mode) { this.client = client; this.watching = watching; this.backgrounding = backgrounding; this.mode = mode; } @Override public WatchableBase> inBackground() { backgrounding = new Backgrounding(); return this; } @Override public AddWatchBuilder2 withMode(AddWatchMode mode) { this.mode = mode; return this; } @Override public Pathable usingWatcher(Watcher watcher) { watching = new Watching(client, watcher); return this; } @Override public Pathable usingWatcher(CuratorWatcher watcher) { watching = new Watching(client, watcher); return this; } @Override public WatchableBase> inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public WatchableBase> inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public WatchableBase> inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public WatchableBase> inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(callback, executor); return this; } @Override public WatchableBase> inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public Void forPath(String path) throws Exception { if ( backgrounding.inBackground() ) { client.processBackgroundOperation(new OperationAndData<>(this, path, backgrounding.getCallback(), null, backgrounding.getContext(), watching), null); } else { pathInForeground(path); } return null; } @Override public void performBackgroundOperation(final OperationAndData data) throws Exception { String path = data.getData(); String fixedPath = client.fixForNamespace(path); try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("AddWatchBuilderImpl-Background"); if ( watching.isWatched() ) { client.getZooKeeper().addWatch ( fixedPath, mode, (rc, path1, ctx) -> { trace.setReturnCode(rc).setWithWatcher(true).setPath(path1).commit(); CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.ADD_WATCH, rc, path1, null, ctx, null, null, null, null, null, null); client.processBackgroundOperation(data, event); }, backgrounding.getContext() ); } else { client.getZooKeeper().addWatch ( fixedPath, watching.getWatcher(path), mode, (rc, path1, ctx) -> { trace.setReturnCode(rc).setWithWatcher(true).setPath(path1).commit(); CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.ADD_WATCH, rc, path1, null, ctx, null, null, null, null, null, null); client.processBackgroundOperation(data, event); }, backgrounding.getContext() ); } } catch ( Throwable e ) { backgrounding.checkError(e, watching); } } private void pathInForeground(final String path) throws Exception { final String fixedPath = client.fixForNamespace(path); OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("AddWatchBuilderImpl-Foreground"); RetryLoop.callWithRetry ( client.getZookeeperClient(), () -> { if ( watching.isWatched() ) { client.getZooKeeper().addWatch(fixedPath, mode); } else { client.getZooKeeper().addWatch(fixedPath, watching.getWatcher(path), mode); } return null; }); trace.setPath(fixedPath).setWithWatcher(true).commit(); } }BackgroundOperation.java000066400000000000000000000017211442004423600373620ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; interface BackgroundOperation { public void performBackgroundOperation(OperationAndData data) throws Exception; } BackgroundSyncImpl.java000066400000000000000000000042531442004423600371630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.CuratorEventType; import org.apache.zookeeper.AsyncCallback; class BackgroundSyncImpl implements BackgroundOperation { private final CuratorFrameworkImpl client; private final Object context; BackgroundSyncImpl(CuratorFrameworkImpl client, Object context) { this.client = client; this.context = context; } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("BackgroundSyncImpl"); final String data = operationAndData.getData(); client.getZooKeeper().sync ( data, new AsyncCallback.VoidCallback() { @Override public void processResult(int rc, String path, Object ctx) { trace.setReturnCode(rc).setRequestBytesLength(data).commit(); CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.SYNC, rc, path, null, ctx, null, null, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } }, context ); } } Backgrounding.java000066400000000000000000000124001442004423600361730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.base.Throwables; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.utils.ThreadUtils; import org.apache.zookeeper.KeeperException; import java.util.concurrent.Executor; public class Backgrounding { private final boolean inBackground; private final Object context; private final BackgroundCallback callback; private final UnhandledErrorListener errorListener; Backgrounding(Object context) { this.inBackground = true; this.context = context; this.callback = null; errorListener = null; } Backgrounding(BackgroundCallback callback) { this.inBackground = true; this.context = null; this.callback = callback; errorListener = null; } Backgrounding(boolean inBackground) { this.inBackground = inBackground; this.context = null; this.callback = null; errorListener = null; } Backgrounding(BackgroundCallback callback, Object context) { this.inBackground = true; this.context = context; this.callback = callback; errorListener = null; } Backgrounding(CuratorFrameworkImpl client, BackgroundCallback callback, Object context, Executor executor) { this(wrapCallback(client, callback, executor), context); } Backgrounding(CuratorFrameworkImpl client, BackgroundCallback callback, Executor executor) { this(wrapCallback(client, callback, executor)); } Backgrounding(Backgrounding rhs, UnhandledErrorListener errorListener) { if ( rhs == null ) { rhs = new Backgrounding(); } this.inBackground = rhs.inBackground; this.context = rhs.context; this.callback = rhs.callback; this.errorListener = errorListener; } public Backgrounding(BackgroundCallback callback, UnhandledErrorListener errorListener) { this.callback = callback; this.errorListener = errorListener; inBackground = true; context = null; } Backgrounding() { inBackground = false; context = null; this.callback = null; errorListener = null; } boolean inBackground() { return inBackground; } Object getContext() { return context; } BackgroundCallback getCallback() { return callback; } void checkError(Throwable e, Watching watching) throws Exception { if ( e != null ) { if ( errorListener != null ) { errorListener.unhandledError("n/a", e); } else if ( e instanceof Exception ) { throw (Exception)e; } else { Throwables.propagate(e); } } } private static BackgroundCallback wrapCallback(final CuratorFrameworkImpl client, final BackgroundCallback callback, final Executor executor) { return new BackgroundCallback() { @Override public void processResult(CuratorFramework dummy, final CuratorEvent event) throws Exception { executor.execute ( new Runnable() { @Override public void run() { try { callback.processResult(client, event); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); if ( e instanceof KeeperException ) { client.validateConnection(client.codeToState(((KeeperException)e).code())); } client.logError("Background operation result handling threw exception", e); } } } ); } }; } } CreateBuilderImpl.java000066400000000000000000001506661442004423600367730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable; import org.apache.curator.framework.api.ACLCreateModeBackgroundPathAndBytesable; import org.apache.curator.framework.api.ACLCreateModePathAndBytesable; import org.apache.curator.framework.api.ACLCreateModeStatBackgroundPathAndBytesable; import org.apache.curator.framework.api.ACLPathAndBytesable; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.BackgroundPathAndBytesable; import org.apache.curator.framework.api.CreateBackgroundModeACLable; import org.apache.curator.framework.api.CreateBackgroundModeStatACLable; import org.apache.curator.framework.api.CreateBuilder; import org.apache.curator.framework.api.CreateBuilder2; import org.apache.curator.framework.api.CreateBuilderMain; import org.apache.curator.framework.api.CreateProtectACLCreateModePathAndBytesable; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.ErrorListenerPathAndBytesable; import org.apache.curator.framework.api.PathAndBytesable; import org.apache.curator.framework.api.ProtectACLCreateModePathAndBytesable; import org.apache.curator.framework.api.ProtectACLCreateModeStatPathAndBytesable; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.api.transaction.OperationType; import org.apache.curator.framework.api.transaction.TransactionCreateBuilder; import org.apache.curator.framework.api.transaction.TransactionCreateBuilder2; import org.apache.curator.utils.InternalACLProvider; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.DataTree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CreateBuilderImpl implements CreateBuilder, CreateBuilder2, BackgroundOperation, ErrorListenerPathAndBytesable { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFrameworkImpl client; private final ProtectedMode protectedMode = new ProtectedMode(); private CreateMode createMode; private Backgrounding backgrounding; private boolean createParentsIfNeeded; private boolean createParentsAsContainers; private boolean compress; private boolean setDataIfExists; private int setDataIfExistsVersion = -1; private boolean idempotent = false; private ACLing acling; private Stat storingStat; private long ttl; @VisibleForTesting boolean failNextCreateForTesting = false; @VisibleForTesting boolean failBeforeNextCreateForTesting = false; @VisibleForTesting boolean failNextIdempotentCheckForTesting = false; CreateBuilderImpl(CuratorFrameworkImpl client) { this.client = client; createMode = CreateMode.PERSISTENT; backgrounding = new Backgrounding(); acling = new ACLing(client.getAclProvider()); createParentsIfNeeded = false; createParentsAsContainers = false; compress = false; setDataIfExists = false; storingStat = null; ttl = -1; } public CreateBuilderImpl(CuratorFrameworkImpl client, CreateMode createMode, Backgrounding backgrounding, boolean createParentsIfNeeded, boolean createParentsAsContainers, boolean doProtected, boolean compress, boolean setDataIfExists, List aclList, Stat storingStat, long ttl) { this.client = client; this.createMode = createMode; this.backgrounding = backgrounding; this.createParentsIfNeeded = createParentsIfNeeded; this.createParentsAsContainers = createParentsAsContainers; this.compress = compress; this.setDataIfExists = setDataIfExists; this.acling = new ACLing(client.getAclProvider(), aclList); this.storingStat = storingStat; this.ttl = ttl; if ( doProtected ) { protectedMode.setProtectedMode(); } } public void setSetDataIfExistsVersion(int version) { this.setDataIfExistsVersion = version; } @Override public CreateBuilder2 orSetData() { return orSetData(-1); } @Override public CreateBuilder2 orSetData(int version) { setDataIfExists = true; setDataIfExistsVersion = version; return this; } @Override public CreateBuilder2 idempotent() { this.idempotent = true; return this; } @Override public CreateBuilderMain withTtl(long ttl) { this.ttl = ttl; return this; } TransactionCreateBuilder asTransactionCreateBuilder(final T context, final CuratorMultiTransactionRecord transaction) { return new TransactionCreateBuilder() { @Override public PathAndBytesable withACL(List aclList) { return withACL(aclList, false); } @Override public PathAndBytesable withACL(List aclList, boolean applyToParents) { CreateBuilderImpl.this.withACL(aclList, applyToParents); return this; } @Override public TransactionCreateBuilder2 withTtl(long ttl) { CreateBuilderImpl.this.withTtl(ttl); return this; } @Override public ACLPathAndBytesable withMode(CreateMode mode) { CreateBuilderImpl.this.withMode(mode); return this; } @Override public ACLCreateModePathAndBytesable compressed() { CreateBuilderImpl.this.compressed(); return this; } @Override public T forPath(String path) throws Exception { return forPath(path, client.getDefaultData()); } @Override public T forPath(String path, byte[] data) throws Exception { if ( compress ) { data = client.getCompressionProvider().compress(path, data); } String fixedPath = client.fixForNamespace(path); transaction.add(Op.create(fixedPath, data, acling.getAclList(path), createMode, ttl), OperationType.CREATE, path); return context; } }; } @Override public CreateBackgroundModeStatACLable compressed() { compress = true; return new CreateBackgroundModeStatACLable() { @Override public CreateBackgroundModeACLable storingStatIn(Stat stat) { storingStat = stat; return asCreateBackgroundModeACLable(); } @Override public ACLCreateModePathAndBytesable creatingParentsIfNeeded() { createParentsIfNeeded = true; return asACLCreateModePathAndBytesable(); } @Override public ACLCreateModePathAndBytesable creatingParentContainersIfNeeded() { setCreateParentsAsContainers(); return creatingParentsIfNeeded(); } @Override public ACLPathAndBytesable withProtectedEphemeralSequential() { return CreateBuilderImpl.this.withProtectedEphemeralSequential(); } @Override public BackgroundPathAndBytesable withACL(List aclList) { return CreateBuilderImpl.this.withACL(aclList); } @Override public BackgroundPathAndBytesable withACL(List aclList, boolean applyToParents) { return CreateBuilderImpl.this.withACL(aclList, applyToParents); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context) { return CreateBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, context, executor); } @Override public ErrorListenerPathAndBytesable inBackground() { return CreateBuilderImpl.this.inBackground(); } @Override public ErrorListenerPathAndBytesable inBackground(Object context) { return CreateBuilderImpl.this.inBackground(context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback) { return CreateBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, executor); } @Override public ACLBackgroundPathAndBytesable withMode(CreateMode mode) { return CreateBuilderImpl.this.withMode(mode); } @Override public String forPath(String path, byte[] data) throws Exception { return CreateBuilderImpl.this.forPath(path, data); } @Override public String forPath(String path) throws Exception { return CreateBuilderImpl.this.forPath(path); } }; } @Override public ACLBackgroundPathAndBytesable withACL(List aclList) { return withACL(aclList, false); } @Override public ACLBackgroundPathAndBytesable withACL(List aclList, boolean applyToParents) { acling = new ACLing(client.getAclProvider(), aclList, applyToParents); return new ACLBackgroundPathAndBytesable() { @Override public BackgroundPathAndBytesable withACL(List aclList) { return CreateBuilderImpl.this.withACL(aclList); } @Override public BackgroundPathAndBytesable withACL(List aclList, boolean applyToParents) { return CreateBuilderImpl.this.withACL(aclList, applyToParents); } @Override public ErrorListenerPathAndBytesable inBackground() { return CreateBuilderImpl.this.inBackground(); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context) { return CreateBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, context, executor); } @Override public ErrorListenerPathAndBytesable inBackground(Object context) { return CreateBuilderImpl.this.inBackground(context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback) { return CreateBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, executor); } @Override public String forPath(String path, byte[] data) throws Exception { return CreateBuilderImpl.this.forPath(path, data); } @Override public String forPath(String path) throws Exception { return CreateBuilderImpl.this.forPath(path); } }; } @Override public ProtectACLCreateModeStatPathAndBytesable creatingParentContainersIfNeeded() { setCreateParentsAsContainers(); return creatingParentsIfNeeded(); } private void setCreateParentsAsContainers() { if ( client.useContainerParentsIfAvailable() ) { createParentsAsContainers = true; } } @Override public ProtectACLCreateModeStatPathAndBytesable creatingParentsIfNeeded() { createParentsIfNeeded = true; return new ProtectACLCreateModeStatPathAndBytesable() { @Override public ACLCreateModeBackgroundPathAndBytesable withProtection() { return CreateBuilderImpl.this.withProtection(); } @Override public BackgroundPathAndBytesable withACL(List aclList) { return withACL(aclList, false); } @Override public BackgroundPathAndBytesable withACL(List aclList, boolean applyToParents) { return CreateBuilderImpl.this.withACL(aclList, applyToParents); } @Override public ErrorListenerPathAndBytesable inBackground() { return CreateBuilderImpl.this.inBackground(); } @Override public ErrorListenerPathAndBytesable inBackground(Object context) { return CreateBuilderImpl.this.inBackground(context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback) { return CreateBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context) { return CreateBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, executor); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, context, executor); } @Override public ACLBackgroundPathAndBytesable withMode(CreateMode mode) { return CreateBuilderImpl.this.withMode(mode); } @Override public String forPath(String path, byte[] data) throws Exception { return CreateBuilderImpl.this.forPath(path, data); } @Override public String forPath(String path) throws Exception { return CreateBuilderImpl.this.forPath(path); } @Override public ACLBackgroundPathAndBytesable storingStatIn(Stat stat) { storingStat = stat; return CreateBuilderImpl.this; } }; } @Override public ACLCreateModeStatBackgroundPathAndBytesable withProtection() { protectedMode.setProtectedMode(); return asACLCreateModeStatBackgroundPathAndBytesable(); } @Override public ACLPathAndBytesable withProtectedEphemeralSequential() { protectedMode.setProtectedMode(); createMode = CreateMode.EPHEMERAL_SEQUENTIAL; return new ACLPathAndBytesable() { @Override public PathAndBytesable withACL(List aclList) { return CreateBuilderImpl.this.withACL(aclList); } @Override public PathAndBytesable withACL(List aclList, boolean applyToParents) { return CreateBuilderImpl.this.withACL(aclList, applyToParents); } @Override public String forPath(String path, byte[] data) throws Exception { return CreateBuilderImpl.this.forPath(path, data); } @Override public String forPath(String path) throws Exception { return CreateBuilderImpl.this.forPath(path); } }; } @Override public ACLBackgroundPathAndBytesable withMode(CreateMode mode) { createMode = mode; return this; } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public ErrorListenerPathAndBytesable inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerPathAndBytesable inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public PathAndBytesable withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public String forPath(String path) throws Exception { return forPath(path, client.getDefaultData()); } @Override public String forPath(final String givenPath, byte[] data) throws Exception { if ( compress ) { data = client.getCompressionProvider().compress(givenPath, data); } final String adjustedPath = adjustPath(client.fixForNamespace(givenPath, createMode.isSequential())); List aclList = acling.getAclList(adjustedPath); client.getSchemaSet().getSchema(givenPath).validateCreate(createMode, givenPath, data, aclList); String returnPath = null; if ( backgrounding.inBackground() ) { pathInBackground(adjustedPath, data, givenPath); } else { String path = protectedPathInForeground(adjustedPath, data, aclList); returnPath = client.unfixForNamespace(path); } return returnPath; } private String protectedPathInForeground(String adjustedPath, byte[] data, List aclList) throws Exception { try { return pathInForeground(adjustedPath, data, aclList); } catch ( Exception e) { ThreadUtils.checkInterrupted(e); if ( ( e instanceof KeeperException.ConnectionLossException || !( e instanceof KeeperException )) && protectedMode.doProtected() ) { /* * CURATOR-45 + CURATOR-79: we don't know if the create operation was successful or not, * register the znode to be sure it is deleted later. */ new FindAndDeleteProtectedNodeInBackground(client, ZKPaths.getPathAndNode(adjustedPath).getPath(), protectedMode.protectedId()).execute(); /* * The current UUID is scheduled to be deleted, it is not safe to use it again. * If this builder is used again later create a new UUID */ protectedMode.resetProtectedId(); } throw e; } } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("CreateBuilderImpl-Background"); final byte[] data = operationAndData.getData().getData(); AsyncCallback.Create2Callback callback = new AsyncCallback.Create2Callback() { @Override public void processResult(int rc, String path, Object ctx, String name, Stat stat) { trace.setReturnCode(rc).setRequestBytesLength(data).setPath(path).commit(); if ( (stat != null) && (storingStat != null) ) { DataTree.copyStat(stat, storingStat); } if ( (rc == KeeperException.Code.NONODE.intValue()) && createParentsIfNeeded ) { backgroundCreateParentsThenNode(client, operationAndData, operationAndData.getData().getPath(), backgrounding, acling.getACLProviderForParents(), createParentsAsContainers); } else if ( (rc == KeeperException.Code.NODEEXISTS.intValue()) && setDataIfExists ) { backgroundSetData(client, operationAndData, operationAndData.getData().getPath(), backgrounding); } else if ( (rc == KeeperException.Code.NODEEXISTS.intValue()) && idempotent ) { backgroundCheckIdempotent(client, operationAndData, operationAndData.getData().getPath(), backgrounding); } else { sendBackgroundResponse(rc, path, ctx, name, stat, operationAndData); } } }; client.getZooKeeper().create ( operationAndData.getData().getPath(), data, acling.getAclList(operationAndData.getData().getPath()), createMode, callback, backgrounding.getContext(), ttl ); } catch ( Throwable e ) { backgrounding.checkError(e, null); } } @Override public CreateProtectACLCreateModePathAndBytesable storingStatIn(Stat stat) { storingStat = stat; return new CreateProtectACLCreateModePathAndBytesable() { @Override public BackgroundPathAndBytesable withACL(List aclList) { return CreateBuilderImpl.this.withACL(aclList); } @Override public BackgroundPathAndBytesable withACL(List aclList, boolean applyToParents) { return CreateBuilderImpl.this.withACL(aclList, applyToParents); } @Override public ErrorListenerPathAndBytesable inBackground() { return CreateBuilderImpl.this.inBackground(); } @Override public ErrorListenerPathAndBytesable inBackground(Object context) { return CreateBuilderImpl.this.inBackground(context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback) { return CreateBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context) { return CreateBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, executor); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, context, executor); } @Override public String forPath(String path, byte[] data) throws Exception { return CreateBuilderImpl.this.forPath(path, data); } @Override public String forPath(String path) throws Exception { return CreateBuilderImpl.this.forPath(path); } @Override public ACLBackgroundPathAndBytesable withMode(CreateMode mode) { return CreateBuilderImpl.this.withMode(mode); } @Override public ACLCreateModeBackgroundPathAndBytesable withProtection() { return CreateBuilderImpl.this.withProtection(); } @Override public ProtectACLCreateModePathAndBytesable creatingParentsIfNeeded() { return CreateBuilderImpl.this.creatingParentsIfNeeded(); } @Override public ProtectACLCreateModePathAndBytesable creatingParentContainersIfNeeded() { return CreateBuilderImpl.this.creatingParentContainersIfNeeded(); } }; } static void backgroundCreateParentsThenNode(final CuratorFrameworkImpl client, final OperationAndData mainOperationAndData, final String path, Backgrounding backgrounding, final InternalACLProvider aclProvider, final boolean createParentsAsContainers) { BackgroundOperation operation = new BackgroundOperation() { @Override public void performBackgroundOperation(OperationAndData dummy) throws Exception { try { ZKPaths.mkdirs(client.getZooKeeper(), path, false, aclProvider, createParentsAsContainers); } catch ( KeeperException e ) { if ( !client.getZookeeperClient().getRetryPolicy().allowRetry(e) ) { sendBackgroundResponse(client, e.code().intValue(), e.getPath(), null, null, null, mainOperationAndData); throw e; } // otherwise safe to ignore as it will get retried } client.queueOperation(mainOperationAndData); } }; OperationAndData parentOperation = new OperationAndData<>(operation, mainOperationAndData.getData(), null, null, backgrounding.getContext(), null); client.queueOperation(parentOperation); } private void backgroundSetData(final CuratorFrameworkImpl client, final OperationAndData mainOperationAndData, final String path, final Backgrounding backgrounding) { final AsyncCallback.StatCallback statCallback = new AsyncCallback.StatCallback() { @Override public void processResult(int rc, String path, Object ctx, Stat stat) { if ( rc == KeeperException.Code.NONODE.intValue() ) { client.queueOperation(mainOperationAndData); // try to create it again } else { sendBackgroundResponse(rc, path, ctx, path, stat, mainOperationAndData); } } }; BackgroundOperation operation = new BackgroundOperation() { @Override public void performBackgroundOperation(OperationAndData op) throws Exception { try { client.getZooKeeper().setData(path, mainOperationAndData.getData().getData(), setDataIfExistsVersion, statCallback, backgrounding.getContext()); } catch ( KeeperException e ) { // ignore } } }; client.queueOperation(new OperationAndData<>(operation, null, null, null, null, null)); } private void backgroundCheckIdempotent(final CuratorFrameworkImpl client, final OperationAndData mainOperationAndData, final String path, final Backgrounding backgrounding) { final AsyncCallback.DataCallback dataCallback = new AsyncCallback.DataCallback() { @Override public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { if ( rc == KeeperException.Code.NONODE.intValue() ) { client.queueOperation(mainOperationAndData); // try to create it again } else { if ( rc == KeeperException.Code.OK.intValue() ) { if ( failNextIdempotentCheckForTesting ) { failNextIdempotentCheckForTesting = false; rc = KeeperException.Code.CONNECTIONLOSS.intValue(); } else if ( !IdempotentUtils.matches(0, mainOperationAndData.getData().getData(), stat.getVersion(), data) ) { rc = KeeperException.Code.NODEEXISTS.intValue(); } } sendBackgroundResponse(rc, path, ctx, path, stat, mainOperationAndData); } } }; BackgroundOperation operation = new BackgroundOperation() { @Override public void performBackgroundOperation(OperationAndData op) throws Exception { try { client.getZooKeeper().getData(path, false, dataCallback, backgrounding.getContext()); } catch ( KeeperException e ) { // ignore client.logError("Unexpected exception in async idempotent check for, ignoring: " + path, e); } } }; client.queueOperation(new OperationAndData<>(operation, null, null, null, null, null)); } private void sendBackgroundResponse(int rc, String path, Object ctx, String name, Stat stat, OperationAndData operationAndData) { sendBackgroundResponse(client, rc, path, ctx, name, stat, operationAndData); } private static void sendBackgroundResponse(CuratorFrameworkImpl client, int rc, String path, Object ctx, String name, Stat stat, OperationAndData operationAndData) { CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.CREATE, rc, path, name, ctx, stat, null, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } private ACLCreateModePathAndBytesable asACLCreateModePathAndBytesable() { return new ACLCreateModePathAndBytesable() { @Override public PathAndBytesable withACL(List aclList) { return CreateBuilderImpl.this.withACL(aclList); } @Override public PathAndBytesable withACL(List aclList, boolean applyToParents) { CreateBuilderImpl.this.withACL(aclList, applyToParents); return this; } @Override public ACLPathAndBytesable withMode(CreateMode mode) { createMode = mode; return new ACLPathAndBytesable() { @Override public PathAndBytesable withACL(List aclList) { return CreateBuilderImpl.this.withACL(aclList); } @Override public PathAndBytesable withACL(List aclList, boolean applyToParents) { return CreateBuilderImpl.this.withACL(aclList, applyToParents); } @Override public String forPath(String path, byte[] data) throws Exception { return CreateBuilderImpl.this.forPath(path, data); } @Override public String forPath(String path) throws Exception { return CreateBuilderImpl.this.forPath(path); } }; } @Override public String forPath(String path, byte[] data) throws Exception { return CreateBuilderImpl.this.forPath(path, data); } @Override public String forPath(String path) throws Exception { return CreateBuilderImpl.this.forPath(path); } }; } private CreateBackgroundModeACLable asCreateBackgroundModeACLable() { return new CreateBackgroundModeACLable() { @Override public BackgroundPathAndBytesable withACL(List aclList) { return CreateBuilderImpl.this.withACL(aclList); } @Override public BackgroundPathAndBytesable withACL(List aclList, boolean applyToParents) { return CreateBuilderImpl.this.withACL(aclList, applyToParents); } @Override public ACLBackgroundPathAndBytesable withMode(CreateMode mode) { return CreateBuilderImpl.this.withMode(mode); } @Override public String forPath(String path) throws Exception { return CreateBuilderImpl.this.forPath(path); } @Override public String forPath(String path, byte[] data) throws Exception { return CreateBuilderImpl.this.forPath(path, data); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, context, executor); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, executor); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context) { return CreateBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback) { return CreateBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerPathAndBytesable inBackground(Object context) { return CreateBuilderImpl.this.inBackground(context); } @Override public ErrorListenerPathAndBytesable inBackground() { return CreateBuilderImpl.this.inBackground(); } @Override public ACLPathAndBytesable withProtectedEphemeralSequential() { return CreateBuilderImpl.this.withProtectedEphemeralSequential(); } @Override public ACLCreateModePathAndBytesable creatingParentsIfNeeded() { createParentsIfNeeded = true; return asACLCreateModePathAndBytesable(); } @Override public ACLCreateModePathAndBytesable creatingParentContainersIfNeeded() { setCreateParentsAsContainers(); return asACLCreateModePathAndBytesable(); } }; } private ACLCreateModeStatBackgroundPathAndBytesable asACLCreateModeStatBackgroundPathAndBytesable() { return new ACLCreateModeStatBackgroundPathAndBytesable() { @Override public BackgroundPathAndBytesable withACL(List aclList) { return CreateBuilderImpl.this.withACL(aclList); } @Override public BackgroundPathAndBytesable withACL(List aclList, boolean applyToParents) { CreateBuilderImpl.this.withACL(aclList, applyToParents); return this; } @Override public ErrorListenerPathAndBytesable inBackground() { return CreateBuilderImpl.this.inBackground(); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, context, executor); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Executor executor) { return CreateBuilderImpl.this.inBackground(callback, executor); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context) { return CreateBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback) { return CreateBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerPathAndBytesable inBackground(Object context) { return CreateBuilderImpl.this.inBackground(context); } @Override public String forPath(String path) throws Exception { return CreateBuilderImpl.this.forPath(path); } @Override public String forPath(String path, byte[] data) throws Exception { return CreateBuilderImpl.this.forPath(path, data); } @Override public ACLBackgroundPathAndBytesable withMode(CreateMode mode) { return CreateBuilderImpl.this.withMode(mode); } @Override public ACLCreateModeBackgroundPathAndBytesable storingStatIn(Stat stat) { storingStat = stat; return CreateBuilderImpl.this; } }; } @VisibleForTesting volatile boolean debugForceFindProtectedNode = false; private void pathInBackground(final String path, final byte[] data, final String givenPath) { final AtomicBoolean firstTime = new AtomicBoolean(true); OperationAndData operationAndData = new OperationAndData(this, new PathAndBytes(path, data), backgrounding.getCallback(), new OperationAndData.ErrorCallback() { public void retriesExhausted(OperationAndData operationAndData) { if ( protectedMode.doProtected() ) { // all retries have failed, findProtectedNodeInForeground(..) included, schedule a clean up new FindAndDeleteProtectedNodeInBackground(client, ZKPaths.getPathAndNode(path).getPath(), protectedMode.protectedId()).execute(); // assign a new id if this builder is used again later protectedMode.resetProtectedId(); } } }, backgrounding.getContext(), null) { @Override void callPerformBackgroundOperation() throws Exception { boolean callSuper = true; boolean localFirstTime = firstTime.getAndSet(false) && !debugForceFindProtectedNode; protectedMode.checkSetSessionId(client, createMode); if ( !localFirstTime && protectedMode.doProtected() ) { debugForceFindProtectedNode = false; String createdPath = null; try { createdPath = findProtectedNodeInForeground(path); } catch ( KeeperException.ConnectionLossException e ) { sendBackgroundResponse(KeeperException.Code.CONNECTIONLOSS.intValue(), path, backgrounding.getContext(), null, null, this); callSuper = false; } if ( createdPath != null ) { try { sendBackgroundResponse(KeeperException.Code.OK.intValue(), createdPath, backgrounding.getContext(), createdPath, null, this); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); client.logError("Processing protected create for path: " + givenPath, e); } callSuper = false; } } if ( failBeforeNextCreateForTesting ) { failBeforeNextCreateForTesting = false; throw new KeeperException.ConnectionLossException(); } if ( failNextCreateForTesting ) { failNextCreateForTesting = false; try { // simulate success on server without notification to client // if another error occurs in pathInForeground that isn't NodeExists, this hangs instead of fully propagating the error. Likely not worth fixing though. pathInForeground(path, data, acling.getAclList(path)); } catch ( KeeperException.NodeExistsException e ) { client.logError("NodeExists while injecting failure after create, ignoring: " + givenPath, e); } throw new KeeperException.ConnectionLossException(); } if ( callSuper ) { super.callPerformBackgroundOperation(); } } }; client.processBackgroundOperation(operationAndData, null); } private String pathInForeground(final String path, final byte[] data, final List aclList) throws Exception { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("CreateBuilderImpl-Foreground"); final AtomicBoolean firstTime = new AtomicBoolean(true); String returnPath = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public String call() throws Exception { boolean localFirstTime = firstTime.getAndSet(false) && !debugForceFindProtectedNode; protectedMode.checkSetSessionId(client, createMode); String createdPath = null; if ( !localFirstTime && protectedMode.doProtected() ) { debugForceFindProtectedNode = false; createdPath = findProtectedNodeInForeground(path); } if ( createdPath == null ) { try { if ( failBeforeNextCreateForTesting ) { failBeforeNextCreateForTesting = false; throw new KeeperException.ConnectionLossException(); } createdPath = client.getZooKeeper().create(path, data, aclList, createMode, storingStat, ttl); } catch ( KeeperException.NoNodeException e ) { if ( createParentsIfNeeded ) { ZKPaths.mkdirs(client.getZooKeeper(), path, false, acling.getACLProviderForParents(), createParentsAsContainers); createdPath = client.getZooKeeper().create(path, data, acling.getAclList(path), createMode, storingStat, ttl); } else { throw e; } } catch ( KeeperException.NodeExistsException e ) { if ( setDataIfExists ) { Stat setStat = client.getZooKeeper().setData(path, data, setDataIfExistsVersion); if ( storingStat != null ) { DataTree.copyStat(setStat, storingStat); } createdPath = path; } else if ( idempotent ) { if ( failNextIdempotentCheckForTesting ) { failNextIdempotentCheckForTesting = false; throw new KeeperException.ConnectionLossException(); } Stat getStat = new Stat(); byte[] existingData = client.getZooKeeper().getData(path, false, getStat); // check to see if data version == 0 and data matches the idempotent case if ( IdempotentUtils.matches(0, data, getStat.getVersion(), existingData) ) { if ( storingStat != null ) { DataTree.copyStat(getStat, storingStat); } createdPath = path; } else { throw e; } } else { throw e; } } } if ( failNextCreateForTesting ) { failNextCreateForTesting = false; throw new KeeperException.ConnectionLossException(); } return createdPath; } } ); trace.setRequestBytesLength(data).setPath(path).commit(); return returnPath; } private String findProtectedNodeInForeground(final String path) throws Exception { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("CreateBuilderImpl-findProtectedNodeInForeground"); String returnPath = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public String call() throws Exception { String foundNode = null; try { final ZKPaths.PathAndNode pathAndNode = ZKPaths.getPathAndNode(path); List children = client.getZooKeeper().getChildren(pathAndNode.getPath(), false); foundNode = findNode(children, pathAndNode.getPath(), protectedMode.protectedId()); log.debug("Protected mode findNode result: {}", foundNode); foundNode = protectedMode.validateFoundNode(client, createMode, foundNode); } catch ( KeeperException.NoNodeException ignore ) { // ignore } return foundNode; } } ); trace.setPath(path).commit(); return returnPath; } @VisibleForTesting String adjustPath(String path) throws Exception { return ProtectedUtils.toProtectedZNodePath(path, protectedMode.protectedId()); } /** * Attempt to find the znode that matches the given path and protected id * * @param children a list of candidates znodes * @param path the path * @param protectedId the protected id * @return the absolute path of the znode or null if it is not found */ static String findNode(final List children, final String path, final String protectedId) { final String protectedPrefix = ProtectedUtils.getProtectedPrefix(protectedId); String foundNode = Iterables.find ( children, new Predicate() { @Override public boolean apply(String node) { return node.startsWith(protectedPrefix); } }, null ); if ( foundNode != null ) { foundNode = ZKPaths.makePath(path, foundNode); } return foundNode; } } CuratorEventImpl.java000066400000000000000000000077141442004423600366750ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.collect.ImmutableList; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.Arrays; import java.util.List; class CuratorEventImpl implements CuratorEvent { private final CuratorEventType type; private final int resultCode; private final String path; private final String name; private final List children; private final Object context; private final Stat stat; private final byte[] data; private final WatchedEvent watchedEvent; private final List aclList; private final List opResults; @Override public CuratorEventType getType() { return type; } @Override public int getResultCode() { return resultCode; } @Override public String getPath() { return path; } @Override public Object getContext() { return context; } @Override public Stat getStat() { return stat; } @Override public byte[] getData() { return data; } @Override public String getName() { return name; } @Override public List getChildren() { return children; } @Override public WatchedEvent getWatchedEvent() { return watchedEvent; } @Override public List getACLList() { return aclList; } @Override public List getOpResults() { return opResults; } @Override public String toString() { return "CuratorEventImpl{" + "type=" + type + ", resultCode=" + resultCode + ", path='" + path + '\'' + ", name='" + name + '\'' + ", children=" + children + ", context=" + context + ", stat=" + stat + ", data=" + Arrays.toString(data) + ", watchedEvent=" + watchedEvent + ", aclList=" + aclList + ", opResults=" + opResults + '}'; } CuratorEventImpl(CuratorFrameworkImpl client, CuratorEventType type, int resultCode, String path, String name, Object context, Stat stat, byte[] data, List children, WatchedEvent watchedEvent, List aclList, List opResults) { this.type = type; this.resultCode = resultCode; this.opResults = (opResults != null) ? ImmutableList.copyOf(opResults) : null; this.path = client.unfixForNamespace(path); this.name = client.unfixForNamespace(name); this.context = context; this.stat = stat; this.data = data; this.children = children; this.watchedEvent = (watchedEvent != null) ? new NamespaceWatchedEvent(client, watchedEvent) : null; this.aclList = (aclList != null) ? ImmutableList.copyOf(aclList) : null; } } CuratorFrameworkImpl.java000066400000000000000000001073551442004423600375530ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.DelayQueue; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.apache.curator.CuratorConnectionLossException; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.AuthInfo; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.api.CompressionProvider; import org.apache.curator.framework.api.CreateBuilder; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.api.DeleteBuilder; import org.apache.curator.framework.api.ExistsBuilder; import org.apache.curator.framework.api.GetACLBuilder; import org.apache.curator.framework.api.GetChildrenBuilder; import org.apache.curator.framework.api.GetConfigBuilder; import org.apache.curator.framework.api.GetDataBuilder; import org.apache.curator.framework.api.ReconfigBuilder; import org.apache.curator.framework.api.RemoveWatchesBuilder; import org.apache.curator.framework.api.SetACLBuilder; import org.apache.curator.framework.api.SetDataBuilder; import org.apache.curator.framework.api.SyncBuilder; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.api.WatchesBuilder; import org.apache.curator.framework.api.transaction.CuratorMultiTransaction; import org.apache.curator.framework.api.transaction.CuratorTransaction; import org.apache.curator.framework.api.transaction.TransactionOp; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.schema.SchemaSet; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateErrorPolicy; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.framework.state.ConnectionStateManager; import org.apache.curator.utils.Compatibility; import org.apache.curator.utils.DebugUtils; import org.apache.curator.utils.EnsurePath; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.curator.utils.ZookeeperFactory; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; public class CuratorFrameworkImpl implements CuratorFramework { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorZookeeperClient client; private final StandardListenerManager listeners; private final StandardListenerManager unhandledErrorListeners; private final ThreadFactory threadFactory; private final int maxCloseWaitMs; private final BlockingQueue> backgroundOperations; private final BlockingQueue> forcedSleepOperations; private final NamespaceImpl namespace; private final ConnectionStateManager connectionStateManager; private final List authInfos; private final byte[] defaultData; private final FailedDeleteManager failedDeleteManager; private final FailedRemoveWatchManager failedRemoveWatcherManager; private final CompressionProvider compressionProvider; private final ACLProvider aclProvider; private final NamespaceFacadeCache namespaceFacadeCache; private final boolean useContainerParentsIfAvailable; private final ConnectionStateErrorPolicy connectionStateErrorPolicy; private final AtomicLong currentInstanceIndex = new AtomicLong(-1); private final InternalConnectionHandler internalConnectionHandler; private final EnsembleTracker ensembleTracker; private final SchemaSet schemaSet; private final Executor runSafeService; private volatile ExecutorService executorService; private final AtomicBoolean logAsErrorConnectionErrors = new AtomicBoolean(false); private static final boolean LOG_ALL_CONNECTION_ISSUES_AS_ERROR_LEVEL = !Boolean.getBoolean(DebugUtils.PROPERTY_LOG_ONLY_FIRST_CONNECTION_ISSUE_AS_ERROR_LEVEL); interface DebugBackgroundListener { void listen(OperationAndData data); } volatile DebugBackgroundListener debugListener = null; @VisibleForTesting public volatile UnhandledErrorListener debugUnhandledErrorListener = null; private final AtomicReference state; public CuratorFrameworkImpl(CuratorFrameworkFactory.Builder builder) { ZookeeperFactory localZookeeperFactory = makeZookeeperFactory(builder.getZookeeperFactory(), builder.getZkClientConfig()); this.client = new CuratorZookeeperClient ( localZookeeperFactory, builder.getEnsembleProvider(), builder.getSessionTimeoutMs(), builder.getConnectionTimeoutMs(), builder.getWaitForShutdownTimeoutMs(), new Watcher() { @Override public void process(WatchedEvent watchedEvent) { CuratorEvent event = new CuratorEventImpl(CuratorFrameworkImpl.this, CuratorEventType.WATCHED, watchedEvent.getState().getIntValue(), unfixForNamespace(watchedEvent.getPath()), null, null, null, null, null, watchedEvent, null, null); processEvent(event); } }, builder.getRetryPolicy(), builder.canBeReadOnly() ); internalConnectionHandler = new StandardInternalConnectionHandler(); listeners = StandardListenerManager.standard(); unhandledErrorListeners = StandardListenerManager.standard(); backgroundOperations = new DelayQueue>(); forcedSleepOperations = new LinkedBlockingQueue<>(); namespace = new NamespaceImpl(this, builder.getNamespace()); threadFactory = getThreadFactory(builder); maxCloseWaitMs = builder.getMaxCloseWaitMs(); connectionStateManager = new ConnectionStateManager(this, builder.getThreadFactory(), builder.getSessionTimeoutMs(), builder.getSimulatedSessionExpirationPercent(), builder.getConnectionStateListenerManagerFactory()); compressionProvider = builder.getCompressionProvider(); aclProvider = builder.getAclProvider(); state = new AtomicReference(CuratorFrameworkState.LATENT); useContainerParentsIfAvailable = builder.useContainerParentsIfAvailable(); connectionStateErrorPolicy = Preconditions.checkNotNull(builder.getConnectionStateErrorPolicy(), "errorPolicy cannot be null"); schemaSet = Preconditions.checkNotNull(builder.getSchemaSet(), "schemaSet cannot be null"); byte[] builderDefaultData = builder.getDefaultData(); defaultData = (builderDefaultData != null) ? Arrays.copyOf(builderDefaultData, builderDefaultData.length) : new byte[0]; authInfos = buildAuths(builder); failedDeleteManager = new FailedDeleteManager(this); failedRemoveWatcherManager = new FailedRemoveWatchManager(this); namespaceFacadeCache = new NamespaceFacadeCache(this); ensembleTracker = builder.withEnsembleTracker() ? new EnsembleTracker(this, builder.getEnsembleProvider()) : null; runSafeService = makeRunSafeService(builder); } private Executor makeRunSafeService(CuratorFrameworkFactory.Builder builder) { if ( builder.getRunSafeService() != null ) { return builder.getRunSafeService(); } ThreadFactory threadFactory = builder.getThreadFactory(); if ( threadFactory == null ) { threadFactory = ThreadUtils.newThreadFactory("SafeNotifyService"); } return Executors.newSingleThreadExecutor(threadFactory); } private List buildAuths(CuratorFrameworkFactory.Builder builder) { ImmutableList.Builder builder1 = ImmutableList.builder(); if ( builder.getAuthInfos() != null ) { builder1.addAll(builder.getAuthInfos()); } return builder1.build(); } @Override public CompletableFuture runSafe(Runnable runnable) { return CompletableFuture.runAsync(runnable, runSafeService); } @Override public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework() { return new WatcherRemovalFacade(this); } @Override public QuorumVerifier getCurrentConfig() { return (ensembleTracker != null) ? ensembleTracker.getCurrentConfig() : null; } private ZookeeperFactory makeZookeeperFactory(final ZookeeperFactory actualZookeeperFactory, ZKClientConfig zkClientConfig) { return new ZookeeperFactory() { @Override public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws Exception { ZooKeeper zooKeeper = actualZookeeperFactory.newZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly, zkClientConfig); addAuthInfos(zooKeeper); return zooKeeper; } }; } private void addAuthInfos(ZooKeeper zooKeeper) { for ( AuthInfo auth: authInfos) { zooKeeper.addAuthInfo(auth.getScheme(), auth.getAuth()); } } private ThreadFactory getThreadFactory(CuratorFrameworkFactory.Builder builder) { ThreadFactory threadFactory = builder.getThreadFactory(); if ( threadFactory == null ) { threadFactory = ThreadUtils.newThreadFactory("Framework"); } return threadFactory; } protected CuratorFrameworkImpl(CuratorFrameworkImpl parent) { client = parent.client; listeners = parent.listeners; unhandledErrorListeners = parent.unhandledErrorListeners; threadFactory = parent.threadFactory; maxCloseWaitMs = parent.maxCloseWaitMs; backgroundOperations = parent.backgroundOperations; forcedSleepOperations = parent.forcedSleepOperations; connectionStateManager = parent.connectionStateManager; defaultData = parent.defaultData; failedDeleteManager = parent.failedDeleteManager; failedRemoveWatcherManager = parent.failedRemoveWatcherManager; compressionProvider = parent.compressionProvider; aclProvider = parent.aclProvider; namespaceFacadeCache = parent.namespaceFacadeCache; namespace = new NamespaceImpl(this, null); state = parent.state; authInfos = parent.authInfos; useContainerParentsIfAvailable = parent.useContainerParentsIfAvailable; connectionStateErrorPolicy = parent.connectionStateErrorPolicy; internalConnectionHandler = parent.internalConnectionHandler; schemaSet = parent.schemaSet; ensembleTracker = null; runSafeService = parent.runSafeService; } @Override public void createContainers(String path) throws Exception { checkExists().creatingParentContainersIfNeeded().forPath(ZKPaths.makePath(path, "foo")); } @Override public void clearWatcherReferences(Watcher watcher) { // NOP } @Override public CuratorFrameworkState getState() { return state.get(); } @Override @Deprecated public boolean isStarted() { return state.get() == CuratorFrameworkState.STARTED; } @Override public boolean blockUntilConnected(int maxWaitTime, TimeUnit units) throws InterruptedException { return connectionStateManager.blockUntilConnected(maxWaitTime, units); } @Override public void blockUntilConnected() throws InterruptedException { blockUntilConnected(0, null); } @Override public ConnectionStateErrorPolicy getConnectionStateErrorPolicy() { return connectionStateErrorPolicy; } @Override public void start() { log.info("Starting"); if ( !state.compareAndSet(CuratorFrameworkState.LATENT, CuratorFrameworkState.STARTED) ) { throw new IllegalStateException("Cannot be started more than once"); } try { connectionStateManager.start(); // ordering dependency - must be called before client.start() final ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( ConnectionState.CONNECTED == newState || ConnectionState.RECONNECTED == newState ) { logAsErrorConnectionErrors.set(true); } } @Override public boolean doNotProxy() { return true; } }; this.getConnectionStateListenable().addListener(listener); client.start(); executorService = Executors.newSingleThreadScheduledExecutor(threadFactory); executorService.submit(new Callable() { @Override public Object call() throws Exception { backgroundOperationsLoop(); return null; } }); if ( ensembleTracker != null ) { ensembleTracker.start(); } log.info(schemaSet.toDocumentation()); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleBackgroundOperationException(null, e); } } @Override public void close() { log.debug("Closing"); if ( state.compareAndSet(CuratorFrameworkState.STARTED, CuratorFrameworkState.STOPPED) ) { listeners.forEach(listener -> { CuratorEvent event = new CuratorEventImpl(CuratorFrameworkImpl.this, CuratorEventType.CLOSING, 0, null, null, null, null, null, null, null, null, null); try { listener.eventReceived(CuratorFrameworkImpl.this, event); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Exception while sending Closing event", e); } }); if ( executorService != null ) { executorService.shutdownNow(); try { executorService.awaitTermination(maxCloseWaitMs, TimeUnit.MILLISECONDS); } catch ( InterruptedException e ) { // Interrupted while interrupting; I give up. Thread.currentThread().interrupt(); } } if ( ensembleTracker != null ) { ensembleTracker.close(); } listeners.clear(); unhandledErrorListeners.clear(); connectionStateManager.close(); client.close(); } } @Override @Deprecated public CuratorFramework nonNamespaceView() { return usingNamespace(null); } @Override public String getNamespace() { String str = namespace.getNamespace(); return (str != null) ? str : ""; } private void checkState() { CuratorFrameworkState state = getState(); Preconditions.checkState(state == CuratorFrameworkState.STARTED, "Expected state [%s] was [%s]", CuratorFrameworkState.STARTED, state); } @Override public CuratorFramework usingNamespace(String newNamespace) { checkState(); return namespaceFacadeCache.get(newNamespace); } @Override public CreateBuilder create() { checkState(); return new CreateBuilderImpl(this); } @Override public DeleteBuilder delete() { checkState(); return new DeleteBuilderImpl(this); } @Override public ExistsBuilder checkExists() { checkState(); return new ExistsBuilderImpl(this); } @Override public GetDataBuilder getData() { checkState(); return new GetDataBuilderImpl(this); } @Override public SetDataBuilder setData() { checkState(); return new SetDataBuilderImpl(this); } @Override public GetChildrenBuilder getChildren() { checkState(); return new GetChildrenBuilderImpl(this); } @Override public GetACLBuilder getACL() { checkState(); return new GetACLBuilderImpl(this); } @Override public SetACLBuilder setACL() { checkState(); return new SetACLBuilderImpl(this); } @Override public ReconfigBuilder reconfig() { return new ReconfigBuilderImpl(this); } @Override public GetConfigBuilder getConfig() { return new GetConfigBuilderImpl(this); } @Override public CuratorTransaction inTransaction() { checkState(); return new CuratorTransactionImpl(this); } @Override public CuratorMultiTransaction transaction() { checkState(); return new CuratorMultiTransactionImpl(this); } @Override public TransactionOp transactionOp() { checkState(); return new TransactionOpImpl(this); } @Override public Listenable getConnectionStateListenable() { return connectionStateManager.getListenable(); } @Override public Listenable getCuratorListenable() { return listeners; } @Override public Listenable getUnhandledErrorListenable() { return unhandledErrorListeners; } @Override public void sync(String path, Object context) { checkState(); path = fixForNamespace(path); internalSync(this, path, context); } @Override public SyncBuilder sync() { return new SyncBuilderImpl(this); } @Override public RemoveWatchesBuilder watches() { return new RemoveWatchesBuilderImpl(this); } @Override public WatchesBuilder watchers() { Preconditions.checkState(Compatibility.hasPersistentWatchers(), "watchers() is not supported in the ZooKeeper library being used. Use watches() instead."); return new WatchesBuilderImpl(this); } protected void internalSync(CuratorFrameworkImpl impl, String path, Object context) { BackgroundOperation operation = new BackgroundSyncImpl(impl, context); performBackgroundOperation(new OperationAndData(operation, path, null, null, context, null)); } @Override public CuratorZookeeperClient getZookeeperClient() { return client; } @Override public EnsurePath newNamespaceAwareEnsurePath(String path) { return namespace.newNamespaceAwareEnsurePath(path); } @Override public SchemaSet getSchemaSet() { return schemaSet; } ACLProvider getAclProvider() { return aclProvider; } FailedDeleteManager getFailedDeleteManager() { return failedDeleteManager; } FailedRemoveWatchManager getFailedRemoveWatcherManager() { return failedRemoveWatcherManager; } RetryLoop newRetryLoop() { return client.newRetryLoop(); } ZooKeeper getZooKeeper() throws Exception { return client.getZooKeeper(); } CompressionProvider getCompressionProvider() { return compressionProvider; } boolean useContainerParentsIfAvailable() { return useContainerParentsIfAvailable; } void processBackgroundOperation(OperationAndData operationAndData, CuratorEvent event) { boolean isInitialExecution = (event == null); if ( isInitialExecution ) { performBackgroundOperation(operationAndData); return; } boolean doQueueOperation = false; do { KeeperException.Code code = KeeperException.Code.get(event.getResultCode()); if ( (code != KeeperException.Code.OK) && getZookeeperClient().getRetryPolicy().allowRetry(KeeperException.create(code)) ) { doQueueOperation = checkBackgroundRetry(operationAndData, event); break; } if ( operationAndData.getCallback() != null ) { sendToBackgroundCallback(operationAndData, event); break; } processEvent(event); } while ( false ); if ( doQueueOperation ) { queueOperation(operationAndData); } } /** * @param operationAndData operation entry * @return true if the operation was actually queued, false if not */ boolean queueOperation(OperationAndData operationAndData) { if ( getState() == CuratorFrameworkState.STARTED ) { backgroundOperations.offer(operationAndData); return true; } return false; } void logError(String reason, final Throwable e) { if ( (reason == null) || (reason.length() == 0) ) { reason = "n/a"; } if ( !Boolean.getBoolean(DebugUtils.PROPERTY_DONT_LOG_CONNECTION_ISSUES) || !(e instanceof KeeperException) ) { if ( e instanceof KeeperException.ConnectionLossException ) { if ( LOG_ALL_CONNECTION_ISSUES_AS_ERROR_LEVEL || logAsErrorConnectionErrors.compareAndSet(true, false) ) { log.error(reason, e); } else { log.debug(reason, e); } } else { log.error(reason, e); } } final String localReason = reason; unhandledErrorListeners.forEach(l -> l.unhandledError(localReason, e)); if ( debugUnhandledErrorListener != null ) { debugUnhandledErrorListener.unhandledError(reason, e); } } String unfixForNamespace(String path) { return namespace.unfixForNamespace(path); } String fixForNamespace(String path) { return namespace.fixForNamespace(path, false); } String fixForNamespace(String path, boolean isSequential) { return namespace.fixForNamespace(path, isSequential); } byte[] getDefaultData() { return defaultData; } NamespaceFacadeCache getNamespaceFacadeCache() { return namespaceFacadeCache; } void validateConnection(Watcher.Event.KeeperState state) { if ( state == Watcher.Event.KeeperState.Disconnected ) { internalConnectionHandler.suspendConnection(this); } else if ( state == Watcher.Event.KeeperState.Expired ) { connectionStateManager.addStateChange(ConnectionState.LOST); } else if ( state == Watcher.Event.KeeperState.SyncConnected ) { internalConnectionHandler.checkNewConnection(this); connectionStateManager.addStateChange(ConnectionState.RECONNECTED); unSleepBackgroundOperations(); } else if ( state == Watcher.Event.KeeperState.ConnectedReadOnly ) { internalConnectionHandler.checkNewConnection(this); connectionStateManager.addStateChange(ConnectionState.READ_ONLY); } } void checkInstanceIndex() { long instanceIndex = client.getInstanceIndex(); long newInstanceIndex = currentInstanceIndex.getAndSet(instanceIndex); if ( (newInstanceIndex >= 0) && (instanceIndex != newInstanceIndex) ) // currentInstanceIndex is initially -1 - ignore this { connectionStateManager.addStateChange(ConnectionState.LOST); } } Watcher.Event.KeeperState codeToState(KeeperException.Code code) { switch ( code ) { case AUTHFAILED: case NOAUTH: { return Watcher.Event.KeeperState.AuthFailed; } case CONNECTIONLOSS: case OPERATIONTIMEOUT: { return Watcher.Event.KeeperState.Disconnected; } case SESSIONEXPIRED: { return Watcher.Event.KeeperState.Expired; } case OK: case SESSIONMOVED: { return Watcher.Event.KeeperState.SyncConnected; } } return Watcher.Event.KeeperState.fromInt(-1); } WatcherRemovalManager getWatcherRemovalManager() { return null; } boolean setToSuspended() { return connectionStateManager.setToSuspended(); } void addStateChange(ConnectionState newConnectionState) { connectionStateManager.addStateChange(newConnectionState); } EnsembleTracker getEnsembleTracker() { return ensembleTracker; } @VisibleForTesting volatile CountDownLatch debugCheckBackgroundRetryLatch; @VisibleForTesting volatile CountDownLatch debugCheckBackgroundRetryReadyLatch; @VisibleForTesting volatile KeeperException.Code injectedCode; @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) private boolean checkBackgroundRetry(OperationAndData operationAndData, CuratorEvent event) { boolean doRetry = false; if ( client.getRetryPolicy().allowRetry(operationAndData.getThenIncrementRetryCount(), operationAndData.getElapsedTimeMs(), operationAndData) ) { doRetry = true; } else { if ( operationAndData.getErrorCallback() != null ) { operationAndData.getErrorCallback().retriesExhausted(operationAndData); } if ( operationAndData.getCallback() != null ) { sendToBackgroundCallback(operationAndData, event); } KeeperException.Code code = KeeperException.Code.get(event.getResultCode()); Exception e = null; try { e = (code != null) ? KeeperException.create(code) : null; } catch ( Throwable t ) { ThreadUtils.checkInterrupted(t); } if ( e == null ) { e = new Exception("Unknown result codegetResultCode()"); } if ( debugCheckBackgroundRetryLatch != null ) // scaffolding to test CURATOR-525 { if ( debugCheckBackgroundRetryReadyLatch != null ) { debugCheckBackgroundRetryReadyLatch.countDown(); } try { debugCheckBackgroundRetryLatch.await(); if (injectedCode != null) { code = injectedCode; } } catch ( InterruptedException ex ) { Thread.currentThread().interrupt(); } } validateConnection(codeToState(code)); logError("Background operation retry gave up", e); } return doRetry; } private void sendToBackgroundCallback(OperationAndData operationAndData, CuratorEvent event) { try { operationAndData.getCallback().processResult(this, event); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleBackgroundOperationException(operationAndData, e); } } private void handleBackgroundOperationException(OperationAndData operationAndData, Throwable e) { do { if ( (operationAndData != null) && getZookeeperClient().getRetryPolicy().allowRetry(e) ) { if ( !Boolean.getBoolean(DebugUtils.PROPERTY_DONT_LOG_CONNECTION_ISSUES) ) { log.debug("Retry-able exception received", e); } if ( client.getRetryPolicy().allowRetry(operationAndData.getThenIncrementRetryCount(), operationAndData.getElapsedTimeMs(), operationAndData) ) { if ( !Boolean.getBoolean(DebugUtils.PROPERTY_DONT_LOG_CONNECTION_ISSUES) ) { log.debug("Retrying operation"); } backgroundOperations.offer(operationAndData); break; } else { if ( !Boolean.getBoolean(DebugUtils.PROPERTY_DONT_LOG_CONNECTION_ISSUES) ) { log.debug("Retry policy did not allow retry"); } if ( operationAndData.getErrorCallback() != null ) { operationAndData.getErrorCallback().retriesExhausted(operationAndData); } } } logError("Background exception was not retry-able or retry gave up", e); } while ( false ); } private void backgroundOperationsLoop() { try { while ( state.get() == CuratorFrameworkState.STARTED ) { OperationAndData operationAndData; try { operationAndData = backgroundOperations.take(); if ( debugListener != null ) { debugListener.listen(operationAndData); } performBackgroundOperation(operationAndData); } catch ( InterruptedException e ) { // swallow the interrupt as it's only possible from either a background // operation and, thus, doesn't apply to this loop or the instance // is being closed in which case the while test will get it } } } finally { log.info("backgroundOperationsLoop exiting"); } } void performBackgroundOperation(OperationAndData operationAndData) { try { if ( !operationAndData.isConnectionRequired() || client.isConnected() ) { operationAndData.callPerformBackgroundOperation(); return; } client.getZooKeeper(); // important - allow connection resets, timeouts, etc. to occur if ( operationAndData.getElapsedTimeMs() < client.getConnectionTimeoutMs() ) { sleepAndQueueOperation(operationAndData); return; } /* * Fix edge case reported as CURATOR-52. Connection timeout is detected when the initial (or previously failed) connection * cannot be re-established. This needs to be run through the retry policy and callbacks need to get invoked, etc. */ WatchedEvent watchedEvent = new WatchedEvent(Watcher.Event.EventType.None, Watcher.Event.KeeperState.Disconnected, null); CuratorEvent event = new CuratorEventImpl(this, CuratorEventType.WATCHED, KeeperException.Code.CONNECTIONLOSS.intValue(), null, null, operationAndData.getContext(), null, null, null, watchedEvent, null, null); if ( checkBackgroundRetry(operationAndData, event) ) { queueOperation(operationAndData); } else { logError("Background retry gave up", new CuratorConnectionLossException()); } } catch ( Throwable e ) { ThreadUtils.checkInterrupted(e); handleBackgroundOperationException(operationAndData, e); } } @VisibleForTesting volatile long sleepAndQueueOperationSeconds = 1; private void sleepAndQueueOperation(OperationAndData operationAndData) throws InterruptedException { operationAndData.sleepFor(sleepAndQueueOperationSeconds, TimeUnit.SECONDS); if ( queueOperation(operationAndData) ) { forcedSleepOperations.add(operationAndData); } } private void unSleepBackgroundOperations() { Collection> drain = new ArrayList<>(forcedSleepOperations.size()); forcedSleepOperations.drainTo(drain); log.debug("Clearing sleep for {} operations", drain.size()); for ( OperationAndData operation : drain ) { operation.clearSleep(); if ( backgroundOperations.remove(operation) ) // due to the internals of DelayQueue, operation must be removed/re-added so that re-sorting occurs { backgroundOperations.offer(operation); } } } private void processEvent(final CuratorEvent curatorEvent) { if ( curatorEvent.getType() == CuratorEventType.WATCHED ) { validateConnection(curatorEvent.getWatchedEvent().getState()); } listeners.forEach(listener -> { try { OperationTrace trace = client.startAdvancedTracer("EventListener"); listener.eventReceived(CuratorFrameworkImpl.this, curatorEvent); trace.commit(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); logError("Event listener threw exception", e); } }); } } CuratorFrameworkState.java000066400000000000000000000023451442004423600377230ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.framework.CuratorFramework; /** * @see CuratorFramework#getState() */ public enum CuratorFrameworkState { /** * {@link CuratorFramework#start()} has not yet been called */ LATENT, /** * {@link CuratorFramework#start()} has been called */ STARTED, /** * {@link CuratorFramework#close()} has been called */ STOPPED } CuratorMultiTransactionImpl.java000066400000000000000000000205561442004423600411130ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import org.apache.curator.RetryLoop; import org.apache.curator.TimeTrace; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.ErrorListenerMultiTransactionMain; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.api.transaction.CuratorMultiTransaction; import org.apache.curator.framework.api.transaction.CuratorMultiTransactionMain; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.framework.schema.Schema; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.SetDataRequest; import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class CuratorMultiTransactionImpl implements CuratorMultiTransaction, CuratorMultiTransactionMain, BackgroundOperation, ErrorListenerMultiTransactionMain { private final CuratorFrameworkImpl client; private Backgrounding backgrounding = new Backgrounding(); public CuratorMultiTransactionImpl(CuratorFrameworkImpl client) { this.client = client; } public CuratorMultiTransactionImpl(CuratorFrameworkImpl client, Backgrounding backgrounding) { this.client = client; this.backgrounding = backgrounding; } @Override public ErrorListenerMultiTransactionMain inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerMultiTransactionMain inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public ErrorListenerMultiTransactionMain inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerMultiTransactionMain inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerMultiTransactionMain inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(callback, executor); return this; } @Override public ErrorListenerMultiTransactionMain inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public CuratorMultiTransactionMain withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public List forOperations(CuratorOp... operations) throws Exception { List ops = (operations != null) ? Arrays.asList(operations) : Lists.newArrayList(); return forOperations(ops); } @Override public List forOperations(List operations) throws Exception { operations = Preconditions.checkNotNull(operations, "operations cannot be null"); Preconditions.checkArgument(!operations.isEmpty(), "operations list cannot be empty"); CuratorMultiTransactionRecord record = new CuratorMultiTransactionRecord(); for ( CuratorOp curatorOp : operations ) { Schema schema = client.getSchemaSet().getSchema(curatorOp.getTypeAndPath().getForPath()); record.add(curatorOp.get(), curatorOp.getTypeAndPath().getType(), curatorOp.getTypeAndPath().getForPath()); if ( (curatorOp.get().getType() == ZooDefs.OpCode.create) || (curatorOp.get().getType() == ZooDefs.OpCode.createContainer) ) { CreateRequest createRequest = (CreateRequest)curatorOp.get().toRequestRecord(); CreateMode createMode = CreateMode.fromFlag(createRequest.getFlags(), CreateMode.PERSISTENT); schema.validateCreate(createMode, createRequest.getPath(), createRequest.getData(), createRequest.getAcl()); } else if ( (curatorOp.get().getType() == ZooDefs.OpCode.delete) || (curatorOp.get().getType() == ZooDefs.OpCode.deleteContainer) ) { DeleteRequest deleteRequest = (DeleteRequest)curatorOp.get().toRequestRecord(); schema.validateDelete(deleteRequest.getPath()); } else if ( curatorOp.get().getType() == ZooDefs.OpCode.setData ) { SetDataRequest setDataRequest = (SetDataRequest)curatorOp.get().toRequestRecord(); schema.validateGeneral(setDataRequest.getPath(), setDataRequest.getData(), null); } } if ( backgrounding.inBackground() ) { client.processBackgroundOperation(new OperationAndData<>(this, record, backgrounding.getCallback(), null, backgrounding.getContext(), null), null); return null; } else { return forOperationsInForeground(record); } } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final TimeTrace trace = client.getZookeeperClient().startTracer("CuratorMultiTransactionImpl-Background"); AsyncCallback.MultiCallback callback = new AsyncCallback.MultiCallback() { @Override public void processResult(int rc, String path, Object ctx, List opResults) { trace.commit(); List curatorResults = (opResults != null) ? CuratorTransactionImpl.wrapResults(client, opResults, operationAndData.getData()) : null; CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.TRANSACTION, rc, path, null, ctx, null, null, null, null, null, curatorResults); client.processBackgroundOperation(operationAndData, event); } }; client.getZooKeeper().multi(operationAndData.getData(), callback, backgrounding.getContext()); } catch ( Throwable e ) { backgrounding.checkError(e, null); } } private List forOperationsInForeground(final CuratorMultiTransactionRecord record) throws Exception { TimeTrace trace = client.getZookeeperClient().startTracer("CuratorMultiTransactionImpl-Foreground"); List responseData = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable>() { @Override public List call() throws Exception { return client.getZooKeeper().multi(record); } } ); trace.commit(); return CuratorTransactionImpl.wrapResults(client, responseData, record); } } CuratorMultiTransactionRecord.java000066400000000000000000000040661442004423600414260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.collect.Lists; import org.apache.curator.framework.api.transaction.OperationType; import org.apache.curator.framework.api.transaction.TypeAndPath; import org.apache.zookeeper.Op; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Iterator; import java.util.List; class CuratorMultiTransactionRecord implements Iterable { private final List metadata = Lists.newArrayList(); private final List ops = new ArrayList<>(); void add(Op op, OperationType type, String forPath) { ops.add(op); metadata.add(new TypeAndPath(type, forPath)); } TypeAndPath getMetadata(int index) { return metadata.get(index); } int metadataSize() { return metadata.size(); } void addToDigest(MessageDigest digest) { for ( Op op : ops ) { digest.update(op.getPath().getBytes()); digest.update(Integer.toString(op.getType()).getBytes()); digest.update(op.toRequestRecord().toString().getBytes()); } } @Override public Iterator iterator() { return ops.iterator(); } int size() { return ops.size(); } } CuratorTempFrameworkImpl.java000066400000000000000000000105321442004423600403670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.CuratorTempFramework; import org.apache.curator.framework.api.TempGetDataBuilder; import org.apache.curator.framework.api.transaction.CuratorTransaction; import org.apache.curator.utils.ThreadUtils; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; public class CuratorTempFrameworkImpl implements CuratorTempFramework { private final CuratorFrameworkFactory.Builder factory; private final long inactiveThresholdMs; // guarded by sync private CuratorFrameworkImpl client; // guarded by sync private ScheduledExecutorService cleanup; // guarded by sync private long lastAccess; public CuratorTempFrameworkImpl(CuratorFrameworkFactory.Builder factory, long inactiveThresholdMs) { this.factory = factory; this.inactiveThresholdMs = inactiveThresholdMs; } @Override public void close() { closeClient(); } @Override public CuratorTransaction inTransaction() throws Exception { openConnectionIfNeeded(); return new CuratorTransactionImpl(client); } @Override public TempGetDataBuilder getData() throws Exception { openConnectionIfNeeded(); return new TempGetDataBuilderImpl(client); } @VisibleForTesting synchronized CuratorFrameworkImpl getClient() { return client; } @VisibleForTesting synchronized ScheduledExecutorService getCleanup() { return cleanup; } @VisibleForTesting synchronized void updateLastAccess() { lastAccess = System.currentTimeMillis(); } private synchronized void openConnectionIfNeeded() throws Exception { if ( client == null ) { client = (CuratorFrameworkImpl)factory.build(); // cast is safe - we control both sides of this client.start(); } if ( cleanup == null ) { ThreadFactory threadFactory = factory.getThreadFactory(); if (threadFactory == null) { threadFactory = ThreadUtils.newGenericThreadFactory("CuratorTempFrameworkImpl"); } cleanup = Executors.newScheduledThreadPool(1, threadFactory); Runnable command = new Runnable() { @Override public void run() { checkInactive(); } }; cleanup.scheduleAtFixedRate(command, inactiveThresholdMs, inactiveThresholdMs, TimeUnit.MILLISECONDS); } updateLastAccess(); } private synchronized void checkInactive() { long elapsed = System.currentTimeMillis() - lastAccess; if ( elapsed >= inactiveThresholdMs ) { closeClient(); } } private synchronized void closeClient() { if ( cleanup != null ) { cleanup.shutdownNow(); cleanup = null; } if ( client != null ) { CloseableUtils.closeQuietly(client); client = null; } } } CuratorTransactionImpl.java000066400000000000000000000164201442004423600400730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import org.apache.curator.RetryLoop; import org.apache.curator.framework.api.Pathable; import org.apache.curator.framework.api.transaction.*; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; @SuppressWarnings("deprecation") class CuratorTransactionImpl implements CuratorTransaction, CuratorTransactionBridge, CuratorTransactionFinal { private final CuratorFrameworkImpl client; private final CuratorMultiTransactionRecord transaction; private boolean isCommitted = false; CuratorTransactionImpl(CuratorFrameworkImpl client) { this.client = client; transaction = new CuratorMultiTransactionRecord(); } @Override public CuratorTransactionFinal and() { return this; } @Override public TransactionCreateBuilder create() { Preconditions.checkState(!isCommitted, "transaction already committed"); CuratorTransactionBridge asBridge = this; return new CreateBuilderImpl(client).asTransactionCreateBuilder(asBridge, transaction); } @Override public TransactionDeleteBuilder delete() { Preconditions.checkState(!isCommitted, "transaction already committed"); CuratorTransactionBridge asBridge = this; return new DeleteBuilderImpl(client).asTransactionDeleteBuilder(asBridge, transaction); } @Override public TransactionSetDataBuilder setData() { Preconditions.checkState(!isCommitted, "transaction already committed"); CuratorTransactionBridge asBridge = this; return new SetDataBuilderImpl(client).asTransactionSetDataBuilder(asBridge, transaction); } @Override public TransactionCheckBuilder check() { Preconditions.checkState(!isCommitted, "transaction already committed"); CuratorTransactionBridge asBridge = this; return makeTransactionCheckBuilder(client, asBridge, transaction); } static TransactionCheckBuilder makeTransactionCheckBuilder(final CuratorFrameworkImpl client, final T context, final CuratorMultiTransactionRecord transaction) { return new TransactionCheckBuilder() { private int version = -1; @Override public T forPath(String path) throws Exception { String fixedPath = client.fixForNamespace(path); transaction.add(Op.check(fixedPath, version), OperationType.CHECK, path); return context; } @Override public Pathable withVersion(int version) { this.version = version; return this; } }; } @Override public Collection commit() throws Exception { Preconditions.checkState(!isCommitted, "transaction already committed"); isCommitted = true; List resultList = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable>() { @Override public List call() throws Exception { return doOperation(); } } ); if ( resultList.size() != transaction.metadataSize() ) { throw new IllegalStateException(String.format("Result size (%d) doesn't match input size (%d)", resultList.size(), transaction.metadataSize())); } return wrapResults(client, resultList, transaction); } static List wrapResults(CuratorFrameworkImpl client, List resultList, CuratorMultiTransactionRecord transaction) { ImmutableList.Builder builder = ImmutableList.builder(); for ( int i = 0; i < resultList.size(); ++i ) { OpResult opResult = resultList.get(i); TypeAndPath metadata = transaction.getMetadata(i); CuratorTransactionResult curatorResult = makeCuratorResult(client, opResult, metadata); builder.add(curatorResult); } return builder.build(); } static CuratorTransactionResult makeCuratorResult(CuratorFrameworkImpl client, OpResult opResult, TypeAndPath metadata) { String resultPath = null; Stat resultStat = null; int error = 0; switch ( opResult.getType() ) { default: { // NOP break; } case ZooDefs.OpCode.create: { OpResult.CreateResult createResult = (OpResult.CreateResult)opResult; resultPath = client.unfixForNamespace(createResult.getPath()); break; } case ZooDefs.OpCode.setData: { OpResult.SetDataResult setDataResult = (OpResult.SetDataResult)opResult; resultStat = setDataResult.getStat(); break; } case ZooDefs.OpCode.error: { OpResult.ErrorResult errorResult = (OpResult.ErrorResult)opResult; error = errorResult.getErr(); break; } } return new CuratorTransactionResult(metadata.getType(), metadata.getForPath(), resultPath, resultStat, error); } private List doOperation() throws Exception { List opResults = client.getZooKeeper().multi(transaction); if ( opResults.size() > 0 ) { OpResult firstResult = opResults.get(0); if ( firstResult.getType() == ZooDefs.OpCode.error ) { OpResult.ErrorResult error = (OpResult.ErrorResult)firstResult; KeeperException.Code code = KeeperException.Code.get(error.getErr()); if ( code == null ) { code = KeeperException.Code.UNIMPLEMENTED; } throw KeeperException.create(code); } } return opResults; } } DefaultACLProvider.java000066400000000000000000000024121442004423600370370ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.framework.api.ACLProvider; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import java.util.List; public class DefaultACLProvider implements ACLProvider { @Override public List getDefaultAcl() { return ZooDefs.Ids.OPEN_ACL_UNSAFE; } @Override public List getAclForPath(String path) { return ZooDefs.Ids.OPEN_ACL_UNSAFE; } } DeleteBuilderImpl.java000066400000000000000000000310761442004423600367630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.*; import org.apache.curator.framework.api.transaction.OperationType; import org.apache.curator.framework.api.transaction.TransactionDeleteBuilder; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class DeleteBuilderImpl implements DeleteBuilder, BackgroundOperation, ErrorListenerPathable { private final CuratorFrameworkImpl client; private int version; private Backgrounding backgrounding; private boolean deletingChildrenIfNeeded; private boolean guaranteed; private boolean quietly; @VisibleForTesting boolean failNextDeleteForTesting = false; @VisibleForTesting boolean failBeforeNextDeleteForTesting = false; DeleteBuilderImpl(CuratorFrameworkImpl client) { this.client = client; version = -1; backgrounding = new Backgrounding(); deletingChildrenIfNeeded = false; guaranteed = false; quietly = false; } public DeleteBuilderImpl(CuratorFrameworkImpl client, int version, Backgrounding backgrounding, boolean deletingChildrenIfNeeded, boolean guaranteed, boolean quietly) { this.client = client; this.version = version; this.backgrounding = backgrounding; this.deletingChildrenIfNeeded = deletingChildrenIfNeeded; this.guaranteed = guaranteed; this.quietly = quietly; } TransactionDeleteBuilder asTransactionDeleteBuilder(final T context, final CuratorMultiTransactionRecord transaction) { return new TransactionDeleteBuilder() { @Override public T forPath(String path) throws Exception { String fixedPath = client.fixForNamespace(path); transaction.add(Op.delete(fixedPath, version), OperationType.DELETE, path); return context; } @Override public Pathable withVersion(int version) { DeleteBuilderImpl.this.withVersion(version); return this; } }; } @Override public DeleteBuilderMain quietly() { quietly = true; return this; } @Override public DeleteBuilderMain idempotent() { // idempotent == quietly for deletes, but keep the interface to be consistent with Create/SetData return quietly(); } @Override public ChildrenDeletable guaranteed() { guaranteed = true; return this; } @Override public BackgroundVersionable deletingChildrenIfNeeded() { deletingChildrenIfNeeded = true; return this; } @Override public BackgroundPathable withVersion(int version) { this.version = version; return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public ErrorListenerPathable inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerPathable inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public Pathable withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("DeleteBuilderImpl-Background"); client.getZooKeeper().delete ( operationAndData.getData(), version, new AsyncCallback.VoidCallback() { @Override public void processResult(int rc, String path, Object ctx) { trace.setReturnCode(rc).setPath(path).commit(); if ( (rc == KeeperException.Code.OK.intValue()) && failNextDeleteForTesting ) { failNextDeleteForTesting = false; rc = KeeperException.Code.CONNECTIONLOSS.intValue(); } if ( (rc == KeeperException.Code.NOTEMPTY.intValue()) && deletingChildrenIfNeeded ) { backgroundDeleteChildrenThenNode(operationAndData); } else { if ( (rc == KeeperException.Code.NONODE.intValue()) && quietly ) { rc = KeeperException.Code.OK.intValue(); } CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.DELETE, rc, path, null, ctx, null, null, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } } }, backgrounding.getContext() ); } catch ( Throwable e ) { backgrounding.checkError(e, null); } } private void backgroundDeleteChildrenThenNode(final OperationAndData mainOperationAndData) { BackgroundOperation operation = new BackgroundOperation() { @Override public void performBackgroundOperation(OperationAndData dummy) throws Exception { try { ZKPaths.deleteChildren(client.getZooKeeper(), mainOperationAndData.getData(), false); } catch ( KeeperException e ) { // ignore } client.queueOperation(mainOperationAndData); } }; OperationAndData parentOperation = new OperationAndData(operation, mainOperationAndData.getData(), null, null, backgrounding.getContext(), null); client.queueOperation(parentOperation); } @Override public Void forPath(String path) throws Exception { client.getSchemaSet().getSchema(path).validateDelete(path); final String unfixedPath = path; path = client.fixForNamespace(path); if ( backgrounding.inBackground() ) { OperationAndData.ErrorCallback errorCallback = null; if ( guaranteed ) { errorCallback = new OperationAndData.ErrorCallback() { @Override public void retriesExhausted(OperationAndData operationAndData) { client.getFailedDeleteManager().addFailedOperation(unfixedPath); } }; } OperationAndData operationAndData = new OperationAndData(this, path, backgrounding.getCallback(), errorCallback, backgrounding.getContext(), null) { @Override void callPerformBackgroundOperation() throws Exception { // inject fault before performing operation if ( failBeforeNextDeleteForTesting ) { failBeforeNextDeleteForTesting = false; throw new KeeperException.ConnectionLossException(); } super.callPerformBackgroundOperation(); } }; client.processBackgroundOperation(operationAndData, null); } else { pathInForeground(path, unfixedPath); } return null; } protected int getVersion() { return version; } private void pathInForeground(final String path, String unfixedPath) throws Exception { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("DeleteBuilderImpl-Foreground"); try { RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public Void call() throws Exception { if ( failBeforeNextDeleteForTesting ) { failBeforeNextDeleteForTesting = false; throw new KeeperException.ConnectionLossException(); } try { client.getZooKeeper().delete(path, version); } catch ( KeeperException.NoNodeException e ) { if ( !quietly ) { throw e; } } catch ( KeeperException.NotEmptyException e ) { if ( deletingChildrenIfNeeded ) { ZKPaths.deleteChildren(client.getZooKeeper(), path, true); } else { throw e; } } if ( failNextDeleteForTesting ) { failNextDeleteForTesting = false; throw new KeeperException.ConnectionLossException(); } return null; } } ); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); //Only retry a guaranteed delete if it's a retryable error if ( (client.getZookeeperClient().getRetryPolicy().allowRetry(e) || (e instanceof InterruptedException)) && guaranteed ) { client.getFailedDeleteManager().addFailedOperation(unfixedPath); } throw e; } trace.setPath(path).commit(); } } EnsembleTracker.java000066400000000000000000000172231442004423600364740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import org.apache.curator.ensemble.EnsembleProvider; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.Compatibility; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @VisibleForTesting public class EnsembleTracker implements Closeable, CuratorWatcher { private final Logger log = LoggerFactory.getLogger(getClass()); private final WatcherRemoveCuratorFramework client; private final EnsembleProvider ensembleProvider; private final AtomicReference state = new AtomicReference<>(State.LATENT); private final AtomicInteger outstanding = new AtomicInteger(0); private final AtomicReference currentConfig = new AtomicReference<>(new QuorumMaj(Maps.newHashMap())); private final ConnectionStateListener connectionStateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( (newState == ConnectionState.CONNECTED) || (newState == ConnectionState.RECONNECTED) ) { try { reset(); } catch ( Exception e ) { log.error("Trying to reset after reconnection", e); } } } @Override public boolean doNotProxy() { return true; } }; private enum State { LATENT, STARTED, CLOSED } EnsembleTracker(CuratorFramework client, EnsembleProvider ensembleProvider) { this.client = client.newWatcherRemoveCuratorFramework(); this.ensembleProvider = ensembleProvider; } public void start() throws Exception { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once"); client.getConnectionStateListenable().addListener(connectionStateListener); reset(); } @Override public void close() { if ( state.compareAndSet(State.STARTED, State.CLOSED) ) { client.removeWatchers(); client.getConnectionStateListenable().removeListener(connectionStateListener); } } @Override public void process(WatchedEvent event) throws Exception { if ( event.getType() == Watcher.Event.EventType.NodeDataChanged ) { reset(); } } /** * Return the current quorum config * * @return config */ public QuorumVerifier getCurrentConfig() { return currentConfig.get(); } @VisibleForTesting public boolean hasOutstanding() { return outstanding.get() > 0; } private void reset() throws Exception { if ( (client.getState() == CuratorFrameworkState.STARTED) && (state.get() == State.STARTED) ) { BackgroundCallback backgroundCallback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { outstanding.decrementAndGet(); if ( (event.getType() == CuratorEventType.GET_CONFIG) && (event.getResultCode() == KeeperException.Code.OK.intValue()) ) { processConfigData(event.getData()); } } }; outstanding.incrementAndGet(); try { client.getConfig().usingWatcher(this).inBackground(backgroundCallback).forEnsemble(); outstanding.incrementAndGet(); // finally block will decrement } finally { outstanding.decrementAndGet(); } } } @VisibleForTesting public static String configToConnectionString(QuorumVerifier data) throws Exception { StringBuilder sb = new StringBuilder(); for ( QuorumPeer.QuorumServer server : data.getAllMembers().values() ) { if ( server.clientAddr == null ) { // Invalid client address configuration in zoo.cfg continue; } if ( sb.length() != 0 ) { sb.append(","); } sb.append(getHostString(server)).append(":").append(server.clientAddr.getPort()); } return sb.toString(); } private static String getHostString(QuorumPeer.QuorumServer server) { InetSocketAddress clientAddr = server.clientAddr; InetAddress clientIpAddr = clientAddr.getAddress(); if ( clientIpAddr != null && clientIpAddr.isAnyLocalAddress() ) { return Compatibility.getHostString(server); } else { return clientAddr.getHostString(); } } private void processConfigData(byte[] data) throws Exception { Properties properties = new Properties(); properties.load(new ByteArrayInputStream(data)); log.info("New config event received: {}", properties); if (!properties.isEmpty()) { QuorumMaj newConfig = new QuorumMaj(properties); String connectionString = configToConnectionString(newConfig); if (connectionString.trim().length() > 0) { currentConfig.set(newConfig); ensembleProvider.setConnectionString(connectionString); } else { log.debug("Invalid config event received: {}", properties); } } else { log.debug("Ignoring new config as it is empty"); } } } ExistsBuilderImpl.java000066400000000000000000000235621442004423600370410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.*; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class ExistsBuilderImpl implements ExistsBuilder, BackgroundOperation, ErrorListenerPathable, ACLableExistBuilderMain { private final CuratorFrameworkImpl client; private Backgrounding backgrounding; private Watching watching; private boolean createParentsIfNeeded; private boolean createParentContainersIfNeeded; private ACLing acling; ExistsBuilderImpl(CuratorFrameworkImpl client) { this(client, new Backgrounding(), null, false, false); } public ExistsBuilderImpl(CuratorFrameworkImpl client, Backgrounding backgrounding, Watcher watcher, boolean createParentsIfNeeded, boolean createParentContainersIfNeeded) { this.client = client; this.backgrounding = backgrounding; this.watching = new Watching(client, watcher); this.createParentsIfNeeded = createParentsIfNeeded; this.createParentContainersIfNeeded = createParentContainersIfNeeded; this.acling = new ACLing(client.getAclProvider()); } @Override public ACLableExistBuilderMain creatingParentsIfNeeded() { createParentContainersIfNeeded = false; createParentsIfNeeded = true; return this; } @Override public ACLableExistBuilderMain creatingParentContainersIfNeeded() { createParentContainersIfNeeded = true; createParentsIfNeeded = false; return this; } @Override public ExistsBuilderMain withACL(List aclList) { acling = new ACLing(client.getAclProvider(), aclList, true); return this; } @Override public BackgroundPathable watched() { watching = new Watching(client, true); return this; } @Override public BackgroundPathable usingWatcher(Watcher watcher) { watching = new Watching(client, watcher); return this; } @Override public BackgroundPathable usingWatcher(CuratorWatcher watcher) { watching = new Watching(client, watcher); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public ErrorListenerPathable inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerPathable inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public Pathable withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("ExistsBuilderImpl-Background"); AsyncCallback.StatCallback callback = new AsyncCallback.StatCallback() { @Override public void processResult(int rc, String path, Object ctx, Stat stat) { watching.commitWatcher(rc, true); trace.setReturnCode(rc).setPath(path).setWithWatcher(watching.hasWatcher()).setStat(stat).commit(); CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.EXISTS, rc, path, null, ctx, stat, null, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } }; if ( watching.isWatched() ) { client.getZooKeeper().exists(operationAndData.getData(), true, callback, backgrounding.getContext()); } else { client.getZooKeeper().exists(operationAndData.getData(), watching.getWatcher(operationAndData.getData()), callback, backgrounding.getContext()); } } catch ( Throwable e ) { backgrounding.checkError(e, watching); } } @Override public Stat forPath(String path) throws Exception { path = client.fixForNamespace(path); client.getSchemaSet().getSchema(path).validateWatch(path, watching.isWatched() || watching.hasWatcher()); Stat returnStat = null; if ( backgrounding.inBackground() ) { OperationAndData operationAndData = new OperationAndData(this, path, backgrounding.getCallback(), null, backgrounding.getContext(), watching); if ( createParentContainersIfNeeded || createParentsIfNeeded ) { CreateBuilderImpl.backgroundCreateParentsThenNode(client, operationAndData, operationAndData.getData(), backgrounding, acling.getACLProviderForParents(), createParentContainersIfNeeded); } else { client.processBackgroundOperation(operationAndData, null); } } else { returnStat = pathInForeground(path); } return returnStat; } private Stat pathInForeground(final String path) throws Exception { if ( createParentContainersIfNeeded || createParentsIfNeeded ) { final String parent = ZKPaths.getPathAndNode(path).getPath(); if ( !parent.equals(ZKPaths.PATH_SEPARATOR) ) { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("ExistsBuilderImpl-Foreground-CreateParents"); RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public Void call() throws Exception { try { ZKPaths.mkdirs(client.getZooKeeper(), parent, true, acling.getACLProviderForParents(), createParentContainersIfNeeded); } catch ( KeeperException.NodeExistsException e ) { // ignore } catch ( KeeperException.NoNodeException e ) { // ignore } return null; } } ); trace.setPath(path).commit(); } } return pathInForegroundStandard(path); } private Stat pathInForegroundStandard(final String path) throws Exception { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("ExistsBuilderImpl-Foreground"); Stat returnStat = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public Stat call() throws Exception { Stat returnStat; if ( watching.isWatched() ) { returnStat = client.getZooKeeper().exists(path, true); } else { returnStat = client.getZooKeeper().exists(path, watching.getWatcher(path)); int rc = (returnStat != null) ? KeeperException.NoNodeException.Code.OK.intValue() : KeeperException.NoNodeException.Code.NONODE.intValue(); watching.commitWatcher(rc, true); } return returnStat; } } ); trace.setPath(path).setWithWatcher(watching.hasWatcher()).setStat(returnStat).commit(); return returnStat; } } ExtractingCuratorOp.java000066400000000000000000000035231442004423600373730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.base.Preconditions; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.TypeAndPath; import org.apache.zookeeper.Op; import java.security.MessageDigest; public class ExtractingCuratorOp implements CuratorOp { private final CuratorMultiTransactionRecord record = new CuratorMultiTransactionRecord(); CuratorMultiTransactionRecord getRecord() { return record; } @Override public TypeAndPath getTypeAndPath() { validate(); return record.getMetadata(0); } @Override public Op get() { validate(); return record.iterator().next(); } public void addToDigest(MessageDigest digest) { record.addToDigest(digest); } private void validate() { Preconditions.checkArgument(record.size() > 0, "No operation has been added"); Preconditions.checkArgument(record.size() == 1, "Multiple operations added"); } } FailedDeleteManager.java000066400000000000000000000023471442004423600372310ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.framework.CuratorFramework; class FailedDeleteManager extends FailedOperationManager { FailedDeleteManager(CuratorFramework client) { super(client); } @Override protected void executeGuaranteedOperationInBackground(String path) throws Exception { client.delete().guaranteed().inBackground().forPath(path); } } FailedOperationManager.java000066400000000000000000000043451442004423600377670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; abstract class FailedOperationManager { private final Logger log = LoggerFactory.getLogger(getClass()); protected final CuratorFramework client; @VisibleForTesting volatile FailedOperationManagerListener debugListener = null; interface FailedOperationManagerListener { public void pathAddedForGuaranteedOperation(T detail); } FailedOperationManager(CuratorFramework client) { this.client = client; } void addFailedOperation(T details) { if ( debugListener != null ) { debugListener.pathAddedForGuaranteedOperation(details); } if ( client.getState() == CuratorFrameworkState.STARTED ) { log.debug("Details being added to guaranteed operation set: " + details); try { executeGuaranteedOperationInBackground(details); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); addFailedOperation(details); } } } protected abstract void executeGuaranteedOperationInBackground(T details) throws Exception; } FailedRemoveWatchManager.java000066400000000000000000000035301442004423600402460ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.Watcher; class FailedRemoveWatchManager extends FailedOperationManager { FailedRemoveWatchManager(CuratorFramework client) { super(client); } @Override protected void executeGuaranteedOperationInBackground(FailedRemoveWatchDetails details) throws Exception { if(details.watcher == null) { client.watches().removeAll().guaranteed().inBackground().forPath(details.path); } else { client.watches().remove(details.watcher).guaranteed().inBackground().forPath(details.path); } } static class FailedRemoveWatchDetails { public final String path; public final Watcher watcher; public FailedRemoveWatchDetails(String path, Watcher watcher) { this.path = path; this.watcher = watcher; } } } FindAndDeleteProtectedNodeInBackground.java000066400000000000000000000113471442004423600430240ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; class FindAndDeleteProtectedNodeInBackground implements BackgroundOperation { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFrameworkImpl client; private final String namespaceAdjustedParentPath; private final String protectedId; FindAndDeleteProtectedNodeInBackground(CuratorFrameworkImpl client, String namespaceAdjustedParentPath, String protectedId) { this.client = client; this.namespaceAdjustedParentPath = namespaceAdjustedParentPath; this.protectedId = protectedId; } void execute() { OperationAndData.ErrorCallback errorCallback = new OperationAndData.ErrorCallback() { @Override public void retriesExhausted(OperationAndData operationAndData) { operationAndData.reset(); client.processBackgroundOperation(operationAndData, null); } }; OperationAndData operationAndData = new OperationAndData(this, null, null, errorCallback, null, null); client.processBackgroundOperation(operationAndData, null); } @VisibleForTesting static final AtomicBoolean debugInsertError = new AtomicBoolean(false); @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("FindAndDeleteProtectedNodeInBackground"); AsyncCallback.Children2Callback callback = new AsyncCallback.Children2Callback() { @Override public void processResult(int rc, String path, Object o, List strings, Stat stat) { trace.setReturnCode(rc).setPath(path).setStat(stat).commit(); if ( debugInsertError.compareAndSet(true, false) ) { rc = KeeperException.Code.CONNECTIONLOSS.intValue(); } if ( rc == KeeperException.Code.OK.intValue() ) { final String node = CreateBuilderImpl.findNode(strings, "/", protectedId); // due to namespacing, don't let CreateBuilderImpl.findNode adjust the path if ( node != null ) { try { String deletePath = client.unfixForNamespace(ZKPaths.makePath(namespaceAdjustedParentPath, node)); client.delete().guaranteed().inBackground().forPath(deletePath); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Could not start guaranteed delete for node: " + node); rc = KeeperException.Code.CONNECTIONLOSS.intValue(); } } } if ( rc != KeeperException.Code.OK.intValue() ) { CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.CHILDREN, rc, path, null, o, stat, null, strings, null, null, null); client.processBackgroundOperation(operationAndData, event); } } }; client.getZooKeeper().getChildren(namespaceAdjustedParentPath, false, callback, null); } } GetACLBuilderImpl.java000066400000000000000000000136031442004423600366140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.ErrorListenerPathable; import org.apache.curator.framework.api.GetACLBuilder; import org.apache.curator.framework.api.Pathable; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class GetACLBuilderImpl implements GetACLBuilder, BackgroundOperation, ErrorListenerPathable> { private final CuratorFrameworkImpl client; private Backgrounding backgrounding; private Stat responseStat; GetACLBuilderImpl(CuratorFrameworkImpl client) { this.client = client; backgrounding = new Backgrounding(); responseStat = new Stat(); } public GetACLBuilderImpl(CuratorFrameworkImpl client, Backgrounding backgrounding, Stat responseStat) { this.client = client; this.backgrounding = backgrounding; this.responseStat = responseStat; } @Override public ErrorListenerPathable> inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathable> inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ErrorListenerPathable> inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerPathable> inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public ErrorListenerPathable> inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathable> inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public Pathable> withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public Pathable> storingStatIn(Stat stat) { responseStat = stat; return this; } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("GetACLBuilderImpl-Background"); AsyncCallback.ACLCallback callback = new AsyncCallback.ACLCallback() { @Override public void processResult(int rc, String path, Object ctx, List acl, Stat stat) { trace.setReturnCode(rc).setPath(path).setStat(stat).commit(); CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.GET_ACL, rc, path, null, ctx, stat, null, null, null, acl, null); client.processBackgroundOperation(operationAndData, event); } }; client.getZooKeeper().getACL(operationAndData.getData(), responseStat, callback, backgrounding.getContext()); } catch ( Throwable e ) { backgrounding.checkError(e, null); } } @Override public List forPath(String path) throws Exception { path = client.fixForNamespace(path); List result = null; if ( backgrounding.inBackground() ) { client.processBackgroundOperation(new OperationAndData(this, path, backgrounding.getCallback(), null, backgrounding.getContext(), null), null); } else { result = pathInForeground(path); } return result; } private List pathInForeground(final String path) throws Exception { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("GetACLBuilderImpl-Foreground"); List result = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable>() { @Override public List call() throws Exception { return client.getZooKeeper().getACL(path, responseStat); } } ); trace.setPath(path).setStat(responseStat).commit(); return result; } } GetChildrenBuilderImpl.java000066400000000000000000000216401442004423600377450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.collect.Lists; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.BackgroundPathable; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.api.ErrorListenerPathable; import org.apache.curator.framework.api.GetChildrenBuilder; import org.apache.curator.framework.api.Pathable; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.api.WatchPathable; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class GetChildrenBuilderImpl implements GetChildrenBuilder, BackgroundOperation, ErrorListenerPathable> { private final CuratorFrameworkImpl client; private Watching watching; private Backgrounding backgrounding; private Stat responseStat; GetChildrenBuilderImpl(CuratorFrameworkImpl client) { this.client = client; watching = new Watching(client); backgrounding = new Backgrounding(); responseStat = null; } public GetChildrenBuilderImpl(CuratorFrameworkImpl client, Watcher watcher, Backgrounding backgrounding, Stat responseStat) { this.client = client; this.watching = new Watching(client, watcher); this.backgrounding = backgrounding; this.responseStat = responseStat; } @Override public WatchPathable> storingStatIn(Stat stat) { responseStat = stat; return new WatchPathable>() { @Override public List forPath(String path) throws Exception { return GetChildrenBuilderImpl.this.forPath(path); } @Override public Pathable> watched() { GetChildrenBuilderImpl.this.watched(); return GetChildrenBuilderImpl.this; } @Override public Pathable> usingWatcher(Watcher watcher) { GetChildrenBuilderImpl.this.usingWatcher(watcher); return GetChildrenBuilderImpl.this; } @Override public Pathable> usingWatcher(CuratorWatcher watcher) { GetChildrenBuilderImpl.this.usingWatcher(watcher); return GetChildrenBuilderImpl.this; } }; } @Override public ErrorListenerPathable> inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathable> inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ErrorListenerPathable> inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathable> inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public ErrorListenerPathable> inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerPathable> inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public Pathable> withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public BackgroundPathable> watched() { watching = new Watching(client, true); return this; } @Override public BackgroundPathable> usingWatcher(Watcher watcher) { watching = new Watching(client, watcher); return this; } @Override public BackgroundPathable> usingWatcher(CuratorWatcher watcher) { watching = new Watching(client, watcher); return this; } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("GetChildrenBuilderImpl-Background"); AsyncCallback.Children2Callback callback = new AsyncCallback.Children2Callback() { @Override public void processResult(int rc, String path, Object o, List strings, Stat stat) { watching.commitWatcher(rc, false); trace.setReturnCode(rc).setPath(path).setWithWatcher(watching.hasWatcher()).setStat(stat).commit(); if ( strings == null ) { strings = Lists.newArrayList(); } CuratorEventImpl event = new CuratorEventImpl(client, CuratorEventType.CHILDREN, rc, path, null, o, stat, null, strings, null, null, null); client.processBackgroundOperation(operationAndData, event); } }; if ( watching.isWatched() ) { client.getZooKeeper().getChildren(operationAndData.getData(), true, callback, backgrounding.getContext()); } else { client.getZooKeeper().getChildren(operationAndData.getData(), watching.getWatcher(operationAndData.getData()), callback, backgrounding.getContext()); } } catch ( Throwable e ) { backgrounding.checkError(e, watching); } } @Override public List forPath(String path) throws Exception { client.getSchemaSet().getSchema(path).validateWatch(path, watching.isWatched() || watching.hasWatcher()); path = client.fixForNamespace(path); List children = null; if ( backgrounding.inBackground() ) { client.processBackgroundOperation(new OperationAndData(this, path, backgrounding.getCallback(), null, backgrounding.getContext(), watching), null); } else { children = pathInForeground(path); } return children; } private List pathInForeground(final String path) throws Exception { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("GetChildrenBuilderImpl-Foreground"); List children = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable>() { @Override public List call() throws Exception { List children; if ( watching.isWatched() ) { children = client.getZooKeeper().getChildren(path, true, responseStat); } else { children = client.getZooKeeper().getChildren(path, watching.getWatcher(path), responseStat); watching.commitWatcher(KeeperException.NoNodeException.Code.OK.intValue(), false); } return children; } } ); trace.setPath(path).setWithWatcher(watching.hasWatcher()).setStat(responseStat).commit(); return children; } } GetConfigBuilderImpl.java000066400000000000000000000245401442004423600374240ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.RetryLoop; import org.apache.curator.TimeTrace; import org.apache.curator.framework.api.*; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class GetConfigBuilderImpl implements GetConfigBuilder, BackgroundOperation, ErrorListenerEnsembleable { private final CuratorFrameworkImpl client; private Backgrounding backgrounding; private Watching watching; private Stat stat; public GetConfigBuilderImpl(CuratorFrameworkImpl client) { this.client = client; backgrounding = new Backgrounding(); watching = new Watching(client); } public GetConfigBuilderImpl(CuratorFrameworkImpl client, Backgrounding backgrounding, Watcher watcher, Stat stat) { this.client = client; this.backgrounding = backgrounding; this.watching = new Watching(client, watcher); this.stat = stat; } @Override public WatchBackgroundEnsembleable storingStatIn(Stat stat) { this.stat = stat; return new WatchBackgroundEnsembleable() { @Override public ErrorListenerEnsembleable inBackground() { return GetConfigBuilderImpl.this.inBackground(); } @Override public ErrorListenerEnsembleable inBackground(Object context) { return GetConfigBuilderImpl.this.inBackground(context); } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback) { return GetConfigBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback, Object context) { return GetConfigBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback, Executor executor) { return GetConfigBuilderImpl.this.inBackground(callback, executor); } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback, Object context, Executor executor) { return GetConfigBuilderImpl.this.inBackground(callback, context, executor); } @Override public byte[] forEnsemble() throws Exception { return GetConfigBuilderImpl.this.forEnsemble(); } @Override public BackgroundEnsembleable watched() { return GetConfigBuilderImpl.this.watched(); } @Override public BackgroundEnsembleable usingWatcher(Watcher watcher) { return GetConfigBuilderImpl.this.usingWatcher(watcher); } @Override public BackgroundEnsembleable usingWatcher(CuratorWatcher watcher) { return GetConfigBuilderImpl.this.usingWatcher(watcher); } }; } @Override public BackgroundEnsembleable watched() { watching = new Watching(client, true); return new InternalBackgroundEnsembleable(); } @Override public BackgroundEnsembleable usingWatcher(Watcher watcher) { watching = new Watching(client, watcher); return new InternalBackgroundEnsembleable(); } @Override public BackgroundEnsembleable usingWatcher(CuratorWatcher watcher) { watching = new Watching(client, watcher); return new InternalBackgroundEnsembleable(); } @Override public ErrorListenerEnsembleable inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerEnsembleable inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(callback, executor); return this; } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public Ensembleable withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public byte[] forEnsemble() throws Exception { if ( backgrounding.inBackground() ) { client.processBackgroundOperation(new OperationAndData(this, null, backgrounding.getCallback(), null, backgrounding.getContext(), watching), null); return null; } else { return configInForeground(); } } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final TimeTrace trace = client.getZookeeperClient().startTracer("GetDataBuilderImpl-Background"); AsyncCallback.DataCallback callback = new AsyncCallback.DataCallback() { @Override public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { watching.commitWatcher(rc, false); trace.commit(); CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.GET_CONFIG, rc, path, null, ctx, stat, data, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } }; if ( watching.isWatched() ) { client.getZooKeeper().getConfig(true, callback, backgrounding.getContext()); } else { client.getZooKeeper().getConfig(watching.getWatcher(ZooDefs.CONFIG_NODE), callback, backgrounding.getContext()); } } catch ( Throwable e ) { backgrounding.checkError(e, watching); } } private byte[] configInForeground() throws Exception { TimeTrace trace = client.getZookeeperClient().startTracer("GetConfigBuilderImpl-Foreground"); try { return RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public byte[] call() throws Exception { if ( watching.isWatched() ) { return client.getZooKeeper().getConfig(true, stat); } byte[] config = client.getZooKeeper().getConfig(watching.getWatcher(ZooDefs.CONFIG_NODE), stat); watching.commitWatcher(KeeperException.NoNodeException.Code.OK.intValue(), false); return config; } } ); } finally { trace.commit(); } } private class InternalBackgroundEnsembleable implements BackgroundEnsembleable { @Override public ErrorListenerEnsembleable inBackground() { return GetConfigBuilderImpl.this.inBackground(); } @Override public ErrorListenerEnsembleable inBackground(Object context) { return GetConfigBuilderImpl.this.inBackground(context); } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback) { return GetConfigBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback, Object context) { return GetConfigBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback, Executor executor) { return GetConfigBuilderImpl.this.inBackground(callback, executor); } @Override public ErrorListenerEnsembleable inBackground(BackgroundCallback callback, Object context, Executor executor) { return GetConfigBuilderImpl.this.inBackground(callback, context, executor); } @Override public byte[] forEnsemble() throws Exception { return GetConfigBuilderImpl.this.forEnsemble(); } } } GetDataBuilderImpl.java000066400000000000000000000273151442004423600370730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.*; import org.apache.curator.utils.ThreadUtils; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.DataTree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class GetDataBuilderImpl implements GetDataBuilder, BackgroundOperation, ErrorListenerPathable { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFrameworkImpl client; private Stat responseStat; private Watching watching; private Backgrounding backgrounding; private boolean decompress; GetDataBuilderImpl(CuratorFrameworkImpl client) { this.client = client; responseStat = null; watching = new Watching(client); backgrounding = new Backgrounding(); decompress = false; } public GetDataBuilderImpl(CuratorFrameworkImpl client, Stat responseStat, Watcher watcher, Backgrounding backgrounding, boolean decompress) { this.client = client; this.responseStat = responseStat; this.watching = new Watching(client, watcher); this.backgrounding = backgrounding; this.decompress = decompress; } @Override public GetDataWatchBackgroundStatable decompressed() { decompress = true; return new GetDataWatchBackgroundStatable() { @Override public ErrorListenerPathable inBackground() { return GetDataBuilderImpl.this.inBackground(); } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context) { return GetDataBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context, Executor executor) { return GetDataBuilderImpl.this.inBackground(callback, context, executor); } @Override public ErrorListenerPathable inBackground(Object context) { return GetDataBuilderImpl.this.inBackground(context); } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback) { return GetDataBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Executor executor) { return GetDataBuilderImpl.this.inBackground(callback, executor); } @Override public byte[] forPath(String path) throws Exception { return GetDataBuilderImpl.this.forPath(path); } @Override public WatchPathable storingStatIn(Stat stat) { return GetDataBuilderImpl.this.storingStatIn(stat); } @Override public BackgroundPathable watched() { return GetDataBuilderImpl.this.watched(); } @Override public BackgroundPathable usingWatcher(Watcher watcher) { return GetDataBuilderImpl.this.usingWatcher(watcher); } @Override public BackgroundPathable usingWatcher(CuratorWatcher watcher) { return GetDataBuilderImpl.this.usingWatcher(watcher); } }; } @Override public WatchPathable storingStatIn(Stat stat) { this.responseStat = stat; return new WatchPathable() { @Override public byte[] forPath(String path) throws Exception { return GetDataBuilderImpl.this.forPath(path); } @Override public Pathable watched() { GetDataBuilderImpl.this.watched(); return GetDataBuilderImpl.this; } @Override public Pathable usingWatcher(Watcher watcher) { GetDataBuilderImpl.this.usingWatcher(watcher); return GetDataBuilderImpl.this; } @Override public Pathable usingWatcher(CuratorWatcher watcher) { GetDataBuilderImpl.this.usingWatcher(watcher); return GetDataBuilderImpl.this; } }; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public ErrorListenerPathable inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerPathable inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public Pathable withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public BackgroundPathable watched() { watching = new Watching(client, true); return this; } @Override public BackgroundPathable usingWatcher(Watcher watcher) { watching = new Watching(client, watcher); return this; } @Override public BackgroundPathable usingWatcher(CuratorWatcher watcher) { watching = new Watching(client, watcher); return this; } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("GetDataBuilderImpl-Background"); AsyncCallback.DataCallback callback = new AsyncCallback.DataCallback() { @Override public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { watching.commitWatcher(rc, false); trace.setReturnCode(rc).setResponseBytesLength(data).setPath(path).setWithWatcher(watching.hasWatcher()).setStat(stat).commit(); if ( (responseStat != null) && (stat != null) ) { DataTree.copyStat(stat, responseStat); } if ( decompress && (data != null) ) { try { data = client.getCompressionProvider().decompress(path, data); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Decompressing for path: " + path, e); rc = KeeperException.Code.DATAINCONSISTENCY.intValue(); } } CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.GET_DATA, rc, path, null, ctx, stat, data, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } }; if ( watching.isWatched() ) { client.getZooKeeper().getData(operationAndData.getData(), true, callback, backgrounding.getContext()); } else { client.getZooKeeper().getData(operationAndData.getData(), watching.getWatcher(operationAndData.getData()), callback, backgrounding.getContext()); } } catch ( Throwable e ) { backgrounding.checkError(e, watching); } } @Override public byte[] forPath(String path) throws Exception { client.getSchemaSet().getSchema(path).validateWatch(path, watching.isWatched() || watching.hasWatcher()); path = client.fixForNamespace(path); byte[] responseData = null; if ( backgrounding.inBackground() ) { client.processBackgroundOperation(new OperationAndData(this, path, backgrounding.getCallback(), null, backgrounding.getContext(), watching), null); } else { responseData = pathInForeground(path); } return responseData; } private byte[] pathInForeground(final String path) throws Exception { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("GetDataBuilderImpl-Foreground"); byte[] responseData = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public byte[] call() throws Exception { byte[] responseData; if ( watching.isWatched() ) { responseData = client.getZooKeeper().getData(path, true, responseStat); } else { responseData = client.getZooKeeper().getData(path, watching.getWatcher(path), responseStat); watching.commitWatcher(KeeperException.NoNodeException.Code.OK.intValue(), false); } return responseData; } } ); trace.setResponseBytesLength(responseData).setPath(path).setWithWatcher(watching.hasWatcher()).setStat(responseStat).commit(); return decompress ? client.getCompressionProvider().decompress(path, responseData) : responseData; } } GzipCompressionProvider.java000066400000000000000000000326751442004423600403040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import org.apache.curator.framework.api.CompressionProvider; import java.io.EOFException; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.zip.*; public class GzipCompressionProvider implements CompressionProvider { // This class re-implements java.util.zip.GZIPInputStream and GZIPOutputStream functionality to avoid // creation many finalized Deflater and Inflater objects on heap (see // https://issues.apache.org/jira/browse/CURATOR-487). Even when Curator's minimum supported Java version becomes // no less than Java 12, where finalize() methods are removed in Deflater and Inflater classes and instead they // are phantom-referenced via Cleaner, it still makes sense to avoid GZIPInputStream and GZIPOutputStream because // phantom references are also not entirely free for GC algorithms, and also to allocate less garbage and make // less unnecessary data copies. private static final int MAX_SAFE_JAVA_BYTE_ARRAY_SIZE = Integer.MAX_VALUE - 128; /** GZIP header magic number. */ private static final int GZIP_MAGIC = 0x8b1f; /** See {@code java.util.zip.GZIPOutputStream.writeHeader()} */ private static final byte[] GZIP_HEADER = new byte[] { (byte) GZIP_MAGIC, // Magic number (byte 0) (byte) (GZIP_MAGIC >> 8), // Magic number (byte 1) Deflater.DEFLATED, // Compression method (CM) 0, // Flags (FLG) 0, // Modification time MTIME (byte 0) 0, // Modification time MTIME (byte 1) 0, // Modification time MTIME (byte 2) 0, // Modification time MTIME (byte 3) 0, // Extra flags (XFLG) 0 // Operating system (OS) }; /** GZip flags, {@link #GZIP_HEADER}'s 4th byte */ private static final int FHCRC = 1 << 1; private static final int FEXTRA = 1 << 2; private static final int FNAME = 1 << 3; private static final int FCOMMENT = 1 << 4; private static final int GZIP_HEADER_SIZE = GZIP_HEADER.length; /** 32-bit CRC and uncompressed data size */ private static final int GZIP_TRAILER_SIZE = Integer.BYTES + Integer.BYTES; /** DEFLATE doesn't produce shorter compressed data */ private static final int MIN_COMPRESSED_DATA_SIZE = 2; /** * Since Deflaters and Inflaters are acquired and returned to the pools in try-finally blocks that are free of * blocking calls themselves, it's not expected that the number of objects in the pools could exceed the number of * hardware threads on the machine much. Therefore it's accepted to have simple "ever-growing" (in fact, no) pools * of strongly-referenced objects. */ private static final ConcurrentLinkedQueue DEFLATER_POOL = new ConcurrentLinkedQueue<>(); private static final ConcurrentLinkedQueue INFLATER_POOL = new ConcurrentLinkedQueue<>(); /** The value verified in GzipCompressionProviderTest.testEmpty() */ private static final byte[] COMPRESSED_EMPTY_BYTES = new byte[] { 31, -117, 8, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private static Deflater acquireDeflater() { Deflater deflater = DEFLATER_POOL.poll(); if ( deflater == null ) { // Using the same settings as in GZIPOutputStream constructor deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true); } return deflater; } private static Inflater acquireInflater() { Inflater inflater = INFLATER_POOL.poll(); if ( inflater == null ) { // Using the same nowrap setting as GZIPInputStream constructor inflater = new Inflater(true); } return inflater; } @Override public byte[] compress(String path, byte[] data) { if ( data.length == 0 ) { // clone() because clients could update the array return COMPRESSED_EMPTY_BYTES.clone(); } return doCompress(data); } @VisibleForTesting static byte[] doCompress(byte[] data) { byte[] result = Arrays.copyOf(GZIP_HEADER, conservativeGZippedSizeEstimate(data.length)); Deflater deflater = acquireDeflater(); try { deflater.setInput(data); deflater.finish(); int offset = GZIP_HEADER_SIZE; while ( true ) { int available = result.length - GZIP_TRAILER_SIZE - offset; int numCompressedBytes = deflater.deflate(result, offset, available); offset += numCompressedBytes; if ( deflater.finished() ) { break; } int newResultLength = result.length + (result.length / 2); result = Arrays.copyOf(result, newResultLength); } // Write GZip trailer CRC32 crc = new CRC32(); crc.update(data, 0, data.length); writeLittleEndianInt(result, offset, (int) crc.getValue()); writeLittleEndianInt(result, offset + 4, data.length); int endOffset = offset + GZIP_TRAILER_SIZE; if ( result.length != endOffset ) { result = Arrays.copyOf(result, endOffset); } return result; } finally { deflater.reset(); DEFLATER_POOL.add(deflater); } } private static int conservativeGZippedSizeEstimate(int dataSize) { int conservativeCompressedDataSizeEstimate; if ( dataSize < 512 ) { // Assuming DEFLATE doesn't compress small data well conservativeCompressedDataSizeEstimate = Math.max(dataSize, MIN_COMPRESSED_DATA_SIZE); } else { // Assuming pretty bad 2:1 compression ratio conservativeCompressedDataSizeEstimate = Math.max(512, dataSize / 2); } return GZIP_HEADER_SIZE + conservativeCompressedDataSizeEstimate + GZIP_TRAILER_SIZE; } private static void writeLittleEndianInt(byte[] b, int offset, int v) { b[offset] = (byte) v; b[offset + 1] = (byte) (v >> 8); b[offset + 2] = (byte) (v >> 16); b[offset + 3] = (byte) (v >> 24); } @Override public byte[] decompress(String path, byte[] gzippedDataBytes) throws IOException { if ( Arrays.equals(gzippedDataBytes, COMPRESSED_EMPTY_BYTES) ) { // Allocating a new array instead of creating a static constant because clients may somehow depend on the // identity of the returned arrays return new byte[0]; } ByteBuffer gzippedData = ByteBuffer.wrap(gzippedDataBytes); gzippedData.order(ByteOrder.LITTLE_ENDIAN); int headerSize = readGzipHeader(gzippedData); if ( gzippedDataBytes.length < headerSize + MIN_COMPRESSED_DATA_SIZE + GZIP_TRAILER_SIZE ) { throw new EOFException("Too short GZipped data"); } int compressedDataSize = gzippedDataBytes.length - headerSize - GZIP_TRAILER_SIZE; // Assuming 3:1 compression ratio. Intentionally a more generous estimation than in // conservativeGZippedSizeEstimate() to reduce the probability of result array reallocation. int initialResultLength = (int) Math.min(compressedDataSize * 3L, MAX_SAFE_JAVA_BYTE_ARRAY_SIZE); byte[] result = new byte[initialResultLength]; Inflater inflater = acquireInflater(); try { inflater.setInput(gzippedDataBytes, headerSize, compressedDataSize); CRC32 crc = new CRC32(); int offset = 0; while (true) { int numDecompressedBytes; try { numDecompressedBytes = inflater.inflate(result, offset, result.length - offset); } catch (DataFormatException e) { String s = e.getMessage(); throw new ZipException(s != null ? s : "Invalid ZLIB data format"); } crc.update(result, offset, numDecompressedBytes); offset += numDecompressedBytes; if ( inflater.finished() || inflater.needsDictionary() ) { break; } // Just calling inflater.needsInput() doesn't work as expected, apparently it doesn't uphold it's own // contract and could have needsInput() == true if numDecompressedBytes != 0 and that just means that // there is not enough space in the result array else if ( numDecompressedBytes == 0 && inflater.needsInput() ) { throw new ZipException("Corrupt GZipped data"); } // Inflater's contract doesn't say whether it's able to be finished() without returning 0 from inflate() // call, so the additional `numDecompressedBytes == 0` condition ensures that we did another cycle and // definitely need to inflate some more bytes. if ( result.length == MAX_SAFE_JAVA_BYTE_ARRAY_SIZE && numDecompressedBytes == 0 ) { throw new OutOfMemoryError("Unable to uncompress that much data into a single byte[] array"); } int newResultLength = (int) Math.min((long) result.length + (result.length / 2), MAX_SAFE_JAVA_BYTE_ARRAY_SIZE); if ( result.length != newResultLength ) { result = Arrays.copyOf(result, newResultLength); } } if ( inflater.getRemaining() != 0 ) { throw new ZipException("Expected just one GZip block, without garbage in the end"); } int checksum = gzippedData.getInt(gzippedDataBytes.length - GZIP_TRAILER_SIZE); int numUncompressedBytes = gzippedData.getInt(gzippedDataBytes.length - Integer.BYTES); if ( checksum != (int) crc.getValue() || numUncompressedBytes != offset ) { throw new ZipException("Corrupt GZIP trailer"); } if ( result.length != offset ) { result = Arrays.copyOf(result, offset); } return result; } finally { inflater.reset(); INFLATER_POOL.add(inflater); } } /** * Returns the header size */ private static int readGzipHeader(ByteBuffer gzippedData) throws IOException { try { return doReadHeader(gzippedData); } catch (BufferUnderflowException e) { throw new EOFException(); } } private static int doReadHeader(ByteBuffer gzippedData) throws IOException { if ( gzippedData.getChar() != GZIP_MAGIC ) { throw new ZipException("Not in GZip format"); } if ( gzippedData.get() != Deflater.DEFLATED ) { throw new ZipException("Unsupported compression method"); } int flags = gzippedData.get(); // Skip MTIME, XFL, and OS fields skip(gzippedData, Integer.BYTES + Byte.BYTES + Byte.BYTES); if ( (flags & FEXTRA) != 0 ) { int extraBytes = gzippedData.getChar(); skip(gzippedData, extraBytes); } if ( (flags & FNAME) != 0 ) { skipZeroTerminatedString(gzippedData); } if ( (flags & FCOMMENT) != 0 ) { skipZeroTerminatedString(gzippedData); } if ( (flags & FHCRC) != 0 ) { CRC32 crc = new CRC32(); crc.update(gzippedData.array(), 0, gzippedData.position()); if ( gzippedData.getChar() != (char) crc.getValue() ) { throw new ZipException("Corrupt GZIP header"); } } return gzippedData.position(); } private static void skip(ByteBuffer gzippedData, int skipBytes) throws IOException { try { gzippedData.position(gzippedData.position() + skipBytes); } catch (IllegalArgumentException e) { throw new EOFException(); } } private static void skipZeroTerminatedString(ByteBuffer gzippedData) { while (gzippedData.get() != 0) { // loop } } } IdempotentUtils.java000066400000000000000000000024401442004423600365520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import java.util.Arrays; /* * Utilties Class for idempotent operations. */ class IdempotentUtils { /** * Returns whether the version and data currently in the node match what would be expected in the idempotent retry case. */ static boolean matches(int expectedVersion, byte[] expectedData, int actualVersion, byte[] actualData) { return expectedVersion == actualVersion && Arrays.equals(expectedData, actualData); } } InternalConnectionHandler.java000066400000000000000000000017611442004423600405200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; interface InternalConnectionHandler { void checkNewConnection(CuratorFrameworkImpl client); void suspendConnection(CuratorFrameworkImpl client); } NamespaceFacade.java000066400000000000000000000103751442004423600364070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.RetryLoop; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.EnsurePath; import org.apache.zookeeper.ZooKeeper; class NamespaceFacade extends CuratorFrameworkImpl { private final CuratorFrameworkImpl client; private final NamespaceImpl namespace; private final FailedDeleteManager failedDeleteManager = new FailedDeleteManager(this); NamespaceFacade(CuratorFrameworkImpl client, String namespace) { super(client); this.client = client; this.namespace = new NamespaceImpl(client, namespace); } @Override public CuratorFramework nonNamespaceView() { return usingNamespace(null); } @Override public CuratorFramework usingNamespace(String newNamespace) { return client.getNamespaceFacadeCache().get(newNamespace); } @Override public String getNamespace() { return namespace.getNamespace(); } @Override public void start() { throw new UnsupportedOperationException(); } @Override public void close() { throw new UnsupportedOperationException(); } @Override public Listenable getConnectionStateListenable() { return client.getConnectionStateListenable(); } @Override public Listenable getCuratorListenable() { throw new UnsupportedOperationException("getCuratorListenable() is only available from a non-namespaced CuratorFramework instance"); } @Override public Listenable getUnhandledErrorListenable() { return client.getUnhandledErrorListenable(); } @Override public void sync(String path, Object context) { internalSync(this, path, context); } @Override public CuratorZookeeperClient getZookeeperClient() { return client.getZookeeperClient(); } @Override RetryLoop newRetryLoop() { return client.newRetryLoop(); } @Override ZooKeeper getZooKeeper() throws Exception { return client.getZooKeeper(); } @Override void processBackgroundOperation(OperationAndData operationAndData, CuratorEvent event) { client.processBackgroundOperation(operationAndData, event); } @Override void logError(String reason, Throwable e) { client.logError(reason, e); } @Override String unfixForNamespace(String path) { return namespace.unfixForNamespace(path); } @Override String fixForNamespace(String path) { return namespace.fixForNamespace(path, false); } @Override String fixForNamespace(String path, boolean isSequential) { return namespace.fixForNamespace(path, isSequential); } @Override public EnsurePath newNamespaceAwareEnsurePath(String path) { return namespace.newNamespaceAwareEnsurePath(path); } @Override FailedDeleteManager getFailedDeleteManager() { return failedDeleteManager; } } NamespaceFacadeCache.java000066400000000000000000000042061442004423600373270ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; class NamespaceFacadeCache { private final CuratorFrameworkImpl client; private final NamespaceFacade nullNamespace; private final CacheLoader loader = new CacheLoader() { @Override public NamespaceFacade load(String namespace) throws Exception { return new NamespaceFacade(client, namespace); } }; private final LoadingCache cache = CacheBuilder.newBuilder() .expireAfterAccess(5, TimeUnit.MINUTES) // does this need config? probably not .build(loader); NamespaceFacadeCache(CuratorFrameworkImpl client) { this.client = client; nullNamespace = new NamespaceFacade(client, null); } NamespaceFacade get(String namespace) { try { return (namespace != null) ? cache.get(namespace) : nullNamespace; } catch ( ExecutionException e ) { throw new RuntimeException(e); // should never happen } } } NamespaceImpl.java000066400000000000000000000072751442004423600361520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.RetryLoop; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.EnsurePath; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.ZooDefs; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; class NamespaceImpl { private final CuratorFrameworkImpl client; private final String namespace; private final AtomicBoolean ensurePathNeeded; NamespaceImpl(CuratorFrameworkImpl client, String namespace) { if ( namespace != null ) { try { PathUtils.validatePath("/" + namespace); } catch ( IllegalArgumentException e ) { throw new IllegalArgumentException("Invalid namespace: " + namespace + ", " + e.getMessage()); } } this.client = client; this.namespace = namespace; ensurePathNeeded = new AtomicBoolean(namespace != null); } String getNamespace() { return namespace; } String unfixForNamespace(String path) { if ( (namespace != null) && (path != null) ) { String namespacePath = ZKPaths.makePath(namespace, null); if ( !namespacePath.equals("/") && path.startsWith(namespacePath) ) { path = (path.length() > namespacePath.length()) ? path.substring(namespacePath.length()) : "/"; } } return path; } String fixForNamespace(String path, boolean isSequential) { if ( ensurePathNeeded.get() ) { try { final CuratorZookeeperClient zookeeperClient = client.getZookeeperClient(); RetryLoop.callWithRetry ( zookeeperClient, new Callable() { @Override public Object call() throws Exception { ZKPaths.mkdirs(zookeeperClient.getZooKeeper(), ZKPaths.makePath("/", namespace), true, client.getAclProvider(), true); return null; } } ); ensurePathNeeded.set(false); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); client.logError("Ensure path threw exception", e); } } return ZKPaths.fixForNamespace(namespace, path, isSequential); } EnsurePath newNamespaceAwareEnsurePath(String path) { return new EnsurePath(fixForNamespace(path, false), client.getAclProvider()); } } NamespaceWatchedEvent.java000066400000000000000000000021511442004423600376160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.zookeeper.WatchedEvent; class NamespaceWatchedEvent extends WatchedEvent { NamespaceWatchedEvent(CuratorFrameworkImpl client, WatchedEvent event) { super(event.getType(), event.getState(), client.unfixForNamespace(event.getPath())); } } NamespaceWatcher.java000066400000000000000000000075071442004423600366440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.utils.ThreadUtils; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import java.io.Closeable; class NamespaceWatcher implements Watcher, Closeable { private volatile CuratorFrameworkImpl client; private volatile Watcher actualWatcher; private final String unfixedPath; private volatile CuratorWatcher curatorWatcher; NamespaceWatcher(CuratorFrameworkImpl client, Watcher actualWatcher, String unfixedPath) { this.client = client; this.actualWatcher = actualWatcher; this.unfixedPath = Preconditions.checkNotNull(unfixedPath, "unfixedPath cannot be null"); this.curatorWatcher = null; } NamespaceWatcher(CuratorFrameworkImpl client, CuratorWatcher curatorWatcher, String unfixedPath) { this.client = client; this.actualWatcher = null; this.curatorWatcher = curatorWatcher; this.unfixedPath = Preconditions.checkNotNull(unfixedPath, "unfixedPath cannot be null"); } String getUnfixedPath() { return unfixedPath; } @Override public void close() { client = null; actualWatcher = null; curatorWatcher = null; } @Override public void process(WatchedEvent event) { if ( client != null ) { if ( (event.getType() != Event.EventType.None) && (client.getWatcherRemovalManager() != null) ) { client.getWatcherRemovalManager().noteTriggeredWatcher(this); } if ( actualWatcher != null ) { actualWatcher.process(new NamespaceWatchedEvent(client, event)); } else if ( curatorWatcher != null ) { try { curatorWatcher.process(new NamespaceWatchedEvent(client, event)); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); client.logError("Watcher exception", e); } } } } /** * NamespaceWatcher should equal other wrappers that wrap the same instance. */ @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null ) { return false; } if ( getClass() == o.getClass() ) { NamespaceWatcher watcher = (NamespaceWatcher)o; return Objects.equal(unfixedPath, watcher.getUnfixedPath()) && Objects.equal(actualWatcher, watcher.actualWatcher) && Objects.equal(curatorWatcher, watcher.curatorWatcher); } return false; } @Override public int hashCode() { return Objects.hashCode(actualWatcher, unfixedPath, curatorWatcher); } } OperationAndData.java000066400000000000000000000105011442004423600365730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import org.apache.curator.RetrySleeper; import org.apache.curator.framework.api.BackgroundCallback; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; class OperationAndData implements Delayed, RetrySleeper { private static final AtomicLong nextOrdinal = new AtomicLong(); private final BackgroundOperation operation; private final T data; private final BackgroundCallback callback; private final long startTimeMs = System.currentTimeMillis(); private final ErrorCallback errorCallback; private final AtomicInteger retryCount = new AtomicInteger(0); private final AtomicLong sleepUntilTimeMs = new AtomicLong(0); private final AtomicLong ordinal = new AtomicLong(); private final Object context; private final boolean connectionRequired; interface ErrorCallback { void retriesExhausted(OperationAndData operationAndData); } OperationAndData(BackgroundOperation operation, T data, BackgroundCallback callback, ErrorCallback errorCallback, Object context, boolean connectionRequired) { this.operation = operation; this.data = data; this.callback = callback; this.errorCallback = errorCallback; this.context = context; this.connectionRequired = connectionRequired; reset(); } void reset() { retryCount.set(0); ordinal.set(nextOrdinal.getAndIncrement()); } OperationAndData(BackgroundOperation operation, T data, BackgroundCallback callback, ErrorCallback errorCallback, Object context, Watching watching) { this(operation, data, callback, errorCallback, context, true); } Object getContext() { return context; } boolean isConnectionRequired() { return connectionRequired; } void callPerformBackgroundOperation() throws Exception { operation.performBackgroundOperation(this); } T getData() { return data; } long getElapsedTimeMs() { return System.currentTimeMillis() - startTimeMs; } int getThenIncrementRetryCount() { return retryCount.getAndIncrement(); } BackgroundCallback getCallback() { return callback; } ErrorCallback getErrorCallback() { return errorCallback; } @VisibleForTesting BackgroundOperation getOperation() { return operation; } void clearSleep() { sleepUntilTimeMs.set(0); } @Override public void sleepFor(long time, TimeUnit unit) throws InterruptedException { sleepUntilTimeMs.set(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(time, unit)); } @Override public long getDelay(TimeUnit unit) { return unit.convert(sleepUntilTimeMs.get() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed o) { if ( o == this ) { return 0; } long diff = getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS); if ( diff == 0 ) { if ( o instanceof OperationAndData ) { diff = ordinal.get() - ((OperationAndData)o).ordinal.get(); } } return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0); } } PathAndBytes.java000066400000000000000000000022121442004423600357440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; class PathAndBytes { private final String path; private final byte[] data; PathAndBytes(String path, byte[] data) { this.path = path; this.data = data; } String getPath() { return path; } byte[] getData() { return data; } } ProtectedMode.java000066400000000000000000000066331442004423600361670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.zookeeper.CreateMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.UUID; /** * manage the protected mode state for {@link org.apache.curator.framework.imps.CreateBuilderImpl} */ class ProtectedMode { private final Logger log = LoggerFactory.getLogger(getClass()); private volatile String protectedId = null; private volatile long sessionId = 0; /** * Enable protected mode */ void setProtectedMode() { resetProtectedId(); } /** * Update the protected mode ID */ void resetProtectedId() { protectedId = UUID.randomUUID().toString(); } /** * @return true if protected mode has been enabled */ boolean doProtected() { return (protectedId != null); } /** * @return the protected mode ID if protected mode is enabled */ String protectedId() { return protectedId; } /** * Record the current session ID if needed * * @param client current client * @param createMode create mode in use * @throws Exception errors */ void checkSetSessionId(CuratorFrameworkImpl client, CreateMode createMode) throws Exception { if ( doProtected() && (sessionId == 0) && createMode.isEphemeral() ) { sessionId = client.getZooKeeper().getSessionId(); } } /** * Validate the found protected-mode node based on the set session ID, etc. * * @param client current client * @param createMode create mode in use * @param foundNode the found node * @return either the found node or null - client should always use the returned value * @throws Exception errors */ String validateFoundNode(CuratorFrameworkImpl client, CreateMode createMode, String foundNode) throws Exception { if ( doProtected() && createMode.isEphemeral() ) { long clientSessionId = client.getZooKeeper().getSessionId(); if ( this.sessionId != clientSessionId ) { log.info("Session has changed during protected mode with ephemeral. old: {} new: {}", this.sessionId, clientSessionId); if ( foundNode != null ) { log.info("Deleted old session's found node: {}", foundNode); client.getFailedDeleteManager().executeGuaranteedOperationInBackground(foundNode); foundNode = null; } this.sessionId = clientSessionId; } } return foundNode; } } ProtectedUtils.java000066400000000000000000000147611442004423600364040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import org.apache.curator.framework.api.CreateBuilderMain; import org.apache.curator.utils.ZKPaths; import java.util.Optional; import java.util.UUID; /** * Utility class to handle ZNode names when using {@link CreateBuilderMain#withProtection()} */ public final class ProtectedUtils { private ProtectedUtils() { throw new RuntimeException("Protected Utils is a helper class"); } /** * First 3 characters in the prefix on ZNode names when using {@link CreateBuilderMain#withProtection()} */ @VisibleForTesting static final String PROTECTED_PREFIX = "_c_"; /** * Last character used in the prefix on ZNode names when using {@link CreateBuilderMain#withProtection()} */ @VisibleForTesting static final char PROTECTED_SEPARATOR = '-'; /** * Prefix length on ZNode name when using {@link CreateBuilderMain#withProtection()} */ @VisibleForTesting static final int PROTECTED_PREFIX_WITH_UUID_LENGTH = PROTECTED_PREFIX.length() + 36 // UUID canonical text representation produced by {@link UUID#toString()} + 1; // Separator length /** * Provides a prefix to be prepended to a ZNode name when protected. The method assumes that the provided string * adjusts to the required format * * @param protectedId canonical text representation of a UUID * @return string that concatenates {@value #PROTECTED_PREFIX}, the given id and {@value #PROTECTED_SEPARATOR} */ public static String getProtectedPrefix(final String protectedId) { return PROTECTED_PREFIX + protectedId + PROTECTED_SEPARATOR; } /** Extracts protectedId assuming provided name has a valid protected format */ private static String extractProtectedIdInternal(final String znodeName) { return znodeName.substring(PROTECTED_PREFIX.length(), PROTECTED_PREFIX_WITH_UUID_LENGTH - 1); } /** * Utility method to determine if a given ZNode name starts with Curator's generated protected prefix. * * @param znodeName ZNode name * @return {@code true} if the given ZNode name starts with Curator's generated protected prefix */ public static boolean isProtectedZNode(final String znodeName) { if ( znodeName.length() > PROTECTED_PREFIX_WITH_UUID_LENGTH && znodeName.startsWith(PROTECTED_PREFIX) && znodeName.charAt(PROTECTED_PREFIX_WITH_UUID_LENGTH - 1) == PROTECTED_SEPARATOR ) { try { //noinspection ResultOfMethodCallIgnored UUID.fromString(extractProtectedIdInternal(znodeName)); return true; } catch ( IllegalArgumentException e ) { // Not an UUID } } return false; } /** * Utility method to remove Curator's generated protected prefix if present * * @param znodeName ZNode name * @return string without Curator's generated protected prefix if present; original string if prefix not present */ public static String normalize(final String znodeName) { return isProtectedZNode(znodeName) ? znodeName.substring(PROTECTED_PREFIX_WITH_UUID_LENGTH) : znodeName; } /** * Utility method to provide a path removing Curator's generated protected prefix if present in the ZNode name * * @param path ZNode path * @return string without Curator's generated protected prefix if present in ZNode name; original string if prefix not present */ public static String normalizePath(final String path) { final ZKPaths.PathAndNode pathAndNode = ZKPaths.getPathAndNode(path); final String name = pathAndNode.getNode(); return isProtectedZNode(name) ? ZKPaths.makePath(pathAndNode.getPath(), normalize(name)) : path; } /** * Extracts protectedId in case the provided name is protected * * @param znodeName name of the ZNode * @return Optional with protectedId if the name is protected or {@code Optional#empty()} */ public static Optional extractProtectedId(final String znodeName) { return Optional.ofNullable(isProtectedZNode(znodeName) ? extractProtectedIdInternal(znodeName) : null); } /** * Converts a given ZNode name to protected format * * @param znodeName name to be converted (e.g. 'name1') * @param protectedId UUID canonical text representation used in protection mode (e.g. '53345f98-9423-4e0c-a7b5-9f819e3ec2e1') * @return name with protected mode prefix (e.g. '_c_53345f98-9423-4e0c-a7b5-9f819e3ec2e1-name1') * or the same name if protectedId is {@code null} */ public static String toProtectedZNode(final String znodeName, final String protectedId) { return (protectedId == null) ? znodeName : getProtectedPrefix(protectedId) + znodeName; } /** * Converts a given path to protected format * * @param path complete path to be converted (e.g. '/root/path1') * @param protectedId UUID canonical text representation used in protection mode (e.g. '53345f98-9423-4e0c-a7b5-9f819e3ec2e1') * @return path with protected mode prefix (e.g. '/root/_c_53345f98-9423-4e0c-a7b5-9f819e3ec2e1-path1') * or the same path if protectedId is {@code null} */ public static String toProtectedZNodePath(final String path, final String protectedId) { if ( protectedId == null ) { return path; } final ZKPaths.PathAndNode pathAndNode = ZKPaths.getPathAndNode(path); return ZKPaths.makePath(pathAndNode.getPath(), toProtectedZNode(pathAndNode.getNode(), protectedId)); } } ReconfigBuilderImpl.java000066400000000000000000000230121442004423600373040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.collect.ImmutableList; import org.apache.curator.RetryLoop; import org.apache.curator.TimeTrace; import org.apache.curator.framework.api.*; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.DataTree; import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class ReconfigBuilderImpl implements ReconfigBuilder, BackgroundOperation, ErrorListenerReconfigBuilderMain { private final CuratorFrameworkImpl client; private Backgrounding backgrounding = new Backgrounding(); private Stat responseStat; private long fromConfig = -1; private List newMembers; private List joining; private List leaving; public ReconfigBuilderImpl(CuratorFrameworkImpl client) { this.client = client; } public ReconfigBuilderImpl(CuratorFrameworkImpl client, Backgrounding backgrounding, Stat responseStat, long fromConfig, List newMembers, List joining, List leaving) { this.client = client; this.backgrounding = backgrounding; this.responseStat = responseStat; this.fromConfig = fromConfig; this.newMembers = newMembers; this.joining = joining; this.leaving = leaving; } public byte[] forEnsemble() throws Exception { if ( backgrounding.inBackground() ) { client.processBackgroundOperation(new OperationAndData<>(this, null, backgrounding.getCallback(), null, backgrounding.getContext(), null), null); return new byte[0]; } else { return ensembleInForeground(); } } @Override public ErrorListenerReconfigBuilderMain inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerReconfigBuilderMain inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public ErrorListenerReconfigBuilderMain inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerReconfigBuilderMain inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerReconfigBuilderMain inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(callback, executor); return this; } @Override public ErrorListenerReconfigBuilderMain inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ReconfigBuilderMain withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public StatConfigureEnsembleable withNewMembers(String... server) { return withNewMembers((server != null) ? Arrays.asList(server) : null); } @Override public StatConfigureEnsembleable withNewMembers(List servers) { newMembers = (servers != null && !servers.isEmpty()) ? ImmutableList.copyOf(servers) : null; return new StatConfigureEnsembleable() { @Override public Ensembleable fromConfig(long config) throws Exception { fromConfig = config; return this; } @Override public byte[] forEnsemble() throws Exception { return ReconfigBuilderImpl.this.forEnsemble(); } @Override public ConfigureEnsembleable storingStatIn(Stat stat) { responseStat = stat; return this; } }; } @Override public LeaveStatConfigEnsembleable joining(String... server) { return joining((server != null) ? Arrays.asList(server) : null); } @Override public LeaveStatConfigEnsembleable joining(List servers) { joining = (servers != null && !servers.isEmpty()) ? ImmutableList.copyOf(servers) : null; return new LeaveStatConfigEnsembleable() { @Override public byte[] forEnsemble() throws Exception { return ReconfigBuilderImpl.this.forEnsemble(); } @Override public ConfigureEnsembleable storingStatIn(Stat stat) { responseStat = stat; return this; } @Override public Ensembleable fromConfig(long config) throws Exception { fromConfig = config; return this; } @Override public JoinStatConfigEnsembleable leaving(String... server) { return ReconfigBuilderImpl.this.leaving(server); } @Override public JoinStatConfigEnsembleable leaving(List servers) { return ReconfigBuilderImpl.this.leaving(servers); } }; } @Override public JoinStatConfigEnsembleable leaving(String... server) { return leaving((server != null) ? Arrays.asList(server) : null); } @Override public JoinStatConfigEnsembleable leaving(List servers) { leaving = (servers != null && !servers.isEmpty()) ? ImmutableList.copyOf(servers) : null; return new JoinStatConfigEnsembleable() { @Override public byte[] forEnsemble() throws Exception { return ReconfigBuilderImpl.this.forEnsemble(); } @Override public ConfigureEnsembleable storingStatIn(Stat stat) { responseStat = stat; return this; } @Override public Ensembleable fromConfig(long config) throws Exception { fromConfig = config; return this; } @Override public LeaveStatConfigEnsembleable joining(String... server) { return joining((server != null) ? Arrays.asList(server) : null); } @Override public LeaveStatConfigEnsembleable joining(List servers) { return ReconfigBuilderImpl.this.joining(servers); } }; } @Override public void performBackgroundOperation(final OperationAndData data) throws Exception { try { final TimeTrace trace = client.getZookeeperClient().startTracer("ReconfigBuilderImpl-Background"); AsyncCallback.DataCallback callback = new AsyncCallback.DataCallback() { @Override public void processResult(int rc, String path, Object ctx, byte[] bytes, Stat stat) { trace.commit(); if ( (responseStat != null) && (stat != null) ) { DataTree.copyStat(stat, responseStat); } CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.RECONFIG, rc, path, null, ctx, stat, bytes, null, null, null, null); client.processBackgroundOperation(data, event); } }; ((ZooKeeperAdmin)client.getZooKeeper()).reconfigure(joining, leaving, newMembers, fromConfig, callback, backgrounding.getContext()); } catch ( Throwable e ) { backgrounding.checkError(e, null); } } private byte[] ensembleInForeground() throws Exception { TimeTrace trace = client.getZookeeperClient().startTracer("ReconfigBuilderImpl-Foreground"); byte[] responseData = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public byte[] call() throws Exception { return ((ZooKeeperAdmin)client.getZooKeeper()).reconfigure(joining, leaving, newMembers, fromConfig, responseStat); } } ); trace.commit(); return responseData; } } RemoveWatchesBuilderImpl.java000066400000000000000000000276711442004423600403430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.RetryLoop; import org.apache.curator.TimeTrace; import org.apache.curator.framework.api.*; import org.apache.curator.utils.DebugUtils; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.WatcherType; import org.apache.zookeeper.ZooKeeper; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class RemoveWatchesBuilderImpl implements RemoveWatchesBuilder, RemoveWatchesType, RemoveWatchesLocal, BackgroundOperation, ErrorListenerPathable { private CuratorFrameworkImpl client; private Watcher watcher; private CuratorWatcher curatorWatcher; private WatcherType watcherType; private boolean guaranteed; private boolean local; private boolean quietly; private Backgrounding backgrounding; public RemoveWatchesBuilderImpl(CuratorFrameworkImpl client) { this.client = client; this.watcher = null; this.curatorWatcher = null; this.watcherType = WatcherType.Any; this.guaranteed = false; this.local = false; this.quietly = false; this.backgrounding = new Backgrounding(); } public RemoveWatchesBuilderImpl(CuratorFrameworkImpl client, Watcher watcher, CuratorWatcher curatorWatcher, WatcherType watcherType, boolean guaranteed, boolean local, boolean quietly, Backgrounding backgrounding) { this.client = client; this.watcher = watcher; this.curatorWatcher = curatorWatcher; this.watcherType = watcherType; this.guaranteed = guaranteed; this.local = local; this.quietly = quietly; this.backgrounding = backgrounding; } void internalRemoval(Watcher watcher, String path) throws Exception { this.watcher = watcher; watcherType = WatcherType.Any; quietly = true; guaranteed = true; if ( Boolean.getBoolean(DebugUtils.PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND) ) { this.backgrounding = new Backgrounding(); pathInForeground(path); } else { this.backgrounding = new Backgrounding(true); pathInBackground(path); } } @Override public RemoveWatchesType remove(Watcher watcher) { this.watcher = watcher; this.curatorWatcher = null; return this; } @Override public RemoveWatchesType remove(CuratorWatcher watcher) { this.watcher = null; this.curatorWatcher = watcher; return this; } @Override public RemoveWatchesType removeAll() { this.watcher = null; this.curatorWatcher = null; return this; } @Override public RemoveWatchesLocal ofType(WatcherType watcherType) { this.watcherType = watcherType; return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public ErrorListenerPathable inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerPathable inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public Pathable withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public RemoveWatchesLocal guaranteed() { guaranteed = true; return this; } @Override public BackgroundPathableQuietlyable locally() { local = true; return this; } @Override public BackgroundPathable quietly() { quietly = true; return this; } @Override public Void forPath(String path) throws Exception { final String adjustedPath = client.fixForNamespace(path); if(backgrounding.inBackground()) { pathInBackground(adjustedPath); } else { pathInForeground(adjustedPath); } return null; } protected CuratorFrameworkImpl getClient() { return client; } private void pathInBackground(final String path) { OperationAndData.ErrorCallback errorCallback = null; //Only need an error callback if we're in guaranteed mode if(guaranteed) { errorCallback = new OperationAndData.ErrorCallback() { @Override public void retriesExhausted(OperationAndData operationAndData) { client.getFailedRemoveWatcherManager().addFailedOperation(new FailedRemoveWatchManager.FailedRemoveWatchDetails(path, watcher)); } }; } client.processBackgroundOperation(new OperationAndData(this, path, backgrounding.getCallback(), errorCallback, backgrounding.getContext(), !local), null); } private void pathInForeground(final String path) throws Exception { NamespaceWatcher namespaceWatcher = makeNamespaceWatcher(path); //For the local case we don't want to use the normal retry loop and we don't want to block until a connection is available. //We just execute the removeWatch, and if it fails, ZK will just remove local watches. if ( local ) { ZooKeeper zkClient = client.getZooKeeper(); if ( namespaceWatcher != null ) { zkClient.removeWatches(path, namespaceWatcher, watcherType, local); } else { zkClient.removeAllWatches(path, watcherType, local); } } else { final NamespaceWatcher finalNamespaceWatcher = namespaceWatcher; RetryLoop.callWithRetry(client.getZookeeperClient(), new Callable() { @Override public Void call() throws Exception { try { ZooKeeper zkClient = client.getZookeeperClient().getZooKeeper(); if ( finalNamespaceWatcher != null ) { zkClient.removeWatches(path, finalNamespaceWatcher, watcherType, false); } else { zkClient.removeAllWatches(path, watcherType, false); } } catch(Exception e) { if ( client.getZookeeperClient().getRetryPolicy().allowRetry(e) && guaranteed ) { //Setup the guaranteed handler client.getFailedRemoveWatcherManager().addFailedOperation(new FailedRemoveWatchManager.FailedRemoveWatchDetails(path, finalNamespaceWatcher)); throw e; } else if (e instanceof KeeperException.NoWatcherException && quietly) { // ignore } else { //Rethrow throw e; } } return null; } }); } } private NamespaceWatcher makeNamespaceWatcher(String path) { NamespaceWatcher namespaceWatcher = null; if ( watcher != null ) { if ( watcher instanceof NamespaceWatcher ) { namespaceWatcher = (NamespaceWatcher)watcher; } else { namespaceWatcher = new NamespaceWatcher(client, watcher, path); } } else if ( curatorWatcher != null ) { namespaceWatcher = new NamespaceWatcher(client, curatorWatcher, path); } return namespaceWatcher; } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final TimeTrace trace = client.getZookeeperClient().startTracer("RemoteWatches-Background"); AsyncCallback.VoidCallback callback = new AsyncCallback.VoidCallback() { @Override public void processResult(int rc, String path, Object ctx) { trace.commit(); CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.REMOVE_WATCHES, rc, path, null, ctx, null, null, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } }; ZooKeeper zkClient = client.getZooKeeper(); NamespaceWatcher namespaceWatcher = makeNamespaceWatcher(operationAndData.getData()); if(namespaceWatcher == null) { zkClient.removeAllWatches(operationAndData.getData(), watcherType, local, callback, operationAndData.getContext()); } else { zkClient.removeWatches(operationAndData.getData(), namespaceWatcher, watcherType, local, callback, operationAndData.getContext()); } } catch ( Throwable e ) { backgrounding.checkError(e, null); } } } SetACLBuilderImpl.java000066400000000000000000000147401442004423600366330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.*; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.SetACLBuilder; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class SetACLBuilderImpl implements SetACLBuilder, BackgroundPathable, BackgroundOperation, ErrorListenerPathable { private final CuratorFrameworkImpl client; private ACLing acling; private Backgrounding backgrounding; private int version; SetACLBuilderImpl(CuratorFrameworkImpl client) { this.client = client; backgrounding = new Backgrounding(); acling = new ACLing(client.getAclProvider()); version = -1; } public SetACLBuilderImpl(CuratorFrameworkImpl client, Backgrounding backgrounding, List aclList, int version) { this.client = client; this.acling = new ACLing(client.getAclProvider(), aclList); this.version = version; this.backgrounding = backgrounding; } @Override public BackgroundPathable withACL(List aclList) { acling = new ACLing(client.getAclProvider(), aclList, false); return this; } @Override public ACLable> withVersion(int version) { this.version = version; return this; } @Override public ErrorListenerPathable inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerPathable inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public Pathable withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public Stat forPath(String path) throws Exception { String fixedPath = client.fixForNamespace(path); List aclList = acling.getAclList(fixedPath); client.getSchemaSet().getSchema(path).validateGeneral(path, null, aclList); Stat resultStat = null; if ( backgrounding.inBackground() ) { client.processBackgroundOperation(new OperationAndData(this, fixedPath, backgrounding.getCallback(), null, backgrounding.getContext(), null), null); } else { resultStat = pathInForeground(fixedPath, aclList); } return resultStat; } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("SetACLBuilderImpl-Background"); String path = operationAndData.getData(); client.getZooKeeper().setACL ( path, acling.getAclList(path), version, new AsyncCallback.StatCallback() { @SuppressWarnings({"unchecked"}) @Override public void processResult(int rc, String path, Object ctx, Stat stat) { trace.setReturnCode(rc).setPath(path).setStat(stat).commit(); CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SET_ACL, rc, path, null, ctx, stat, null, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } }, backgrounding.getContext() ); } catch ( Throwable e ) { backgrounding.checkError(e, null); } } private Stat pathInForeground(final String path, final List aclList) throws Exception { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("SetACLBuilderImpl-Foreground"); Stat resultStat = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public Stat call() throws Exception { return client.getZooKeeper().setACL(path, aclList, version); } } ); trace.setPath(path).setStat(resultStat).commit(); return resultStat; } } SetDataBuilderImpl.java000066400000000000000000000376271442004423600371160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.*; import org.apache.curator.framework.api.transaction.OperationType; import org.apache.curator.framework.api.transaction.TransactionSetDataBuilder; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.Executor; public class SetDataBuilderImpl implements SetDataBuilder, BackgroundOperation, ErrorListenerPathAndBytesable { private final CuratorFrameworkImpl client; private Backgrounding backgrounding; private int version; private boolean compress; private boolean idempotent = false; @VisibleForTesting boolean failNextSetForTesting = false; @VisibleForTesting boolean failBeforeNextSetForTesting = false; @VisibleForTesting boolean failNextIdempotentCheckForTesting = false; SetDataBuilderImpl(CuratorFrameworkImpl client) { this.client = client; backgrounding = new Backgrounding(); version = -1; compress = false; } public SetDataBuilderImpl(CuratorFrameworkImpl client, Backgrounding backgrounding, int version, boolean compress) { this.client = client; this.backgrounding = backgrounding; this.version = version; this.compress = compress; } TransactionSetDataBuilder asTransactionSetDataBuilder(final T context, final CuratorMultiTransactionRecord transaction) { return new TransactionSetDataBuilder() { @Override public T forPath(String path, byte[] data) throws Exception { if ( compress ) { data = client.getCompressionProvider().compress(path, data); } String fixedPath = client.fixForNamespace(path); transaction.add(Op.setData(fixedPath, data, version), OperationType.SET_DATA, path); return context; } @Override public T forPath(String path) throws Exception { return forPath(path, client.getDefaultData()); } @Override public PathAndBytesable withVersion(int version) { SetDataBuilderImpl.this.withVersion(version); return this; } @Override public VersionPathAndBytesable compressed() { compress = true; return this; } }; } @Override public SetDataBackgroundVersionable compressed() { compress = true; return new SetDataBackgroundVersionable() { @Override public ErrorListenerPathAndBytesable inBackground() { return SetDataBuilderImpl.this.inBackground(); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context) { return SetDataBuilderImpl.this.inBackground(callback, context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context, Executor executor) { return SetDataBuilderImpl.this.inBackground(callback, context, executor); } @Override public ErrorListenerPathAndBytesable inBackground(Object context) { return SetDataBuilderImpl.this.inBackground(context); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback) { return SetDataBuilderImpl.this.inBackground(callback); } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Executor executor) { return SetDataBuilderImpl.this.inBackground(callback, executor); } @Override public Stat forPath(String path, byte[] data) throws Exception { return SetDataBuilderImpl.this.forPath(path, data); } @Override public Stat forPath(String path) throws Exception { return SetDataBuilderImpl.this.forPath(path); } @Override public BackgroundPathAndBytesable withVersion(int version) { return SetDataBuilderImpl.this.withVersion(version); } }; } @Override public BackgroundPathAndBytesable withVersion(int version) { this.version = version; return this; } @Override public SetDataBuilder idempotent() { this.idempotent = true; return this; } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathAndBytesable inBackground() { backgrounding = new Backgrounding(true); return this; } @Override public ErrorListenerPathAndBytesable inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public ErrorListenerPathAndBytesable inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public PathAndBytesable withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } private void backgroundCheckIdempotent(final CuratorFrameworkImpl client, final OperationAndData mainOperationAndData, final String path, final Backgrounding backgrounding) { final AsyncCallback.DataCallback dataCallback = new AsyncCallback.DataCallback() { @Override public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { if ( rc == KeeperException.Code.OK.intValue() ) { if ( failNextIdempotentCheckForTesting ) { failNextIdempotentCheckForTesting = false; rc = KeeperException.Code.CONNECTIONLOSS.intValue(); } else if ( !idempotentSetMatches(stat.getVersion(), mainOperationAndData.getData().getData(), data) ) { rc = KeeperException.Code.BADVERSION.intValue(); } } final CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SET_DATA, rc, path, null, ctx, stat, null, null, null, null, null); client.processBackgroundOperation(mainOperationAndData, event); } }; BackgroundOperation operation = new BackgroundOperation() { @Override public void performBackgroundOperation(OperationAndData op) throws Exception { try { client.getZooKeeper().getData(path, false, dataCallback, backgrounding.getContext()); } catch ( KeeperException e ) { // ignore client.logError("Unexpected exception in async idempotent check for, ignoring: " + path, e); } } }; client.queueOperation(new OperationAndData<>(operation, null, null, null, null, null)); } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("SetDataBuilderImpl-Background"); final byte[] data = operationAndData.getData().getData(); client.getZooKeeper().setData ( operationAndData.getData().getPath(), data, version, new AsyncCallback.StatCallback() { @SuppressWarnings({"unchecked"}) @Override public void processResult(int rc, String path, Object ctx, Stat stat) { trace.setReturnCode(rc).setRequestBytesLength(data).setPath(path).setStat(stat).commit(); if ( (rc == KeeperException.Code.OK.intValue()) && failNextSetForTesting ) { failNextSetForTesting = false; rc = KeeperException.Code.CONNECTIONLOSS.intValue(); } if ( rc == KeeperException.Code.BADVERSION.intValue() && idempotent ) { backgroundCheckIdempotent(client, operationAndData, operationAndData.getData().getPath(), backgrounding); } else { CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SET_DATA, rc, path, null, ctx, stat, null, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } } }, backgrounding.getContext() ); } catch ( Throwable e ) { backgrounding.checkError(e, null); } } @Override public Stat forPath(String path) throws Exception { return forPath(path, client.getDefaultData()); } @Override public Stat forPath(String path, byte[] data) throws Exception { client.getSchemaSet().getSchema(path).validateGeneral(path, data, null); if ( compress ) { data = client.getCompressionProvider().compress(path, data); } path = client.fixForNamespace(path); Stat resultStat = null; if ( backgrounding.inBackground() ) { OperationAndData operationAndData = new OperationAndData(this, new PathAndBytes(path, data), backgrounding.getCallback(), null, backgrounding.getContext(), null) { @Override void callPerformBackgroundOperation() throws Exception { // inject fault before performing operation if ( failBeforeNextSetForTesting ) { failBeforeNextSetForTesting = false; throw new KeeperException.ConnectionLossException(); } super.callPerformBackgroundOperation(); } }; client.processBackgroundOperation(operationAndData, null); } else { resultStat = pathInForeground(path, data); } return resultStat; } int getVersion() { return version; } /** * If the client did not specify a version (version == -1), idempotency just means that the node now has the specified data. * If the client did specify a version, idempotentcy has the additional constraint that the current version must be * one larger than the specified version, which is the behavior that would be observed in the normal (non-idempotent, non-failure) case for setData. */ private boolean idempotentSetMatches(int getVersion, byte[] data, byte[] getData) { return ( version == -1 || (version + 1 == getVersion) ) && Arrays.equals(data, getData); } private Stat pathInForeground(final String path, final byte[] data) throws Exception { OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("SetDataBuilderImpl-Foreground"); Stat resultStat = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public Stat call() throws Exception { if ( failBeforeNextSetForTesting ) { failBeforeNextSetForTesting = false; throw new KeeperException.ConnectionLossException(); } Stat localResultStat = null; try { localResultStat = client.getZooKeeper().setData(path, data, version); } catch ( KeeperException.BadVersionException e ) { if ( !idempotent ) { throw e; } Stat getStat = new Stat(); // inject fault before performing operation if ( failNextIdempotentCheckForTesting ) { failNextIdempotentCheckForTesting = false; throw new KeeperException.ConnectionLossException(); } byte[] existingData = client.getZooKeeper().getData(path, false, getStat); if ( idempotentSetMatches(getStat.getVersion(), data, existingData) ) { localResultStat = getStat; } else { throw e; } } if ( failNextSetForTesting ) { failNextSetForTesting = false; throw new KeeperException.ConnectionLossException(); } return localResultStat; } } ); trace.setRequestBytesLength(data).setPath(path).setStat(resultStat).commit(); return resultStat; } } StandardInternalConnectionHandler.java000066400000000000000000000022401442004423600421720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; class StandardInternalConnectionHandler implements InternalConnectionHandler { @Override public void suspendConnection(CuratorFrameworkImpl client) { client.setToSuspended(); } @Override public void checkNewConnection(CuratorFrameworkImpl client) { client.checkInstanceIndex(); } } SyncBuilderImpl.java000077500000000000000000000113161442004423600364730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.ErrorListenerPathable; import org.apache.curator.framework.api.Pathable; import org.apache.curator.framework.api.SyncBuilder; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.zookeeper.AsyncCallback; import java.util.concurrent.Executor; public class SyncBuilderImpl implements SyncBuilder, BackgroundOperation, ErrorListenerPathable { private final CuratorFrameworkImpl client; private Backgrounding backgrounding = new Backgrounding(); public SyncBuilderImpl(CuratorFrameworkImpl client) { //To change body of created methods use File | Settings | File Templates. this.client = client; } public SyncBuilderImpl(CuratorFrameworkImpl client, Backgrounding backgrounding) { this.client = client; this.backgrounding = backgrounding; } @Override public ErrorListenerPathable inBackground() { // NOP always in background return this; } @Override public ErrorListenerPathable inBackground(Object context) { backgrounding = new Backgrounding(context); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback) { backgrounding = new Backgrounding(callback); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context) { backgrounding = new Backgrounding(callback, context); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Executor executor) { backgrounding = new Backgrounding(client, callback, executor); return this; } @Override public ErrorListenerPathable inBackground(BackgroundCallback callback, Object context, Executor executor) { backgrounding = new Backgrounding(client, callback, context, executor); return this; } @Override public Pathable withUnhandledErrorListener(UnhandledErrorListener listener) { backgrounding = new Backgrounding(backgrounding, listener); return this; } @Override public void performBackgroundOperation(final OperationAndData operationAndData) throws Exception { try { final OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("SyncBuilderImpl-Background"); final String path = operationAndData.getData(); String adjustedPath = client.fixForNamespace(path); AsyncCallback.VoidCallback voidCallback = new AsyncCallback.VoidCallback() { @Override public void processResult(int rc, String path, Object ctx) { trace.setReturnCode(rc).setPath(path).commit(); CuratorEvent event = new CuratorEventImpl(client, CuratorEventType.SYNC, rc, path, path, ctx, null, null, null, null, null, null); client.processBackgroundOperation(operationAndData, event); } }; client.getZooKeeper().sync(adjustedPath, voidCallback, backgrounding.getContext()); } catch ( Throwable e ) { backgrounding.checkError(e, null); } } @Override public Void forPath(String path) throws Exception { OperationAndData operationAndData = new OperationAndData(this, path, backgrounding.getCallback(), null, backgrounding.getContext(), null); client.processBackgroundOperation(operationAndData, null); return null; } } TempGetDataBuilderImpl.java000066400000000000000000000051631442004423600377160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.RetryLoop; import org.apache.curator.drivers.OperationTrace; import org.apache.curator.framework.api.Pathable; import org.apache.curator.framework.api.StatPathable; import org.apache.curator.framework.api.TempGetDataBuilder; import org.apache.zookeeper.data.Stat; import java.util.concurrent.Callable; class TempGetDataBuilderImpl implements TempGetDataBuilder { private final CuratorFrameworkImpl client; private Stat responseStat; private boolean decompress; TempGetDataBuilderImpl(CuratorFrameworkImpl client) { this.client = client; responseStat = null; decompress = false; } @Override public StatPathable decompressed() { decompress = true; return this; } @Override public Pathable storingStatIn(Stat stat) { responseStat = stat; return this; } @Override public byte[] forPath(String path) throws Exception { final String localPath = client.fixForNamespace(path); OperationTrace trace = client.getZookeeperClient().startAdvancedTracer("GetDataBuilderImpl-Foreground"); byte[] responseData = RetryLoop.callWithRetry ( client.getZookeeperClient(), new Callable() { @Override public byte[] call() throws Exception { return client.getZooKeeper().getData(localPath, false, responseStat); } } ); trace.setResponseBytesLength(responseData).setPath(path).setStat(responseStat).commit(); return decompress ? client.getCompressionProvider().decompress(path, responseData) : responseData; } } TransactionOpImpl.java000066400000000000000000000047011442004423600370310ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.TransactionCheckBuilder; import org.apache.curator.framework.api.transaction.TransactionCreateBuilder; import org.apache.curator.framework.api.transaction.TransactionDeleteBuilder; import org.apache.curator.framework.api.transaction.TransactionOp; import org.apache.curator.framework.api.transaction.TransactionSetDataBuilder; public class TransactionOpImpl implements TransactionOp { private final CuratorFrameworkImpl client; public TransactionOpImpl(CuratorFrameworkImpl client) { this.client = client; } @Override public TransactionCreateBuilder create() { ExtractingCuratorOp op = new ExtractingCuratorOp(); return new CreateBuilderImpl(client).asTransactionCreateBuilder(op, op.getRecord()); } @Override public TransactionDeleteBuilder delete() { ExtractingCuratorOp op = new ExtractingCuratorOp(); return new DeleteBuilderImpl(client).asTransactionDeleteBuilder(op, op.getRecord()); } @Override public TransactionSetDataBuilder setData() { ExtractingCuratorOp op = new ExtractingCuratorOp(); return new SetDataBuilderImpl(client).asTransactionSetDataBuilder(op, op.getRecord()); } @Override public TransactionCheckBuilder check() { ExtractingCuratorOp op = new ExtractingCuratorOp(); return CuratorTransactionImpl.makeTransactionCheckBuilder(client, op, op.getRecord()); } } WatcherRemovalFacade.java000066400000000000000000000115001442004423600374250ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.RetryLoop; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.EnsurePath; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; class WatcherRemovalFacade extends CuratorFrameworkImpl implements WatcherRemoveCuratorFramework { private final CuratorFrameworkImpl client; private final WatcherRemovalManager removalManager; WatcherRemovalFacade(CuratorFrameworkImpl client) { super(client); this.client = client; removalManager = new WatcherRemovalManager(client); } @Override public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework() { return client.newWatcherRemoveCuratorFramework(); } WatcherRemovalManager getRemovalManager() { return removalManager; } @Override public QuorumVerifier getCurrentConfig() { return client.getCurrentConfig(); } @Override public void removeWatchers() { removalManager.removeWatchers(); } @Override WatcherRemovalManager getWatcherRemovalManager() { return removalManager; } @Override public CuratorFramework nonNamespaceView() { return client.nonNamespaceView(); } @Override public CuratorFramework usingNamespace(String newNamespace) { return client.usingNamespace(newNamespace); } @Override public String getNamespace() { return client.getNamespace(); } @Override public void start() { throw new UnsupportedOperationException(); } @Override public void close() { throw new UnsupportedOperationException(); } @Override public Listenable getConnectionStateListenable() { return client.getConnectionStateListenable(); } @Override public Listenable getCuratorListenable() { return client.getCuratorListenable(); } @Override public Listenable getUnhandledErrorListenable() { return client.getUnhandledErrorListenable(); } @Override public void sync(String path, Object context) { client.sync(path, context); } @Override public CuratorZookeeperClient getZookeeperClient() { return client.getZookeeperClient(); } @Override RetryLoop newRetryLoop() { return client.newRetryLoop(); } @Override ZooKeeper getZooKeeper() throws Exception { return client.getZooKeeper(); } @Override void processBackgroundOperation(OperationAndData operationAndData, CuratorEvent event) { client.processBackgroundOperation(operationAndData, event); } @Override void logError(String reason, Throwable e) { client.logError(reason, e); } @Override String unfixForNamespace(String path) { return client.unfixForNamespace(path); } @Override String fixForNamespace(String path) { return client.fixForNamespace(path); } @Override String fixForNamespace(String path, boolean isSequential) { return client.fixForNamespace(path, isSequential); } @Override public EnsurePath newNamespaceAwareEnsurePath(String path) { return client.newNamespaceAwareEnsurePath(path); } @Override FailedDeleteManager getFailedDeleteManager() { return client.getFailedDeleteManager(); } } WatcherRemovalManager.java000066400000000000000000000051751442004423600376470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.zookeeper.Watcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Set; public class WatcherRemovalManager { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFrameworkImpl client; private final Set entries = Sets.newConcurrentHashSet(); WatcherRemovalManager(CuratorFrameworkImpl client) { this.client = client; } void add(NamespaceWatcher watcher) { watcher = Preconditions.checkNotNull(watcher, "watcher cannot be null"); entries.add(watcher); } @VisibleForTesting Set getEntries() { return Sets.newHashSet(entries); } void removeWatchers() { List localEntries = Lists.newArrayList(entries); while ( localEntries.size() > 0 ) { NamespaceWatcher watcher = localEntries.remove(0); if ( entries.remove(watcher) ) { try { log.debug("Removing watcher for path: " + watcher.getUnfixedPath()); RemoveWatchesBuilderImpl builder = new RemoveWatchesBuilderImpl(client); builder.internalRemoval(watcher, watcher.getUnfixedPath()); } catch ( Exception e ) { log.error("Could not remove watcher for path: " + watcher.getUnfixedPath()); } } } } void noteTriggeredWatcher(NamespaceWatcher watcher) { entries.remove(watcher); } } WatchesBuilderImpl.java000066400000000000000000000033141442004423600371510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.framework.api.AddWatchBuilder; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.api.WatchesBuilder; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.WatcherType; public class WatchesBuilderImpl extends RemoveWatchesBuilderImpl implements WatchesBuilder { public WatchesBuilderImpl(CuratorFrameworkImpl client) { super(client); } public WatchesBuilderImpl(CuratorFrameworkImpl client, Watcher watcher, CuratorWatcher curatorWatcher, WatcherType watcherType, boolean guaranteed, boolean local, boolean quietly, Backgrounding backgrounding) { super(client, watcher, curatorWatcher, watcherType, guaranteed, local, quietly, backgrounding); } @Override public AddWatchBuilder add() { return new AddWatchBuilderImpl(getClient()); } }Watching.java000066400000000000000000000062021442004423600351650ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; public class Watching { private final Watcher watcher; private final CuratorWatcher curatorWatcher; private final boolean watched; private final CuratorFrameworkImpl client; private NamespaceWatcher namespaceWatcher; public Watching(CuratorFrameworkImpl client, boolean watched) { this.client = client; this.watcher = null; this.curatorWatcher = null; this.watched = watched; } public Watching(CuratorFrameworkImpl client, Watcher watcher) { this.client = client; this.watcher = watcher; this.curatorWatcher = null; this.watched = false; } public Watching(CuratorFrameworkImpl client, CuratorWatcher watcher) { this.client = client; this.watcher = null; this.curatorWatcher = watcher; this.watched = false; } public Watching(CuratorFrameworkImpl client) { this.client = client; watcher = null; watched = false; curatorWatcher = null; } Watcher getWatcher(String unfixedPath) { namespaceWatcher = null; if ( watcher != null ) { namespaceWatcher = new NamespaceWatcher(client, this.watcher, unfixedPath); } else if ( curatorWatcher != null ) { namespaceWatcher = new NamespaceWatcher(client, curatorWatcher, unfixedPath); } return namespaceWatcher; } boolean hasWatcher() { return (watcher != null) || (curatorWatcher != null); } boolean isWatched() { return watched; } void commitWatcher(int rc, boolean isExists) { boolean doCommit = false; if ( isExists ) { doCommit = ((rc == KeeperException.Code.OK.intValue()) || (rc == KeeperException.Code.NONODE.intValue())); } else { doCommit = (rc == KeeperException.Code.OK.intValue()); } if ( doCommit && (namespaceWatcher != null) ) { if ( client.getWatcherRemovalManager() != null ) { client.getWatcherRemovalManager().add(namespaceWatcher); } } } } curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/listen/000077500000000000000000000000001442004423600331635ustar00rootroot00000000000000Listenable.java000066400000000000000000000031001442004423600360230ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/listen/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.listen; import java.util.concurrent.Executor; /** * Abstracts a listenable object */ public interface Listenable { /** * Add the given listener. The listener will be executed in the containing * instance's thread. * * @param listener listener to add */ public void addListener(T listener); /** * Add the given listener. The listener will be executed using the given * executor * * @param listener listener to add * @param executor executor to run listener in */ public void addListener(T listener, Executor executor); /** * Remove the given listener * * @param listener listener to remove */ public void removeListener(T listener); } ListenerEntry.java000066400000000000000000000023201442004423600365530ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/listen/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.listen; import java.util.concurrent.Executor; /** * Generic holder POJO for a listener and its executor * @param the listener type */ public class ListenerEntry { public final T listener; public final Executor executor; public ListenerEntry(T listener, Executor executor) { this.listener = listener; this.executor = executor; } } ListenerManager.java000066400000000000000000000026261442004423600370350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/listen/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.listen; import java.util.function.Consumer; public interface ListenerManager extends Listenable { /** * Remove all listeners */ void clear(); /** * Return the number of listeners * * @return number */ int size(); /** * Utility - apply the given function to each listener. The function receives * the listener as an argument. * * @param function function to call for each listener */ void forEach(Consumer function); default boolean isEmpty() { return size() == 0; } } MappingListenerManager.java000066400000000000000000000060131442004423600403430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/listen/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.listen; import org.apache.curator.utils.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Function; /** * Version of ListenerManager that supports mapping/wrapping of listeners */ public class MappingListenerManager implements ListenerManager { private final Logger log = LoggerFactory.getLogger(getClass()); private final Map> listeners = new ConcurrentHashMap<>(); private final Function mapper; /** * Returns a new container that wraps listeners using the given mapper * * @param mapper listener mapper/wrapper * @return new container */ public static ListenerManager mapping(Function mapper) { return new MappingListenerManager<>(mapper); } @Override public void addListener(K listener) { addListener(listener, Runnable::run); } @Override public void addListener(K listener, Executor executor) { V mapped = mapper.apply(listener); listeners.put(listener, new ListenerEntry<>(mapped, executor)); } @Override public void removeListener(K listener) { if ( listener != null ) { listeners.remove(listener); } } @Override public void clear() { listeners.clear(); } @Override public int size() { return listeners.size(); } @Override public void forEach(Consumer function) { for ( ListenerEntry entry : listeners.values() ) { entry.executor.execute(() -> { try { function.accept(entry.listener); } catch ( Throwable e ) { ThreadUtils.checkInterrupted(e); log.error(String.format("Listener (%s) threw an exception", entry.listener), e); } }); } } MappingListenerManager(Function mapper) { this.mapper = mapper; } } StandardListenerManager.java000066400000000000000000000051701442004423600405130ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/listen/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.listen; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.UnaryOperator; /** * Non mapping version of a listener container */ public class StandardListenerManager implements UnaryListenerManager { private final ListenerManager container; /** * Returns a new standard listener container * * @return new container */ public static StandardListenerManager standard() { MappingListenerManager container = new MappingListenerManager<>(Function.identity()); return new StandardListenerManager<>(container); } /** * Returns a new mapping container that maps to the same type * * @param mapper listener mapper/wrapper * @return new container */ public static StandardListenerManager mappingStandard(UnaryOperator mapper) { MappingListenerManager container = new MappingListenerManager<>(mapper); return new StandardListenerManager<>(container); } @Override public void addListener(T listener) { container.addListener(listener); } @Override public void addListener(T listener, Executor executor) { container.addListener(listener, executor); } @Override public void removeListener(T listener) { container.removeListener(listener); } @Override public void clear() { container.clear(); } @Override public int size() { return container.size(); } @Override public void forEach(Consumer function) { container.forEach(function); } private StandardListenerManager(ListenerManager container) { this.container = container; } } UnaryListenerManager.java000066400000000000000000000017431442004423600400530ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/listen/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.listen; /** * A {@link ListenerManager} that doesn't do any mapping */ public interface UnaryListenerManager extends ListenerManager { } curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/schema/000077500000000000000000000000001442004423600331255ustar00rootroot00000000000000DefaultSchemaValidator.java000066400000000000000000000022261442004423600402660ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/schema/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.schema; import org.apache.zookeeper.data.ACL; import java.util.List; /** * The default data validator - always returns true */ public class DefaultSchemaValidator implements SchemaValidator { @Override public boolean isValid(Schema schema, String path, byte[] data, List acl) { return true; } } Schema.java000066400000000000000000000246461442004423600351250ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/schema/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.schema; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.ACL; import java.util.List; import java.util.Map; import java.util.regex.Pattern; /** * Represents and documents operations allowed for a given path pattern */ public class Schema { private final String name; private final Pattern pathRegex; private final String fixedPath; private final String documentation; private final SchemaValidator schemaValidator; private final Allowance ephemeral; private final Allowance sequential; private final Allowance watched; private final boolean canBeDeleted; private final Map metadata; public enum Allowance { CAN, MUST, CANNOT } /** * Start a builder for the given full path. Note: full path schemas * take precedence over regex path schemas. * * @param path full ZNode path. This schema only applies to an exact match * @return builder */ public static SchemaBuilder builder(String path) { return new SchemaBuilder(null, path); } /** * Start a builder for the given path pattern. * * @param pathRegex regex for the path. This schema applies to any matching paths * @return builder */ public static SchemaBuilder builder(Pattern pathRegex) { return new SchemaBuilder(pathRegex, null); } /** * Start a schema builder for a typical Curator recipe's parent node * * @param parentPath Path to the parent node * @return builder */ public static SchemaBuilder builderForRecipeParent(String parentPath) { return new SchemaBuilder(null, parentPath) .sequential(Allowance.CANNOT) .ephemeral(Allowance.CANNOT) ; } /** * Start a schema builder for a typical Curator recipe's children * * @param parentPath Path to the parent node * @return builder */ public static SchemaBuilder builderForRecipe(String parentPath) { return new SchemaBuilder(Pattern.compile(ZKPaths.makePath(parentPath, ".*")), null) .sequential(Allowance.MUST) .ephemeral(Allowance.MUST) .watched(Allowance.MUST) .canBeDeleted(true) ; } Schema(String name, Pattern pathRegex, String path, String documentation, SchemaValidator schemaValidator, Allowance ephemeral, Allowance sequential, Allowance watched, boolean canBeDeleted, Map metadata) { Preconditions.checkArgument((pathRegex != null) || (path != null), "pathRegex and path cannot both be null"); this.pathRegex = pathRegex; this.fixedPath = fixPath(path); this.metadata = ImmutableMap.copyOf(Preconditions.checkNotNull(metadata, "metadata cannot be null")); this.name = Preconditions.checkNotNull(name, "name cannot be null"); this.documentation = Preconditions.checkNotNull(documentation, "documentation cannot be null"); this.schemaValidator = Preconditions.checkNotNull(schemaValidator, "dataValidator cannot be null"); this.ephemeral = Preconditions.checkNotNull(ephemeral, "ephemeral cannot be null"); this.sequential = Preconditions.checkNotNull(sequential, "sequential cannot be null"); this.watched = Preconditions.checkNotNull(watched, "watched cannot be null"); this.canBeDeleted = canBeDeleted; } private String fixPath(String path) { if ( path != null ) { if ( path.endsWith(ZKPaths.PATH_SEPARATOR) ) { return (path.length() > 1) ? path.substring(0, path.length() - 1) : ""; } return path; } return null; } /** * Validate that this schema allows znode deletion * * @param path the znode full path * @throws SchemaViolation if schema does not allow znode deletion */ public void validateDelete(String path) { if ( !canBeDeleted ) { throw new SchemaViolation(this, new SchemaViolation.ViolatorData(path, null, null), "Cannot be deleted"); } } /** * Validate that this schema's watching setting matches * * @param path the znode full path * @param isWatching true if attempt is being made to watch node * @throws SchemaViolation if schema's watching setting does not match */ public void validateWatch(String path, boolean isWatching) { if ( isWatching && (watched == Allowance.CANNOT) ) { throw new SchemaViolation(this, new SchemaViolation.ViolatorData(path, null, null), "Cannot be watched"); } if ( !isWatching && (watched == Allowance.MUST) ) { throw new SchemaViolation(this, new SchemaViolation.ViolatorData(path, null, null), "Must be watched"); } } /** * Validate that this schema's create mode setting matches and that the data is valid * * @param mode CreateMode being used * @param path the znode full path * @param data data being set * @param acl the creation acls * @throws SchemaViolation if schema's create mode setting does not match or data is invalid */ public void validateCreate(CreateMode mode, String path, byte[] data, List acl) { if ( mode.isEphemeral() && (ephemeral == Allowance.CANNOT) ) { throw new SchemaViolation(this, new SchemaViolation.ViolatorData(path, data, acl), "Cannot be ephemeral"); } if ( !mode.isEphemeral() && (ephemeral == Allowance.MUST) ) { throw new SchemaViolation(this, new SchemaViolation.ViolatorData(path, data, acl), "Must be ephemeral"); } if ( mode.isSequential() && (sequential == Allowance.CANNOT) ) { throw new SchemaViolation(this, new SchemaViolation.ViolatorData(path, data, acl), "Cannot be sequential"); } if ( !mode.isSequential() && (sequential == Allowance.MUST) ) { throw new SchemaViolation(this, new SchemaViolation.ViolatorData(path, data, acl), "Must be sequential"); } validateGeneral(path, data, acl); } /** * Validate that this schema validates the data * * * @param path the znode full path * @param data data being set * @param acl if creating, the acls otherwise null or empty list * @throws SchemaViolation if data is invalid */ public void validateGeneral(String path, byte[] data, List acl) { if ( !schemaValidator.isValid(this, path, data, acl) ) { throw new SchemaViolation(this, new SchemaViolation.ViolatorData(path, data, acl), "Data is not valid"); } } public String getName() { return name; } /** * Return the raw path for this schema. If a full path was used, it is returned. * If a regex was used, it is returned * * @return path */ public String getRawPath() { return (fixedPath != null) ? fixedPath : pathRegex.pattern(); } public Map getMetadata() { return metadata; } public Pattern getPathRegex() { return pathRegex; } public String getPath() { return fixedPath; } public String getDocumentation() { return documentation; } public SchemaValidator getSchemaValidator() { return schemaValidator; } public Allowance getEphemeral() { return ephemeral; } public Allowance getSequential() { return sequential; } public Allowance getWatched() { return watched; } public boolean canBeDeleted() { return canBeDeleted; } // intentionally only path and pathRegex @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } Schema schema = (Schema)o; //noinspection SimplifiableIfStatement if ( !pathRegex.equals(schema.pathRegex) ) { return false; } return fixedPath.equals(schema.fixedPath); } // intentionally only path and pathRegex @Override public int hashCode() { int result = pathRegex.hashCode(); result = 31 * result + fixedPath.hashCode(); return result; } @Override public String toString() { return "Schema{" + "name='" + name + '\'' + ", pathRegex=" + pathRegex + ", path='" + fixedPath + '\'' + ", documentation='" + documentation + '\'' + ", dataValidator=" + schemaValidator.getClass() + ", ephemeral=" + ephemeral + ", sequential=" + sequential + ", watched=" + watched + ", canBeDeleted=" + canBeDeleted + ", metadata=" + metadata + '}'; } public String toDocumentation() { String pathLabel = (pathRegex != null) ? "Path Regex: " : "Path: "; return "Name: " + name + '\n' + pathLabel + getRawPath() + '\n' + "Doc: " + documentation + '\n' + "Validator: " + schemaValidator.getClass().getSimpleName() + '\n' + "Meta: " + metadata + '\n' + String.format("ephemeral: %s | sequential: %s | watched: %s | canBeDeleted: %s", ephemeral, sequential, watched, canBeDeleted) + '\n' ; } } SchemaBuilder.java000066400000000000000000000102531442004423600364210ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/schema/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.schema; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import java.util.Map; import java.util.UUID; import java.util.regex.Pattern; public class SchemaBuilder { private final Pattern pathRegex; private final String path; private String name = UUID.randomUUID().toString(); private String documentation = ""; private SchemaValidator schemaValidator = new DefaultSchemaValidator(); private Schema.Allowance ephemeral = Schema.Allowance.CAN; private Schema.Allowance sequential = Schema.Allowance.CAN; private Schema.Allowance watched = Schema.Allowance.CAN; private boolean canBeDeleted = true; private Map metadata = ImmutableMap.of(); /** * Build a new schema from the currently set values * * @return new schema */ public Schema build() { return new Schema(name, pathRegex, path, documentation, schemaValidator, ephemeral, sequential, watched, canBeDeleted, metadata); } /** * @param name unique name for this schema * @return this for chaining */ public SchemaBuilder name(String name) { this.name = Preconditions.checkNotNull(name, "name cannot be null"); return this; } /** * @param documentation user displayable documentation for the schema * @return this for chaining */ public SchemaBuilder documentation(String documentation) { this.documentation = Preconditions.checkNotNull(documentation, "documentation cannot be null"); return this; } /** * @param schemaValidator a data validator - will be used to validate data set for the znode * @return this for chaining */ public SchemaBuilder dataValidator(SchemaValidator schemaValidator) { this.schemaValidator = Preconditions.checkNotNull(schemaValidator, "dataValidator cannot be null"); return this; } /** * @param ephemeral whether can, must or cannot be ephemeral * @return this for chaining */ public SchemaBuilder ephemeral(Schema.Allowance ephemeral) { this.ephemeral = Preconditions.checkNotNull(ephemeral, "ephemeral cannot be null"); return this; } /** * @param sequential whether can, must or cannot be sequential * @return this for chaining */ public SchemaBuilder sequential(Schema.Allowance sequential) { this.sequential = Preconditions.checkNotNull(sequential, "sequential cannot be null"); return this; } /** * @param watched whether can, must or cannot be watched * @return this for chaining */ public SchemaBuilder watched(Schema.Allowance watched) { this.watched = watched; return this; } /** * @param canBeDeleted true if znode can be deleted * @return this for chaining */ public SchemaBuilder canBeDeleted(boolean canBeDeleted) { this.canBeDeleted = canBeDeleted; return this; } /** * @param metadata any field -> value you want * @return this for chaining */ public SchemaBuilder metadata(Map metadata) { this.metadata = ImmutableMap.copyOf(metadata); return this; } SchemaBuilder(Pattern pathRegex, String path) { this.pathRegex = pathRegex; this.path = path; } } SchemaSet.java000066400000000000000000000156641442004423600356010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/schema/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.schema; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import org.apache.curator.framework.CuratorFramework; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; /** * Collection of all schemas for a Curator instance */ public class SchemaSet { private final Logger log = LoggerFactory.getLogger(getClass()); private final Map schemas; private final Map pathToSchemas; private final List regexSchemas; private final CacheLoader cacheLoader = new CacheLoader() { @Override public Schema load(String path) throws Exception { for ( Schema schema : regexSchemas ) { if ( schema.getPathRegex().matcher(path).matches() ) { log.debug("path -> {}", schema); return schema; } } return nullSchema; } }; private final LoadingCache regexCache = CacheBuilder .newBuilder() .softValues() .build(cacheLoader); private static final Schema nullSchema = new Schema("__null__", null, "", "Null schema", new DefaultSchemaValidator(), Schema.Allowance.CAN, Schema.Allowance.CAN, Schema.Allowance.CAN, true, ImmutableMap.of()); private static final Schema defaultSchema = new Schema("__default__", null, "", "Default schema", new DefaultSchemaValidator(), Schema.Allowance.CAN, Schema.Allowance.CAN, Schema.Allowance.CAN, true, ImmutableMap.of()); private final boolean useDefaultSchema; /** * Return the default (empty) schema set * * @return default schema set */ public static SchemaSet getDefaultSchemaSet() { return new SchemaSet(Collections.emptyList(), true) { @Override public String toDocumentation() { return "Default schema"; } }; } /** * Define a schema set. Schemas are matched in a well defined order: *
    *
  1. Exact match on full path (i.e. non-regex)
  2. *
  3. Match on the first regex path, searched in the order given to this constructor
  4. *
* * @param schemas the schemas for the set. * @param useDefaultSchema if true, return a default schema when there is no match. Otherwise, an exception is thrown */ public SchemaSet(List schemas, boolean useDefaultSchema) { schemas = Preconditions.checkNotNull(schemas, "schemas cannot be null"); this.useDefaultSchema = useDefaultSchema; this.schemas = Maps.uniqueIndex(schemas, new Function() { @Override public String apply(Schema schema) { return schema.getName(); } }); ImmutableMap.Builder pathBuilder = ImmutableMap.builder(); ImmutableList.Builder regexBuilder = ImmutableList.builder(); for ( Schema schema : schemas ) { if ( schema.getPath() != null ) { pathBuilder.put(schema.getPath(), schema); } else { regexBuilder.add(schema); } } pathToSchemas = pathBuilder.build(); regexSchemas = regexBuilder.build(); } /** * Return the schemas * * @return schemas */ public Collection getSchemas() { return schemas.values(); } /** * Find the first matching schema for the path and return it * * @param path ZNode full path * @return matching schema or a default schema */ public Schema getSchema(String path) { if ( schemas.size() > 0 ) { Schema schema = pathToSchemas.get(path); if ( schema == null ) { try { schema = regexCache.get(path); if ( schema.equals(nullSchema) ) { schema = useDefaultSchema ? defaultSchema : null; } } catch ( ExecutionException e ) { throw new RuntimeException(e); } } if ( schema != null ) { return schema; } } if ( useDefaultSchema ) { return defaultSchema; } throw new SchemaViolation(null, new SchemaViolation.ViolatorData(path, null, null), "No schema found for: " + path); } /** * Utility to return a ZNode path for the given name * * @param client Curator client * @param name path/schema name * @return ZNode path */ public static String getNamedPath(CuratorFramework client, String name) { return client.getSchemaSet().getNamedSchema(name).getRawPath(); } /** * Return the schema with the given key/name * * @param name name * @return schema or null */ public Schema getNamedSchema(String name) { return schemas.get(name); } /** * Build a user displayable documentation string for the schemas in this set * * @return documentation */ public String toDocumentation() { StringBuilder str = new StringBuilder("Curator Schemas:\n\n"); for ( Map.Entry schemaEntry : schemas.entrySet() ) { str.append(schemaEntry.getKey()).append('\n').append(schemaEntry.getValue().toDocumentation()).append('\n'); } return str.toString(); } } SchemaSetLoader.java000066400000000000000000000175651442004423600367320ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/schema/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.schema; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Pattern; /** *

* Utility to load schems set from a JSON stream/file. NOTE: * to avoid adding a new dependency to Curator, the Jackson library has been used * with "provided" scope. You will need to add a dependency to com.fasterxml.jackson.core:jackson-core:2.7.3 * and com.fasterxml.jackson.core:jackson-databind:2.7.3 to your project *

* *

* The JSON stream should be an array of named schemas:

*

 * [
 *     {
 *         "name": "name",                       required - name of the schema
 *         "path": "path or pattern",            required - full path or regex pattern
 *         "isRegex": true/false,                optional - true if path is a regular expression - default is false
 *         "schemaValidator": "name",            optional - name of a schema validator - default is no validator
 *         "documentation": "docs",              optional - user displayable docs - default is ""
 *         "ephemeral": "allowance",             optional - "can", "must" or "cannot" - default is "can"
 *         "sequential": "allowance",            optional - "can", "must" or "cannot" - default is "can"
 *         "watched": "allowance",               optional - "can", "must" or "cannot" - default is "can"
 *         "canBeDeleted": true/false            optional - true if ZNode at path can be deleted - default is true
 *         "metadata": {                         optional - any fields -> values that you want
 *             "field1": "value1",
 *             "field2": "value2"
 *         }
 *     }
 * ]
 * 
*

*/ public class SchemaSetLoader { private final List schemas; /** * Called to map a schema validator name in the JSON stream to an actual data validator */ public interface SchemaValidatorMapper { /** * @param name name of the validator * @return the validator */ SchemaValidator getSchemaValidator(String name); } /** * @param json the json to parse * @param schemaValidatorMapper mapper from validator name to instance - can be null if not needed */ public SchemaSetLoader(String json, SchemaValidatorMapper schemaValidatorMapper) { this(getRoot(new StringReader(json)), schemaValidatorMapper); } /** * @param jsonStream the json stream to parse * @param schemaValidatorMapper mapper from validator name to instance - can be null if not needed */ public SchemaSetLoader(Reader jsonStream, SchemaValidatorMapper schemaValidatorMapper) { this(getRoot(jsonStream), schemaValidatorMapper); } /** * @param root a Jackson root node * @param schemaValidatorMapper mapper from validator name to instance - can be null if not needed */ public SchemaSetLoader(JsonNode root, SchemaValidatorMapper schemaValidatorMapper) { ImmutableList.Builder builder = ImmutableList.builder(); read(builder, root, schemaValidatorMapper); schemas = builder.build(); } /** * @param useDefaultSchema if true, return a default schema when there is no match. Otherwise, an exception is thrown * @return schema set */ public SchemaSet toSchemaSet(boolean useDefaultSchema) { return new SchemaSet(schemas, useDefaultSchema); } public List getSchemas() { return schemas; } private static JsonNode getRoot(Reader in) { try { return new ObjectMapper().readTree(in); } catch ( IOException e ) { throw new RuntimeException(e); } } private void read(ImmutableList.Builder builder, JsonNode node, SchemaValidatorMapper schemaValidatorMapper) { for ( JsonNode child : node ) { readNode(builder, child, schemaValidatorMapper); } } private void readNode(ImmutableList.Builder builder, JsonNode node, SchemaValidatorMapper schemaValidatorMapper) { String name = getText(node, "name", null); String path = getText(node, "path", null); boolean isRegex = getBoolean(node, "isRegex"); if ( name == null ) { throw new RuntimeException("name is required at: " + node); } if ( path == null ) { throw new RuntimeException("path is required at: " + node); } SchemaBuilder schemaBuilder = isRegex ? Schema.builder(Pattern.compile(path)) : Schema.builder(path); String schemaValidatorName = getText(node, "schemaValidator", null); if ( schemaValidatorName != null ) { if ( schemaValidatorMapper == null ) { throw new RuntimeException("No SchemaValidatorMapper provided but needed at: " + node); } schemaBuilder.dataValidator(schemaValidatorMapper.getSchemaValidator(schemaValidatorName)); } Map metadata = Maps.newHashMap(); if ( node.has("metadata") ) { JsonNode metadataNode = node.get("metadata"); Iterator fieldNameIterator = metadataNode.fieldNames(); while ( fieldNameIterator.hasNext() ) { String fieldName = fieldNameIterator.next(); metadata.put(fieldName, getText(metadataNode, fieldName, "")); } } Schema schema = schemaBuilder.name(name) .documentation(getText(node, "documentation", "")) .ephemeral(getAllowance(node, "ephemeral")) .sequential(getAllowance(node, "sequential")) .watched(getAllowance(node, "watched")) .canBeDeleted(getBoolean(node, "canBeDeleted")) .metadata(metadata) .build(); builder.add(schema); } private String getText(JsonNode node, String name, String defaultValue) { JsonNode namedNode = node.get(name); return (namedNode != null) ? namedNode.asText() : defaultValue; } private boolean getBoolean(JsonNode node, String name) { JsonNode namedNode = node.get(name); return (namedNode != null) && namedNode.asBoolean(); } private Schema.Allowance getAllowance(JsonNode node, String name) { JsonNode namedNode = node.get(name); try { return (namedNode != null) ? Schema.Allowance.valueOf(namedNode.asText().toUpperCase()) : Schema.Allowance.CAN; } catch ( IllegalArgumentException ignore ) { throw new RuntimeException("Must be one of: " + Arrays.toString(Schema.Allowance.values()) + " at " + node); } } } SchemaValidator.java000066400000000000000000000023211442004423600367550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/schema/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.schema; import org.apache.zookeeper.data.ACL; import java.util.List; public interface SchemaValidator { /** * @param schema the schema being validated * @param path the path being operated on * @param data data or null * @param acl acls or null * @return true if valid */ boolean isValid(Schema schema, String path, byte[] data, List acl); } SchemaViolation.java000066400000000000000000000105271442004423600370030ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/schema/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.schema; import com.google.common.collect.ImmutableList; import org.apache.zookeeper.data.ACL; import java.util.Arrays; import java.util.List; /** * Thrown by the various validation methods in a Schema */ public class SchemaViolation extends RuntimeException { private final Schema schema; private final String violation; private final ViolatorData violatorData; /** * Data about the calling API that violated the schema */ public static class ViolatorData { private final String path; private final byte[] data; private final List acl; public ViolatorData(String path, byte[] data, List acl) { this.path = path; this.data = (data != null) ? Arrays.copyOf(data, data.length) : null; this.acl = (acl != null) ? ImmutableList.copyOf(acl) : null; } /** * The path used in the API or null * * @return path or null */ public String getPath() { return path; } /** * The data used in the API or null * * @return data or null */ public byte[] getData() { return data; } /** * The ACLs used in the API or null * * @return ACLs or null */ public List getAcl() { return acl; } @Override public String toString() { String dataString = (data != null) ? new String(data) : ""; return "ViolatorData{" + "path='" + path + '\'' + ", data=" + dataString + ", acl=" + acl + '}'; } } /** * @param violation the violation * @deprecated use {@link #SchemaViolation(Schema, ViolatorData, String)} instance */ public SchemaViolation(String violation) { super(String.format("Schema violation: %s", violation)); this.schema = null; this.violation = violation; this.violatorData = new ViolatorData(null, null, null); } /** * @param schema the schema * @param violation the violation * @deprecated use {@link #SchemaViolation(Schema, ViolatorData, String)} instance */ public SchemaViolation(Schema schema, String violation) { super(String.format("Schema violation: %s for schema: %s", violation, schema)); this.schema = schema; this.violation = violation; this.violatorData = new ViolatorData(null, null, null); } /** * @param schema the schema * @param violatorData data about the caller who violated the schema * @param violation the violation */ public SchemaViolation(Schema schema, ViolatorData violatorData, String violation) { super(toString(schema, violation, violatorData)); this.schema = schema; this.violation = violation; this.violatorData = violatorData; } public Schema getSchema() { return schema; } public String getViolation() { return violation; } public ViolatorData getViolatorData() { return violatorData; } @Override public String toString() { return toString(schema, violation, violatorData) + super.toString(); } private static String toString(Schema schema, String violation, ViolatorData violatorData) { return (violation != null ? violation : "") + " " + schema + " " + violatorData; } } curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/000077500000000000000000000000001442004423600330055ustar00rootroot00000000000000CircuitBreaker.java000066400000000000000000000063331442004423600364740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import org.apache.curator.RetryPolicy; import org.apache.curator.RetrySleeper; import org.apache.curator.utils.ThreadUtils; import java.time.Duration; import java.util.Objects; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; class CircuitBreaker { private final RetryPolicy retryPolicy; private final ScheduledExecutorService service; private boolean isOpen = false; private int retryCount = 0; private long startNanos = 0; static CircuitBreaker build(RetryPolicy retryPolicy) { return new CircuitBreaker(retryPolicy, ThreadUtils.newSingleThreadScheduledExecutor("CircuitBreakingConnectionStateListener")); } static CircuitBreaker build(RetryPolicy retryPolicy, ScheduledExecutorService service) { return new CircuitBreaker(retryPolicy, service); } // IMPORTANT - all methods below MUST be guarded by synchronization boolean isOpen() { return isOpen; } int getRetryCount() { return retryCount; } boolean tryToOpen(Runnable completion) { if ( isOpen ) { return false; } isOpen = true; retryCount = 0; startNanos = System.nanoTime(); if ( tryToRetry(completion) ) { return true; } close(); return false; } boolean tryToRetry(Runnable completion) { if ( !isOpen ) { return false; } long[] sleepTimeNanos = new long[]{0L}; RetrySleeper retrySleeper = (time, unit) -> sleepTimeNanos[0] = unit.toNanos(time); Duration elapsedTime = Duration.ofNanos(System.nanoTime() - startNanos); if ( retryPolicy.allowRetry(retryCount, elapsedTime.toMillis(), retrySleeper) ) { ++retryCount; service.schedule(completion, sleepTimeNanos[0], TimeUnit.NANOSECONDS); return true; } return false; } boolean close() { boolean wasOpen = isOpen; retryCount = 0; isOpen = false; startNanos = 0; return wasOpen; } private CircuitBreaker(RetryPolicy retryPolicy, ScheduledExecutorService service) { this.retryPolicy = Objects.requireNonNull(retryPolicy, "retryPolicy cannot be null"); this.service = Objects.requireNonNull(service, "service cannot be null"); } } CircuitBreakingConnectionStateListener.java000066400000000000000000000213021442004423600433630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Objects; import java.util.concurrent.ScheduledExecutorService; /** *

* A proxy for connection state listeners that adds circuit breaking behavior. During network * outages ZooKeeper can become very noisy sending connection/disconnection events in rapid succession. * Curator recipes respond to these messages by resetting state, etc. E.g. LeaderLatch must delete * its lock node and try to recreate it in order to try to re-obtain leadership, etc. *

* *

* This noisy herding can be avoided by using the circuit breaking listener. When it * receives {@link org.apache.curator.framework.state.ConnectionState#SUSPENDED}, the circuit * becomes "open" (based on the provided {@link org.apache.curator.RetryPolicy}) and will ignore * future connection state changes until RetryPolicy timeout has elapsed. Note: however, if the connection * goes from {@link org.apache.curator.framework.state.ConnectionState#SUSPENDED} to * {@link org.apache.curator.framework.state.ConnectionState#LOST} the first LOST state is sent. *

* *

* When the circuit is closed, all connection state changes are forwarded to the managed * listener. When the first disconnected state is received, the circuit becomes open. The state change * that caused the circuit to open is sent to the managed listener and the RetryPolicy will be used to * get a delay amount. While the delay is active, the circuit breaker will store state changes but will not * forward them to the managed listener (except, however, the first time the state changes from SUSPENDED to LOST). * When the delay elapses, if the connection has been restored, the circuit closes and forwards the * new state to the managed listener. If the connection has not been restored, the RetryPolicy is checked * again. If the RetryPolicy indicates another retry is allowed the process repeats. If, however, the * RetryPolicy indicates that retries are exhausted then the circuit closes - if the current state * is different than the state that caused the circuit to open it is forwarded to the managed listener. *

* *

* NOTE: You should not use this listener directly. Instead, set {@link org.apache.curator.framework.state.ConnectionStateListenerManagerFactory#circuitBreaking(org.apache.curator.RetryPolicy)} * in the {@link org.apache.curator.framework.CuratorFrameworkFactory.Builder#connectionStateListenerManagerFactory(ConnectionStateListenerManagerFactory)}. *

* *

* E.g. *

 * ConnectionStateListenerManagerFactory factory = ConnectionStateListenerManagerFactory.circuitBreaking(...retry policy for circuit breaking...);
 * CuratorFramework client = CuratorFrameworkFactory.builder()
 *     .connectionStateListenerManagerFactory(factory)
 *     ... etc ...
 *     .build();
 * // all connection state listeners set for "client" will get circuit breaking behavior
 * 
*

*/ public class CircuitBreakingConnectionStateListener implements ConnectionStateListener { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFramework client; private final ConnectionStateListener listener; private final CircuitBreaker circuitBreaker; // guarded by sync private boolean circuitLostHasBeenSent; // guarded by sync private ConnectionState circuitLastState; // guarded by sync private ConnectionState circuitInitialState; /** * @param client Curator instance * @param listener listener to manage * @param retryPolicy breaking policy to use */ public CircuitBreakingConnectionStateListener(CuratorFramework client, ConnectionStateListener listener, RetryPolicy retryPolicy) { this(client, listener, CircuitBreaker.build(retryPolicy)); } /** * @param client Curator instance * @param listener listener to manage * @param retryPolicy breaking policy to use * @param service scheduler to use */ public CircuitBreakingConnectionStateListener(CuratorFramework client, ConnectionStateListener listener, RetryPolicy retryPolicy, ScheduledExecutorService service) { this(client, listener, CircuitBreaker.build(retryPolicy, service)); } CircuitBreakingConnectionStateListener(CuratorFramework client, ConnectionStateListener listener, CircuitBreaker circuitBreaker) { this.client = Objects.requireNonNull(client, "client cannot be null"); this.listener = Objects.requireNonNull(listener, "listener cannot be null"); this.circuitBreaker = Objects.requireNonNull(circuitBreaker, "circuitBreaker cannot be null"); reset(); } @Override public synchronized void stateChanged(CuratorFramework client, ConnectionState newState) { if ( circuitBreaker.isOpen() ) { handleOpenStateChange(newState); } else { handleClosedStateChange(newState); } } /** * Returns true if the circuit is open * * @return true/false */ public synchronized boolean isOpen() { return circuitBreaker.isOpen(); } private synchronized void handleClosedStateChange(ConnectionState newState) { if ( !newState.isConnected() ) { if ( circuitBreaker.tryToOpen(this::checkCloseCircuit) ) { log.info("Circuit is opening. State: {} post-retryCount: {}", newState, circuitBreaker.getRetryCount()); circuitLastState = circuitInitialState = newState; circuitLostHasBeenSent = (newState == ConnectionState.LOST); } else { log.debug("Could not open circuit breaker. State: {}", newState); } } callListener(newState); } private synchronized void handleOpenStateChange(ConnectionState newState) { if ( circuitLostHasBeenSent || (newState != ConnectionState.LOST) ) { log.debug("Circuit is open. Ignoring state change: {}", newState); circuitLastState = newState; } else { log.debug("Circuit is open. State changed to LOST. Sending to listener."); circuitLostHasBeenSent = true; circuitLastState = circuitInitialState = ConnectionState.LOST; callListener(ConnectionState.LOST); } } private synchronized void checkCloseCircuit() { if ( (circuitLastState == null) || circuitLastState.isConnected() ) { log.info("Circuit is closing. Initial state: {} - Last state: {}", circuitInitialState, circuitLastState); closeCircuit(); } else if ( circuitBreaker.tryToRetry(this::checkCloseCircuit) ) { log.debug("Circuit open is continuing due to retry. State: {} post-retryCount: {}", circuitLastState, circuitBreaker.getRetryCount()); } else { log.info("Circuit is closing due to retries exhausted. Initial state: {} - Last state: {}", circuitInitialState, circuitLastState); closeCircuit(); } } private synchronized void callListener(ConnectionState newState) { if ( newState != null ) { listener.stateChanged(client, newState); } } private synchronized void closeCircuit() { ConnectionState stateToSend = (circuitLastState == circuitInitialState) ? null : circuitLastState; reset(); callListener(stateToSend); } private synchronized void reset() { circuitLastState = null; circuitInitialState = null; circuitLostHasBeenSent = false; circuitBreaker.close(); } } CircuitBreakingManager.java000066400000000000000000000060271442004423600401360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.listen.UnaryListenerManager; import java.util.concurrent.Executor; import java.util.function.Consumer; class CircuitBreakingManager implements UnaryListenerManager { private final StandardListenerManager mainContainer = StandardListenerManager.standard(); private final StandardListenerManager doNotProxyContainer = StandardListenerManager.standard(); private final CircuitBreakingConnectionStateListener masterListener; CircuitBreakingManager(CuratorFramework client, CircuitBreaker circuitBreaker) { ConnectionStateListener masterStateChanged = (__, newState) -> mainContainer.forEach(listener -> listener.stateChanged(client, newState)); masterListener = new CircuitBreakingConnectionStateListener(client, masterStateChanged, circuitBreaker); } @Override public void clear() { doNotProxyContainer.clear(); mainContainer.clear(); } @Override public int size() { return mainContainer.size() + doNotProxyContainer.size(); } @Override public void forEach(Consumer function) { doNotProxyContainer.forEach(function); function.accept(masterListener); } @Override public void addListener(ConnectionStateListener listener) { if ( listener.doNotProxy() ) { doNotProxyContainer.addListener(listener); } else { mainContainer.addListener(listener); } } @Override public void addListener(ConnectionStateListener listener, Executor executor) { if ( listener.doNotProxy() ) { doNotProxyContainer.addListener(listener, executor); } else { mainContainer.addListener(listener, executor); } } @Override public void removeListener(ConnectionStateListener listener) { mainContainer.removeListener(listener); doNotProxyContainer.removeListener(listener); } } ConnectionState.java000066400000000000000000000065211442004423600366750ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; /** * Represents state changes in the connection to ZK */ public enum ConnectionState { /** * Sent for the first successful connection to the server. NOTE: You will only * get one of these messages for any CuratorFramework instance. */ CONNECTED { public boolean isConnected() { return true; } }, /** * There has been a loss of connection. Leaders, locks, etc. should suspend * until the connection is re-established. */ SUSPENDED { public boolean isConnected() { return false; } }, /** * A suspended, lost, or read-only connection has been re-established */ RECONNECTED { public boolean isConnected() { return true; } }, /** *

* Curator will set the LOST state when it believes that the ZooKeeper session * has expired. ZooKeeper connections have a session. When the session expires, clients must take appropriate * action. In Curator, this is complicated by the fact that Curator internally manages the ZooKeeper * connection. Curator will set the LOST state when any of the following occurs: * a) ZooKeeper returns a {@link Watcher.Event.KeeperState#Expired} or {@link KeeperException.Code#SESSIONEXPIRED}; * b) Curator closes the internally managed ZooKeeper instance; c) The session timeout * elapses during a network partition. *

*/ LOST { public boolean isConnected() { return false; } }, /** * The connection has gone into read-only mode. This can only happen if you pass true * for {@link CuratorFrameworkFactory.Builder#canBeReadOnly()}. See the ZooKeeper doc * regarding read only connections: * http://wiki.apache.org/hadoop/ZooKeeper/GSoCReadOnlyMode. * The connection will remain in read only mode until another state change is sent. */ READ_ONLY { public boolean isConnected() { return true; } } ; /** * Check if this state indicates that Curator has a connection to ZooKeeper * * @return True if connected, false otherwise */ public abstract boolean isConnected(); } ConnectionStateErrorPolicy.java000066400000000000000000000024671442004423600410740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; /** * Recipes should use the configured error policy to decide how to handle * errors such as {@link ConnectionState} changes. * * @since 3.0.0 */ public interface ConnectionStateErrorPolicy { /** * Returns true if the given state should cause the recipe to * act as though the connection has been lost. i.e. locks should * exit, etc. * * @param state the state * @return true/false */ boolean isErrorState(ConnectionState state); } ConnectionStateListener.java000066400000000000000000000033161442004423600404020ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import org.apache.curator.framework.CuratorFramework; public interface ConnectionStateListener { /** * Called when there is a state change in the connection * * @param client the client * @param newState the new state */ void stateChanged(CuratorFramework client, ConnectionState newState); /** * ConnectionStateListener managers set via {@link org.apache.curator.framework.CuratorFrameworkFactory.Builder#connectionStateListenerManagerFactory(ConnectionStateListenerManagerFactory)} * are allowed to proxy (etc.) ConnectionStateListeners as needed. If this method returns true * the ConnectionStateListener manager must not proxy the listener as it's a vital internal * listener used by Curator. * * @return true/false */ default boolean doNotProxy() { return false; } } ConnectionStateListenerManagerFactory.java000066400000000000000000000056231442004423600432300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.listen.UnaryListenerManager; import java.util.concurrent.ScheduledExecutorService; @FunctionalInterface public interface ConnectionStateListenerManagerFactory { /** * Create a new listener manager * * @param client curator client * @return manager */ UnaryListenerManager newManager(CuratorFramework client); /** * Pass through */ ConnectionStateListenerManagerFactory standard = (__) -> StandardListenerManager.standard(); /** * Listeners set in this manager receive circuit breaking behavior using {@link org.apache.curator.framework.state.CircuitBreakingConnectionStateListener} * as a master listener that proxies to any listener registered by client code (unless the listener returns true * for {@link ConnectionStateListener#doNotProxy()}). * * @param retryPolicy the circuit breaking policy to use * @return new listener manager factory */ static ConnectionStateListenerManagerFactory circuitBreaking(RetryPolicy retryPolicy) { return client -> new CircuitBreakingManager(client, CircuitBreaker.build(retryPolicy)); } /** * Listeners set in this manager receive circuit breaking behavior using {@link org.apache.curator.framework.state.CircuitBreakingConnectionStateListener} * as a master listener that proxies to any listener registered by client code (unless the listener returns true * for {@link ConnectionStateListener#doNotProxy()}). * * @param retryPolicy the circuit breaking policy to use * @param service the scheduler to use * @return new listener manager factory */ static ConnectionStateListenerManagerFactory circuitBreaking(RetryPolicy retryPolicy, ScheduledExecutorService service) { return client -> new CircuitBreakingManager(client, CircuitBreaker.build(retryPolicy, service)); } } ConnectionStateManager.java000066400000000000000000000322211442004423600401640ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.UnaryListenerManager; import org.apache.curator.utils.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; /** * Used internally to manage connection state */ public class ConnectionStateManager implements Closeable { private static final int QUEUE_SIZE; static { int size = 25; String property = System.getProperty("ConnectionStateManagerSize", null); if ( property != null ) { try { size = Integer.parseInt(property); } catch ( NumberFormatException ignore ) { // ignore } } QUEUE_SIZE = size; } private final Logger log = LoggerFactory.getLogger(getClass()); private final BlockingQueue eventQueue = new ArrayBlockingQueue(QUEUE_SIZE); private final CuratorFramework client; private final int sessionTimeoutMs; private final int sessionExpirationPercent; private final AtomicBoolean initialConnectMessageSent = new AtomicBoolean(false); private final ExecutorService service; private final AtomicReference state = new AtomicReference(State.LATENT); private final UnaryListenerManager listeners; // guarded by sync private ConnectionState currentConnectionState; private volatile long startOfSuspendedEpoch = 0; private volatile long lastExpiredInstanceIndex = -1; private enum State { LATENT, STARTED, CLOSED } /** * @param client the client * @param threadFactory thread factory to use or null for a default * @param sessionTimeoutMs the ZK session timeout in milliseconds * @param sessionExpirationPercent percentage of negotiated session timeout to use when simulating a session timeout. 0 means don't simulate at all */ public ConnectionStateManager(CuratorFramework client, ThreadFactory threadFactory, int sessionTimeoutMs, int sessionExpirationPercent) { this(client, threadFactory, sessionTimeoutMs, sessionExpirationPercent, ConnectionStateListenerManagerFactory.standard); } /** * @param client the client * @param threadFactory thread factory to use or null for a default * @param sessionTimeoutMs the ZK session timeout in milliseconds * @param sessionExpirationPercent percentage of negotiated session timeout to use when simulating a session timeout. 0 means don't simulate at all * @param managerFactory manager factory to use */ public ConnectionStateManager(CuratorFramework client, ThreadFactory threadFactory, int sessionTimeoutMs, int sessionExpirationPercent, ConnectionStateListenerManagerFactory managerFactory) { this.client = client; this.sessionTimeoutMs = sessionTimeoutMs; this.sessionExpirationPercent = sessionExpirationPercent; if ( threadFactory == null ) { threadFactory = ThreadUtils.newThreadFactory("ConnectionStateManager"); } service = Executors.newSingleThreadExecutor(threadFactory); listeners = managerFactory.newManager(client); } /** * Start the manager */ public void start() { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once"); service.submit ( new Callable() { @Override public Object call() throws Exception { processEvents(); return null; } } ); } @Override public void close() { if ( state.compareAndSet(State.STARTED, State.CLOSED) ) { service.shutdownNow(); listeners.clear(); } } /** * Return the listenable * * @return listenable * @since 4.2.0 return type has changed from ListenerContainer to Listenable */ public Listenable getListenable() { return listeners; } /** * Change to {@link ConnectionState#SUSPENDED} only if not already suspended and not lost * * @return true if connection is set to SUSPENDED */ public synchronized boolean setToSuspended() { if ( state.get() != State.STARTED ) { return false; } if ( (currentConnectionState == ConnectionState.LOST) || (currentConnectionState == ConnectionState.SUSPENDED) ) { return false; } setCurrentConnectionState(ConnectionState.SUSPENDED); postState(ConnectionState.SUSPENDED); return true; } /** * Post a state change. If the manager is already in that state the change * is ignored. Otherwise the change is queued for listeners. * * @param newConnectionState new state * @return true if the state actually changed, false if it was already at that state */ public synchronized boolean addStateChange(ConnectionState newConnectionState) { if ( state.get() != State.STARTED ) { return false; } ConnectionState previousState = currentConnectionState; if ( previousState == newConnectionState ) { return false; } setCurrentConnectionState(newConnectionState); ConnectionState localState = newConnectionState; boolean isNegativeMessage = ((newConnectionState == ConnectionState.LOST) || (newConnectionState == ConnectionState.SUSPENDED) || (newConnectionState == ConnectionState.READ_ONLY)); if ( !isNegativeMessage && initialConnectMessageSent.compareAndSet(false, true) ) { localState = ConnectionState.CONNECTED; } postState(localState); return true; } public synchronized boolean blockUntilConnected(int maxWaitTime, TimeUnit units) throws InterruptedException { long startTime = System.currentTimeMillis(); boolean hasMaxWait = (units != null); long maxWaitTimeMs = hasMaxWait ? TimeUnit.MILLISECONDS.convert(maxWaitTime, units) : 0; while ( !isConnected() ) { if ( hasMaxWait ) { long waitTime = maxWaitTimeMs - (System.currentTimeMillis() - startTime); if ( waitTime <= 0 ) { return isConnected(); } wait(waitTime); } else { wait(); } } return isConnected(); } public synchronized boolean isConnected() { return (currentConnectionState != null) && currentConnectionState.isConnected(); } private void postState(ConnectionState state) { log.info("State change: " + state); notifyAll(); while ( !eventQueue.offer(state) ) { eventQueue.poll(); log.warn("ConnectionStateManager queue full - dropping events to make room"); } } private void processEvents() { while ( state.get() == State.STARTED ) { try { int useSessionTimeoutMs = getUseSessionTimeoutMs(); long elapsedMs = startOfSuspendedEpoch == 0 ? useSessionTimeoutMs / 2 : System.currentTimeMillis() - startOfSuspendedEpoch; long pollMaxMs = useSessionTimeoutMs - elapsedMs; final ConnectionState newState = eventQueue.poll(pollMaxMs, TimeUnit.MILLISECONDS); if ( newState != null ) { if ( listeners.isEmpty() ) { log.warn("There are no ConnectionStateListeners registered."); } listeners.forEach(listener -> listener.stateChanged(client, newState)); } else if ( sessionExpirationPercent > 0 ) { synchronized(this) { checkSessionExpiration(); } } synchronized(this) { if ( (currentConnectionState == ConnectionState.LOST) && client.getZookeeperClient().isConnected() ) { // CURATOR-525 - there is a race whereby LOST is sometimes set after the connection has been repaired // this "hack" fixes it by forcing the state to RECONNECTED log.warn("ConnectionState is LOST but isConnected() is true. Forcing RECONNECTED."); addStateChange(ConnectionState.RECONNECTED); } } } catch ( InterruptedException e ) { // swallow the interrupt as it's only possible from either a background // operation and, thus, doesn't apply to this loop or the instance // is being closed in which case the while test will get it } } } private void checkSessionExpiration() { if ( (currentConnectionState == ConnectionState.SUSPENDED) && (startOfSuspendedEpoch != 0) ) { long elapsedMs = System.currentTimeMillis() - startOfSuspendedEpoch; int useSessionTimeoutMs = getUseSessionTimeoutMs(); if ( elapsedMs >= useSessionTimeoutMs ) { startOfSuspendedEpoch = System.currentTimeMillis(); // reset startOfSuspendedEpoch to avoid spinning on this session expiration injection CURATOR-405 log.warn(String.format("Session timeout has elapsed while SUSPENDED. Injecting a session expiration. Elapsed ms: %d. Adjusted session timeout ms: %d", elapsedMs, useSessionTimeoutMs)); try { if (lastExpiredInstanceIndex == client.getZookeeperClient().getInstanceIndex()) { // last expiration didn't work for this instance, so event thread is dead and a reset is needed. CURATOR-561 client.getZookeeperClient().reset(); } else { lastExpiredInstanceIndex = client.getZookeeperClient().getInstanceIndex(); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); } } catch ( Exception e ) { log.error("Could not inject session expiration", e); } } } else if ( currentConnectionState == ConnectionState.LOST ) { try { // give ConnectionState.checkTimeouts() a chance to run, reset ensemble providers, etc. client.getZookeeperClient().getZooKeeper(); } catch ( Exception e ) { log.error("Could not get ZooKeeper", e); } } } private void setCurrentConnectionState(ConnectionState newConnectionState) { currentConnectionState = newConnectionState; startOfSuspendedEpoch = (currentConnectionState == ConnectionState.SUSPENDED) ? System.currentTimeMillis() : 0; } private int getUseSessionTimeoutMs() { int lastNegotiatedSessionTimeoutMs = client.getZookeeperClient().getLastNegotiatedSessionTimeoutMs(); int useSessionTimeoutMs = (lastNegotiatedSessionTimeoutMs > 0) ? lastNegotiatedSessionTimeoutMs : sessionTimeoutMs; useSessionTimeoutMs = sessionExpirationPercent > 0 && startOfSuspendedEpoch != 0 ? (useSessionTimeoutMs * sessionExpirationPercent) / 100 : useSessionTimeoutMs; return useSessionTimeoutMs; } } SessionConnectionStateErrorPolicy.java000066400000000000000000000021721442004423600424310ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; /** * This policy treats only {@link ConnectionState#LOST} as an error */ public class SessionConnectionStateErrorPolicy implements ConnectionStateErrorPolicy { @Override public boolean isErrorState(ConnectionState state) { return state == ConnectionState.LOST; } } StandardConnectionStateErrorPolicy.java000066400000000000000000000023111442004423600425410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; /** * This policy treats {@link ConnectionState#SUSPENDED} and {@link ConnectionState#LOST} * as errors */ public class StandardConnectionStateErrorPolicy implements ConnectionStateErrorPolicy { @Override public boolean isErrorState(ConnectionState state) { return ((state == ConnectionState.SUSPENDED) || (state == ConnectionState.LOST)); } } curator-apache-curator-5.5.0/curator-framework/src/site/000077500000000000000000000000001442004423600233005ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/site/confluence/000077500000000000000000000000001442004423600254215ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/site/confluence/index.confluence000066400000000000000000000157601442004423600306040ustar00rootroot00000000000000h1. Framework The Curator Framework is a high\-level API that greatly simplifies using ZooKeeper. It adds many features that build on ZooKeeper and handles the complexity of managing connections to the ZooKeeper cluster and retrying operations. Some of the features are: * Automatic connection management: ** There are potential error cases that require ZooKeeper clients to recreate a connection and/or retry operations. Curator automatically and transparently (mostly) handles these cases. ** Watches for NodeDataChanged events and calls updateServerList() as needed. ** Watches are automatically removed by Curator recipes * Cleaner API: ** simplifies the raw ZooKeeper methods, events, etc. ** provides a modern, fluent interface * Recipe implementations (see [[Recipes|../curator\-recipes/index.html]]): ** Leader election ** Shared lock ** Path cache and watcher ** Distributed Queue ** Distributed Priority Queue ** ... _NOTE:_ A Java 8 asynchronous version of CuratorFramework is available: [[Curator Async|../curator\-x\-async/index.html]]. h2. Allocating a Curator Framework Instance CuratorFrameworks are allocated using the CuratorFrameworkFactory which provides both factory methods and a builder for creating instances. IMPORTANT: CuratorFramework instances are fully thread\-safe. You should share one CuratorFramework per ZooKeeper cluster in your application. The factory methods (newClient()) provide a simplified way of creating an instance. The Builder gives control over all parameters. Once you have a CuratorFramework instance, you must call the start() method. At the end of your application, you should call close(). h2. CuratorFramework API The CuratorFramework uses a Fluent\-style interface. Operations are constructed using builders returned by the CuratorFramework instance. When strung together, the methods form sentence\-like statements. e.g. {code} client.create().forPath("/head", new byte[0]); client.delete().inBackground().forPath("/head"); client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/head/child", new byte[0]); client.getData().watched().inBackground().forPath("/test"); {code} h3. Methods |create()|Begins a create operation. Call additional methods (mode or background) and finalize the operation by calling forPath()| |delete()|Begins a delete operation. Call additional methods (version or background) and finalize the operation by calling forPath()| |checkExists()|Begins an operation to check that a ZNode exists. Call additional methods (watch or background) and finalize the operation by calling forPath()| |getData()|Begins an operation to get a ZNode's data. Call additional methods (watch, background or get stat) and finalize the operation by calling forPath()| |setData()|Begins an operation to set a ZNode's data. Call additional methods (version or background) and finalize the operation by calling forPath()| |getChildren()|Begins an operation to get a ZNode's list of children ZNodes. Call additional methods (watch, background or get stat) and finalize the operation by calling forPath()| |transactionOp()|Used to allocate operations to be used with transaction().| |transaction()|Atomically submit a set of operations as a transaction.| |getACL()|Begins an operation to return a ZNode's ACL settings. Call additional methods and finalize the operation by calling forPath()| |setACL()|Begins an operation to set a ZNode's ACL settings. Call additional methods and finalize the operation by calling forPath()| |getConfig()|Begins an operation to return the last committed configuration. Call additional methods and finalize the operation by calling forEnsemble()| |reconfig()|Begins an operation to change the configuration. Call additional methods and finalize the operation by calling forEnsemble()| h3. Notifications Notifications for background operations and watches are published via the ClientListener interface. You register listeners with the CuratorFramework instance using the addListener() method. The listener implements two methods: |eventReceived()|A background operation has completed or a watch has triggered. Examine the given event for details| clientClosedDueToError()|An unrecoverable error has occurred. The CuratorFramework instance has been shut down| h3. CuratorEvent The CuratorEvent object is a super\-set POJO that can hold every type of background notification and triggered watch. The useful fields of CuratorEvent depend on the type of event which is exposed via the getType() method. ||Event Type||Event Methods|| |CREATE|getResultCode() and getPath()| |DELETE|getResultCode() and getPath()| |EXISTS|getResultCode(), getPath() and getStat()| |GET\_DATA|getResultCode(), getPath(), getStat() and getData()| |SET\_DATA|getResultCode(), getPath() and getStat()| |CHILDREN|getResultCode(), getPath(), getStat(), getChildren()| |SYNC|getResultCode(), getStat()| |GET\_ACL|getResultCode(), getACLList()| |SET\_ACL|getResultCode()| |TRANSACTION|getResultCode(), getOpResults()| |WATCHED|getWatchedEvent()| |GET\_CONFIG|getResultCode(), getData()| |RECONFIG|getResultCode(), getData()| h2. Namespaces Because a ZooKeeper cluster is a shared environment, it's vital that a namespace convention is observed so that various applications that use a given cluster don't use conflicting ZK paths. The CuratorFramework has a concept of a "namespace". You set the namespace when creating a CuratorFramework instance (via the Builder). The CuratorFramework will then prepend the namespace to all paths when one of its APIs is called. i.e. {code} CuratorFramework client = CuratorFrameworkFactory.builder().namespace("MyApp") ... build(); ... client.create().forPath("/test", data); // node was actually written to: "/MyApp/test" {code} h2. Temporary Connections Temporary CuratorFramework instances are meant for single requests to ZooKeeper ensembles over a failure prone network such as a WAN. The APIs available from CuratorTempFramework are limited. Further, the connection will be closed after a period of inactivity. This is based on an idea mentioned in a post by Camille Fournier: [[http://whilefalse.blogspot.com/2012/12/building-global-highly-available.html]]. h3. Creating a CuratorTempFramework CuratorTempFramework instances are created via the CuratorFrameworkFactory just like normal CuratorFramework instances. However, instead of calling the {{build()}} method, call {{buildTemp()}}. {{buildTemp()}} creates a CuratorTempFramework instance that closes itself after 3 minutes of inactivity. There is an alternate version of {{buildTemp()}} that allows you to specify the inactivity period. h3. Limited API CuratorTempFramework instances provide the following methods: {code} /** * Stop the client */ public void close(); /** * Start a transaction builder * * @return builder object * @throws Exception errors */ public CuratorTransaction inTransaction() throws Exception; /** * Start a get data builder * * @return builder object * @throws Exception errors */ public TempGetDataBuilder getData() throws Exception; {code} curator-apache-curator-5.5.0/curator-framework/src/site/confluence/schema.confluence000066400000000000000000000117531442004423600307330ustar00rootroot00000000000000h1. Schema/Documentation Support In larger ZooKeeper applications you will end with many paths to ZNodes that have specific meanings and uses. E.g. "/myapp/clients/clusters/instances" or "/myapp/writers/locks". These paths can become unwieldy and difficult to reason about. Ideally, you should have a simple way to commonly refer to these paths, a way to validate how they are used, and a way to document what they are used for. Curator provides a mechanism to: * Document your ZooKeeper paths * Validate operations on ZooKeeper paths * Map a simple string to a ZooKeeper path When a Curator operation violates a schema, {{SchemaViolation}} is thrown. h2. Schema and SchemaSet The basic specification is the Schema class: || Field || Type || Required || Description || | name | String | Y | A unique name for this schema | | path | String | Y | A full path to a ZNode or a regex pattern to a ZNode | | documentation | String | N | User displayable documentation for this schema | | schemaValidator | SchemaValidator | N | _see below_ | | ephemeral | can/must/cannot | N | Whether the schema allows for ephemerals at the path | | sequential | can/must/cannot | N | Whether the schema allows for sequentials at the path | | watched | can/must/cannot | N | Whether the schema allows for watchers at the path | | canBeDeleted | true/false | N | Whether the schema allows the path to be deleted | | metadata | map | N | Any fields\-to\-values you want | All the Schema instances are combined into a SchemaSet and this can be set in the CuratorFrameworkFactory when creating a CuratorFramework instance. Schemas in a SchemaSet are applied in the following order: # Exact match on full path (i.e. non-regex) # Match on the first regex path, searched in the order given to the SchemaSet constructor h3. SchemaValidator SchemaValidators are used to optionally validate a ZNode operation when the node is created or modified. It is a functor of the form: {code} boolean isValid(Schema schema, String path, byte[] data, List acl); {code} h2. Getting ZNode paths/schemas by name Use SchemaSets to access ZNode paths by a simple name. E.g. {code} CuratorFramework client = ... String path = SchemaSet.getNamedPath(client, "locks"); client.create().forPath(path); {code} h2. Loading JSON Schema from a file/stream An optional utility is provided to load SchemaSets from a JSON file or stream: SchemaSetLoader. *NOTE:* to avoid adding a new dependency to Curator, the Jackson library has been used with "provided" scope. You will need to add a dependency to jackson\-core and jackson\-databind to your project. The JSON stream should be an array of schemas: {code} [ { "name": "name", required - name of the schema "path": "path or pattern", required - full path or regex pattern "isRegex": true/false, optional - true if path is a regular expression - default is false "schemaValidator": "name", optional - name of a schema validator - default is no validator "documentation": "docs", optional - user displayable docs - default is "" "ephemeral": "allowance", optional - "can", "must" or "cannot" - default is "can" "sequential": "allowance", optional - "can", "must" or "cannot" - default is "can" "watched": "allowance", optional - "can", "must" or "cannot" - default is "can" "canBeDeleted": true/false, optional - true if ZNode at path can be deleted - default is true "metadata": { optional - any fields -> values that you want "field1": "value1", "field2": "value2" } } ] {code} h2. Examples h3. Example 1 {code} [ { "name": "test", "path": "/a/b/c", "ephemeral": "must", "sequential": "cannot", "metadata": { "origin": "outside", "type": "large" } } ] {code} * This SchemaSet has only 1 schema * The schema applies only to the path "/a/b/c" * The ZNode "/a/b/c" must be ephemeral, cannot be sequential, can be watched, and can be deleted h3. Example 2 {code} [ { "name": "test", "path": "/a/b/c", "ephemeral": "must", "sequential": "cannot" }, { "name": "test2", "path": "/a/.*", "isRegex": true, "schemaValidator": "test" "ephemeral": "cannot", "canBeDeleted": false } ] {code} * This SchemaSet has 2 schemas * The first schema applies only to the path "/a/b/c" * The ZNode "/a/b/c" must be ephemeral, cannot be sequential, can be watched, and can be deleted * The second schema is regex and applies to any path that matches the expression "/a/.\*" * The ZNodes that match "/a/.\*" cannot be ephemeral, can be sequential, can be watched, and cannot be deleted * The second schema also has a schema validator. The schema validator named "test" (configured when constructing the SchemaSetLoader) will be called to validate ZNodes that match "/a/.\*". curator-apache-curator-5.5.0/curator-framework/src/site/site.xml000066400000000000000000000031701442004423600247670ustar00rootroot00000000000000 ]]> curator-apache-curator-5.5.0/curator-framework/src/test/000077500000000000000000000000001442004423600233135ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/000077500000000000000000000000001442004423600242345ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/000077500000000000000000000000001442004423600250235ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/000077500000000000000000000000001442004423600262445ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/000077500000000000000000000000001442004423600277235ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/000077500000000000000000000000001442004423600317205ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/ensemble/000077500000000000000000000000001442004423600335125ustar00rootroot00000000000000TestEnsembleProvider.java000066400000000000000000000124571442004423600404140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/ensemble/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.ensemble; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.ensemble.EnsembleProvider; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.TestingServer; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; public class TestEnsembleProvider extends BaseClassForTests { private final Timing timing = new Timing(); @Test public void testBasic() { Semaphore counter = new Semaphore(0); final CuratorFramework client = newClient(counter); try { client.start(); assertTrue(timing.acquireSemaphore(counter)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testAfterSessionExpiration() throws Exception { TestingServer oldServer = server; Semaphore counter = new Semaphore(0); final CuratorFramework client = newClient(counter); try { final CountDownLatch connectedLatch = new CountDownLatch(1); final CountDownLatch lostLatch = new CountDownLatch(1); final CountDownLatch reconnectedLatch = new CountDownLatch(1); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.CONNECTED ) { connectedLatch.countDown(); } if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } if ( newState == ConnectionState.RECONNECTED ) { reconnectedLatch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); client.start(); assertTrue(timing.awaitLatch(connectedLatch)); server.stop(); assertTrue(timing.awaitLatch(lostLatch)); counter.drainPermits(); for ( int i = 0; i < 5; ++i ) { // the ensemble provider should still be called periodically when the connection is lost assertTrue(timing.acquireSemaphore(counter), "Failed when i is: " + i); } server = new TestingServer(); // this changes the CountingEnsembleProvider's value for getConnectionString() - connection should notice this and recover assertTrue(timing.awaitLatch(reconnectedLatch)); } finally { CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(oldServer); } } private CuratorFramework newClient(Semaphore counter) { return CuratorFrameworkFactory.builder() .ensembleProvider(new CountingEnsembleProvider(counter)) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()) .retryPolicy(new RetryOneTime(1)) .build(); } private class CountingEnsembleProvider implements EnsembleProvider { private final Semaphore getConnectionStringCounter; public CountingEnsembleProvider(Semaphore getConnectionStringCounter) { this.getConnectionStringCounter = getConnectionStringCounter; } @Override public void start() { // NOP } @Override public String getConnectionString() { getConnectionStringCounter.release(); return server.getConnectString(); } @Override public void close() { // NOP } @Override public void setConnectionString(String connectionString) { // NOP } @Override public boolean updateServerListEnabled() { return false; } } } curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/000077500000000000000000000000001442004423600326705ustar00rootroot00000000000000TestBlockUntilConnected.java000066400000000000000000000200271442004423600402060ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.TestingServer; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestBlockUntilConnected extends BaseClassForTests { /** * Test the case where we're already connected */ @Test public void testBlockUntilConnectedCurrentlyConnected() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { final CountDownLatch connectedLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState.isConnected() ) { connectedLatch.countDown(); } } }); client.start(); assertTrue(timing.awaitLatch(connectedLatch), "Timed out awaiting latch"); assertTrue(client.blockUntilConnected(1, TimeUnit.SECONDS), "Not connected"); } catch ( InterruptedException e ) { fail("Unexpected interruption"); } finally { CloseableUtils.closeQuietly(client); } } /** * Test the case where we are not currently connected and never have been */ @Test public void testBlockUntilConnectedCurrentlyNeverConnected() { CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); assertTrue(client.blockUntilConnected(5, TimeUnit.SECONDS), "Not connected"); } catch ( InterruptedException e ) { fail("Unexpected interruption"); } finally { CloseableUtils.closeQuietly(client); } } /** * Test the case where we are not currently connected, but have been previously */ @Test public void testBlockUntilConnectedCurrentlyAwaitingReconnect() { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). sessionTimeoutMs(timing.session()). retryPolicy(new RetryOneTime(1)). build(); final CountDownLatch lostLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } } }); try { client.start(); //Block until we're connected assertTrue(client.blockUntilConnected(5, TimeUnit.SECONDS), "Failed to connect"); //Kill the server CloseableUtils.closeQuietly(server); //Wait until we hit the lost state assertTrue(timing.awaitLatch(lostLatch), "Failed to reach LOST state"); server = new TestingServer(server.getPort(), server.getTempDirectory()); assertTrue(client.blockUntilConnected(5, TimeUnit.SECONDS), "Not connected"); } catch ( Exception e ) { fail("Unexpected exception " + e); } finally { CloseableUtils.closeQuietly(client); } } /** * Test the case where we are not currently connected and time out before a * connection becomes available. */ @Test public void testBlockUntilConnectedConnectTimeout() { //Kill the server CloseableUtils.closeQuietly(server); CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); assertFalse(client.blockUntilConnected(5, TimeUnit.SECONDS), "Connected"); } catch ( InterruptedException e ) { fail("Unexpected interruption"); } finally { CloseableUtils.closeQuietly(client); } } /** * Test the case where we are not currently connected and the thread gets interrupted * prior to a connection becoming available */ @Test public void testBlockUntilConnectedInterrupt() { //Kill the server CloseableUtils.closeQuietly(server); final CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final Thread threadToInterrupt = Thread.currentThread(); Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { threadToInterrupt.interrupt(); } }, 3000); client.blockUntilConnected(5, TimeUnit.SECONDS); fail("Expected interruption did not occur"); } catch ( InterruptedException e ) { //This is expected } finally { CloseableUtils.closeQuietly(client); } } /** * Test that we are actually connected every time that we block until connection is established in a tight loop. */ @Test public void testBlockUntilConnectedTightLoop() throws InterruptedException { CuratorFramework client; for(int i = 0 ; i < 50 ; i++) { client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(100)); try { client.start(); client.blockUntilConnected(); assertTrue(client.getZookeeperClient().isConnected(), "Not connected after blocking for connection #" + i); } finally { client.close(); } } } } TestCleanState.java000066400000000000000000000106751442004423600363500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.test.WatchersDebug; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.ZooKeeper; import java.util.concurrent.Callable; import org.awaitility.Awaitility; public class TestCleanState { private static final boolean IS_ENABLED = Boolean.getBoolean("PROPERTY_VALIDATE_NO_REMAINING_WATCHERS"); public static void closeAndTestClean(CuratorFramework client) { if ( (client == null) || !IS_ENABLED ) { return; } try { Timing2 timing = new Timing2(); CuratorFrameworkImpl internalClient = (CuratorFrameworkImpl)client; EnsembleTracker ensembleTracker = internalClient.getEnsembleTracker(); if ( ensembleTracker != null ) { Awaitility.await() .until(() -> !ensembleTracker.hasOutstanding()); ensembleTracker.close(); } ZooKeeper zooKeeper = internalClient.getZooKeeper(); if ( zooKeeper != null ) { final int maxLoops = 3; for ( int i = 0; i < maxLoops; ++i ) // it takes time for the watcher removals to settle due to async/watchers, etc. So, if there are remaining watchers, sleep a bit { if ( i > 0 ) { timing.multiple(.5).sleepABit(); } boolean isLast = (i + 1) == maxLoops; if ( WatchersDebug.getChildWatches(zooKeeper).size() != 0 ) { if ( isLast ) { throw new AssertionError("One or more child watchers are still registered: " + WatchersDebug.getChildWatches(zooKeeper)); } continue; } if ( WatchersDebug.getExistWatches(zooKeeper).size() != 0 ) { if ( isLast ) { throw new AssertionError("One or more exists watchers are still registered: " + WatchersDebug.getExistWatches(zooKeeper)); } continue; } if ( WatchersDebug.getDataWatches(zooKeeper).size() != 0 ) { if ( isLast ) { throw new AssertionError("One or more data watchers are still registered: " + WatchersDebug.getDataWatches(zooKeeper)); } continue; } break; } } } catch ( IllegalStateException ignore ) { // client already closed } catch ( Exception e ) { e.printStackTrace(); // not sure what to do here } finally { CloseableUtils.closeQuietly(client); } } public static void test(CuratorFramework client, Callable proc) throws Exception { boolean succeeded = false; try { proc.call(); succeeded = true; } finally { if ( succeeded ) { closeAndTestClean(client); } else { CloseableUtils.closeQuietly(client); } } } private TestCleanState() { } } TestCompression.java000066400000000000000000000113701442004423600366170ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.CompressionProvider; import org.apache.curator.retry.RetryOneTime; import org.junit.jupiter.api.Test; import java.util.concurrent.atomic.AtomicInteger; public class TestCompression extends BaseClassForTests { @Test public void testCompressionProvider() throws Exception { final byte[] data = "here's a string".getBytes(); final AtomicInteger compressCounter = new AtomicInteger(); final AtomicInteger decompressCounter = new AtomicInteger(); CompressionProvider compressionProvider = new CompressionProvider() { @Override public byte[] compress(String path, byte[] data) throws Exception { compressCounter.incrementAndGet(); byte[] bytes = new byte[data.length * 2]; System.arraycopy(data, 0, bytes, 0, data.length); System.arraycopy(data, 0, bytes, data.length, data.length); return bytes; } @Override public byte[] decompress(String path, byte[] compressedData) throws Exception { decompressCounter.incrementAndGet(); byte[] bytes = new byte[compressedData.length / 2]; System.arraycopy(compressedData, 0, bytes, 0, bytes.length); return bytes; } }; CuratorFramework client = CuratorFrameworkFactory.builder(). compressionProvider(compressionProvider). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); client.create().compressed().creatingParentsIfNeeded().forPath("/a/b/c", data); assertNotEquals(data, client.getData().forPath("/a/b/c")); assertEquals(data.length, client.getData().decompressed().forPath("/a/b/c").length); } finally { CloseableUtils.closeQuietly(client); } assertEquals(compressCounter.get(), 1); assertEquals(decompressCounter.get(), 1); } @Test public void testSetData() throws Exception { final byte[] data = "here's a string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.create().creatingParentsIfNeeded().forPath("/a/b/c", data); assertArrayEquals(data, client.getData().forPath("/a/b/c")); client.setData().compressed().forPath("/a/b/c", data); assertEquals(data.length, client.getData().decompressed().forPath("/a/b/c").length); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSimple() throws Exception { final byte[] data = "here's a string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.create().compressed().creatingParentsIfNeeded().forPath("/a/b/c", data); assertNotEquals(data, client.getData().forPath("/a/b/c")); assertEquals(data.length, client.getData().decompressed().forPath("/a/b/c").length); } finally { CloseableUtils.closeQuietly(client); } } } TestCompressionInTransactionNew.java000066400000000000000000000143421442004423600417700ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; public class TestCompressionInTransactionNew extends BaseClassForTests { @Test public void testSetData() throws Exception { final String path = "/a"; final byte[] data = "here's a string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); //Create uncompressed data in a transaction CuratorOp op = client.transactionOp().create().forPath(path, data); client.transaction().forOperations(op); assertArrayEquals(data, client.getData().forPath(path)); //Create compressed data in transaction op = client.transactionOp().setData().compressed().forPath(path, data); client.transaction().forOperations(op); assertArrayEquals(data, client.getData().decompressed().forPath(path)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSetCompressedAndUncompressed() throws Exception { final String path1 = "/a"; final String path2 = "/b"; final byte[] data1 = "here's a string".getBytes(); final byte[] data2 = "here's another string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); //Create the nodes CuratorOp op1 = client.transactionOp().create().compressed().forPath(path1); CuratorOp op2 = client.transactionOp().create().forPath(path2); client.transaction().forOperations(op1, op2); //Check they exist assertNotNull(client.checkExists().forPath(path1)); assertNotNull(client.checkExists().forPath(path2)); //Set the nodes, path1 compressed, path2 uncompressed. op1 = client.transactionOp().setData().compressed().forPath(path1, data1); op2 = client.transactionOp().setData().forPath(path2, data2); client.transaction().forOperations(op1, op2); assertNotEquals(data1, client.getData().forPath(path1)); assertArrayEquals(data1, client.getData().decompressed().forPath(path1)); assertArrayEquals(data2, client.getData().forPath(path2)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSimple() throws Exception { final String path1 = "/a"; final String path2 = "/a/b"; final byte[] data1 = "here's a string".getBytes(); final byte[] data2 = "here's another string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); CuratorOp op1 = client.transactionOp().create().compressed().forPath(path1, data1); CuratorOp op2 = client.transactionOp().create().compressed().forPath(path2, data2); client.transaction().forOperations(op1, op2); assertNotEquals(data1, client.getData().forPath(path1)); assertArrayEquals(data1, client.getData().decompressed().forPath(path1)); assertNotEquals(data2, client.getData().forPath(path2)); assertArrayEquals(data2, client.getData().decompressed().forPath(path2)); } finally { CloseableUtils.closeQuietly(client); } } /** * Test the case where both uncompressed and compressed data is generated in * the same transaction * @throws Exception */ @Test public void testCreateCompressedAndUncompressed() throws Exception { final String path1 = "/a"; final String path2 = "/b"; final byte[] data1 = "here's a string".getBytes(); final byte[] data2 = "here's another string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); CuratorOp op1 = client.transactionOp().create().compressed().forPath(path1, data1); CuratorOp op2 = client.transactionOp().create().forPath(path2, data2); client.transaction().forOperations(op1, op2); assertNotEquals(data1, client.getData().forPath(path1)); assertArrayEquals(data1, client.getData().decompressed().forPath(path1)); assertArrayEquals(data2, client.getData().forPath(path2)); } finally { CloseableUtils.closeQuietly(client); } } } TestCompressionInTransactionOld.java000066400000000000000000000134121442004423600417520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.junit.jupiter.api.Test; @SuppressWarnings("deprecation") public class TestCompressionInTransactionOld extends BaseClassForTests { @Test public void testSetData() throws Exception { final String path = "/a"; final byte[] data = "here's a string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); //Create uncompressed data in a transaction client.inTransaction().create().forPath(path, data).and().commit(); assertArrayEquals(data, client.getData().forPath(path)); //Create compressed data in transaction client.inTransaction().setData().compressed().forPath(path, data).and().commit(); assertArrayEquals(data, client.getData().decompressed().forPath(path)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSetCompressedAndUncompressed() throws Exception { final String path1 = "/a"; final String path2 = "/b"; final byte[] data1 = "here's a string".getBytes(); final byte[] data2 = "here's another string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); //Create the nodes client.inTransaction().create().compressed().forPath(path1).and(). create().forPath(path2).and().commit(); //Check they exist assertNotNull(client.checkExists().forPath(path1)); assertNotNull(client.checkExists().forPath(path2)); //Set the nodes, path1 compressed, path2 uncompressed. client.inTransaction().setData().compressed().forPath(path1, data1).and(). setData().forPath(path2, data2).and().commit(); assertNotEquals(data1, client.getData().forPath(path1)); assertArrayEquals(data1, client.getData().decompressed().forPath(path1)); assertArrayEquals(data2, client.getData().forPath(path2)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSimple() throws Exception { final String path1 = "/a"; final String path2 = "/a/b"; final byte[] data1 = "here's a string".getBytes(); final byte[] data2 = "here's another string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.inTransaction().create().compressed().forPath(path1, data1).and(). create().compressed().forPath(path2, data2).and().commit(); assertNotEquals(data1, client.getData().forPath(path1)); assertArrayEquals(data1, client.getData().decompressed().forPath(path1)); assertNotEquals(data2, client.getData().forPath(path2)); assertArrayEquals(data2, client.getData().decompressed().forPath(path2)); } finally { CloseableUtils.closeQuietly(client); } } /** * Test the case where both uncompressed and compressed data is generated in * the same transaction * @throws Exception */ @Test public void testCreateCompressedAndUncompressed() throws Exception { final String path1 = "/a"; final String path2 = "/b"; final byte[] data1 = "here's a string".getBytes(); final byte[] data2 = "here's another string".getBytes(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.inTransaction().create().compressed().forPath(path1, data1).and(). create().forPath(path2, data2).and().commit(); assertNotEquals(data1, client.getData().forPath(path1)); assertArrayEquals(data1, client.getData().decompressed().forPath(path1)); assertArrayEquals(data2, client.getData().forPath(path2)); } finally { CloseableUtils.closeQuietly(client); } } } TestCreate.java000066400000000000000000000517251442004423600355310ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.apache.zookeeper.ZooDefs.Ids.ANYONE_ID_UNSAFE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CreateBuilderMain; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class TestCreate extends BaseClassForTests { private static final List READ_CREATE = Collections.singletonList(new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ, ANYONE_ID_UNSAFE)); private static final List READ_CREATE_WRITE = Collections.singletonList(new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ | ZooDefs.Perms.WRITE, ANYONE_ID_UNSAFE)); private static ACLProvider testACLProvider = new ACLProvider() { @Override public List getDefaultAcl() { return ZooDefs.Ids.OPEN_ACL_UNSAFE; } @Override public List getAclForPath(String path) { switch ( path ) { case "/bar": return READ_CREATE; case "/bar/foo": return READ_CREATE_WRITE; } return null; } }; private CuratorFramework createClient(ACLProvider aclProvider) { return CuratorFrameworkFactory.builder(). aclProvider(aclProvider). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); } /** * Tests that the ACL list provided to the create builder is used for creating the parents. */ @Test public void testCreateWithParentsWithAcl() throws Exception { CuratorFramework client = createClient(new DefaultACLProvider()); try { client.start(); String path = "/bar/foo"; List acl = Collections.singletonList(new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ, ANYONE_ID_UNSAFE)); client.create().creatingParentsIfNeeded().withACL(acl).forPath(path); List actual_bar_foo = client.getACL().forPath(path); assertEquals(actual_bar_foo, acl); List actual_bar = client.getACL().forPath("/bar"); assertEquals(actual_bar, ZooDefs.Ids.OPEN_ACL_UNSAFE); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateWithParentsWithAclApplyToParents() throws Exception { CuratorFramework client = createClient(new DefaultACLProvider()); try { client.start(); String path = "/bar/foo"; List acl = Collections.singletonList(new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ, ANYONE_ID_UNSAFE)); client.create().creatingParentsIfNeeded().withACL(acl, true).forPath(path); List actual_bar_foo = client.getACL().forPath(path); assertEquals(actual_bar_foo, acl); List actual_bar = client.getACL().forPath("/bar"); assertEquals(actual_bar, acl); } finally { CloseableUtils.closeQuietly(client); } } /** * Tests that the ACL list provided to the create builder is used for creating the parents. */ @Test public void testCreateWithParentsWithAclInBackground() throws Exception { CuratorFramework client = createClient(new DefaultACLProvider()); try { client.start(); final CountDownLatch latch = new CountDownLatch(1); String path = "/bar/foo"; List acl = Collections.singletonList(new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ, ANYONE_ID_UNSAFE)); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { latch.countDown(); } }; client.create().creatingParentsIfNeeded().withACL(acl).inBackground(callback).forPath(path); assertTrue(latch.await(2000, TimeUnit.MILLISECONDS), "Callback not invoked"); List actual_bar_foo = client.getACL().forPath(path); assertEquals(actual_bar_foo, acl); List actual_bar = client.getACL().forPath("/bar"); assertEquals(actual_bar, ZooDefs.Ids.OPEN_ACL_UNSAFE); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateWithParentsWithAclApplyToParentsInBackground() throws Exception { CuratorFramework client = createClient(new DefaultACLProvider()); try { client.start(); final CountDownLatch latch = new CountDownLatch(1); String path = "/bar/foo"; List acl = Collections.singletonList(new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ, ANYONE_ID_UNSAFE)); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { latch.countDown(); } }; client.create().creatingParentsIfNeeded().withACL(acl, true).inBackground(callback).forPath(path); assertTrue(latch.await(2000, TimeUnit.MILLISECONDS), "Callback not invoked"); List actual_bar_foo = client.getACL().forPath(path); assertEquals(actual_bar_foo, acl); List actual_bar = client.getACL().forPath("/bar"); assertEquals(actual_bar, acl); } finally { CloseableUtils.closeQuietly(client); } } /** * Tests that if no ACL list provided to the create builder, then the ACL list is created based on the client's ACLProvider. */ @Test public void testCreateWithParentsWithoutAcl() throws Exception { CuratorFramework client = createClient(testACLProvider); try { client.start(); String path = "/bar/foo/boo"; client.create().creatingParentsIfNeeded().forPath(path); List actual_bar_foo_boo = client.getACL().forPath("/bar/foo/boo"); assertEquals(actual_bar_foo_boo, ZooDefs.Ids.OPEN_ACL_UNSAFE); List actual_bar_foo = client.getACL().forPath("/bar/foo"); assertEquals(actual_bar_foo, READ_CREATE_WRITE); List actual_bar = client.getACL().forPath("/bar"); assertEquals(actual_bar, READ_CREATE); } finally { CloseableUtils.closeQuietly(client); } } /** * Tests that if no ACL list provided to the create builder, then the ACL list is created based on the client's ACLProvider. */ @Test public void testCreateWithParentsWithoutAclInBackground() throws Exception { CuratorFramework client = createClient(testACLProvider); try { client.start(); final CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { latch.countDown(); } }; final String path = "/bar/foo/boo"; client.create().creatingParentsIfNeeded().inBackground(callback).forPath(path); assertTrue(latch.await(2000, TimeUnit.MILLISECONDS), "Callback not invoked"); List actual_bar_foo_boo = client.getACL().forPath(path); assertEquals(actual_bar_foo_boo, ZooDefs.Ids.OPEN_ACL_UNSAFE); List actual_bar_foo = client.getACL().forPath("/bar/foo"); assertEquals(actual_bar_foo, READ_CREATE_WRITE); List actual_bar = client.getACL().forPath("/bar"); assertEquals(actual_bar, READ_CREATE); } finally { CloseableUtils.closeQuietly(client); } } private void check(CuratorFramework client, CreateBuilderMain builder, String path, byte[] data, boolean expectedSuccess) throws Exception { int expectedCode = (expectedSuccess) ? KeeperException.Code.OK.intValue() : KeeperException.Code.NODEEXISTS.intValue(); try { builder.forPath(path, data); assertEquals(expectedCode, KeeperException.Code.OK.intValue()); Stat stat = new Stat(); byte[] actualData = client.getData().storingStatIn(stat).forPath(path); assertTrue(IdempotentUtils.matches(0, data, stat.getVersion(), actualData)); } catch (KeeperException e) { assertEquals(expectedCode, e.getCode()); } } private void checkBackground(CuratorFramework client, CreateBuilderMain builder, String path, byte[] data, boolean expectedSuccess) throws Exception { int expectedCode = (expectedSuccess) ? KeeperException.Code.OK.intValue() : KeeperException.Code.NODEEXISTS.intValue(); AtomicInteger actualCode = new AtomicInteger(-1); CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { actualCode.set(event.getResultCode()); latch.countDown(); } }; builder.inBackground(callback).forPath(path, data); assertTrue(latch.await(5000, TimeUnit.MILLISECONDS), "Callback not invoked"); assertEquals(expectedCode, actualCode.get()); if (expectedCode == KeeperException.Code.OK.intValue()) { Stat stat = new Stat(); byte[] actualData = client.getData().storingStatIn(stat).forPath(path); assertTrue(IdempotentUtils.matches(0, data, stat.getVersion(), actualData)); } } private CreateBuilderMain clBefore(CreateBuilderMain builder) { ((CreateBuilderImpl)builder).failBeforeNextCreateForTesting = true; return builder; } private CreateBuilderMain clAfter(CreateBuilderMain builder) { ((CreateBuilderImpl)builder).failNextCreateForTesting = true; return builder; } private CreateBuilderMain clCheck(CreateBuilderMain builder) { ((CreateBuilderImpl)builder).failNextIdempotentCheckForTesting = true; return builder; } /** * Tests that NodeExists on failNextCreate doesn't hang in background */ @Test public void testBackgroundFaultInjectionHang() throws Exception { CuratorFramework client = createClient(new DefaultACLProvider()); try { client.start(); Stat stat = new Stat(); String path = "/create"; byte[] data = new byte[] {1, 2}; CreateBuilderMain create = client.create(); check(client, create, path, data, true); checkBackground(client, clAfter(client.create()), path, data, false); } finally { CloseableUtils.closeQuietly(client); } } /** * Tests all cases of idempotent create */ @Test public void testIdempotentCreate() throws Exception { CuratorFramework client = createClient(new DefaultACLProvider()); try { client.start(); Stat stat = new Stat(); String path = "/idpcreate"; String pathBack = "/idpcreateback"; byte[] data1 = new byte[] {1, 2, 3}; byte[] data2 = new byte[] {4, 5, 6}; // check foreground and backgroud // first create should succeed check(client, client.create().idempotent(), path, data1, true); checkBackground(client, client.create().idempotent(), pathBack, data1, true); // repeating the same op should succeed check(client, client.create().idempotent(), path, data1, true); checkBackground(client, client.create().idempotent(), pathBack, data1, true); // same op with different data should fail even though version matches check(client, client.create().idempotent(), path, data2, false); checkBackground(client, client.create().idempotent(), pathBack, data2, false); // now set data to new version client.setData().forPath(path, data2); client.setData().forPath(pathBack, data2); // version should now be 1 so both versions should fail check(client, client.create().idempotent(), path, data1, false); checkBackground(client, client.create().idempotent(), pathBack, data1, false); check(client, client.create().idempotent(), path, data2, false); checkBackground(client, client.create().idempotent(), pathBack, data2, false); } finally { CloseableUtils.closeQuietly(client); } } // Test that idempotent create automatically retries successfully upon connectionLoss @Test public void testIdempotentCreateConnectionLoss() throws Exception { CuratorFramework client = createClient(new DefaultACLProvider()); try { client.start(); String path1 = "/idpcreate1"; String path2 = "/idpcreate2"; String path3 = "/create3"; String path4 = "/create4"; byte[] data = new byte[] {1, 2, 3}; byte[] data2 = new byte[] {1, 2, 3, 4}; // check foreground and background // Test that idempotent create succeeds with connection loss before or after first create check(client, clBefore(client.create().idempotent()), path1, data, true); checkBackground(client, clBefore(client.create().idempotent()), path1+"back", data, true); check(client, clAfter(client.create().idempotent()), path2, data, true); checkBackground(client, clAfter(client.create().idempotent()), path2+"back", data, true); // Test that repeating same operation succeeds, even with connection loss before/after/check check(client, clBefore(client.create().idempotent()), path1, data, true); checkBackground(client, clBefore(client.create().idempotent()), path1+"back", data, true); check(client, clAfter(client.create().idempotent()), path2, data, true); checkBackground(client, clAfter(client.create().idempotent()), path2+"back", data, true); check(client, clCheck(client.create().idempotent()), path2, data, true); checkBackground(client, clCheck(client.create().idempotent()), path2+"back", data, true); // test that idempotent correctly fails, even after connection loss, // by repeating earlier operations with different data check(client, clBefore(client.create().idempotent()), path1, data2, false); checkBackground(client, clBefore(client.create().idempotent()), path1+"back", data2, false); check(client, clAfter(client.create().idempotent()), path2, data2, false); checkBackground(client, clAfter(client.create().idempotent()), path2+"back", data2, false); check(client, clCheck(client.create().idempotent()), path2, data2, false); checkBackground(client, clCheck(client.create().idempotent()), path2+"back", data2, false); // Test that non-idempotent succeeds with CL before create, but fails with connection loss after check(client, clBefore(client.create()), path3, data, true); checkBackground(client, clBefore(client.create()), path3+"back", data, true); check(client, clAfter(client.create()), path4, data, false); checkBackground(client, clAfter(client.create()), path4+"back", data, false); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateProtectedUtils() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build()) { client.start(); client.blockUntilConnected(); client.create().forPath("/parent"); assertEquals(client.getChildren().forPath("/parent").size(), 0); client.create().withProtection().withMode(CreateMode.EPHEMERAL).forPath("/parent/test"); final List children = client.getChildren().forPath("/parent"); assertEquals(1, children.size()); final String testZNodeName = children.get(0); assertEquals(testZNodeName.length(), ProtectedUtils.PROTECTED_PREFIX_WITH_UUID_LENGTH + "test".length()); assertTrue(testZNodeName.startsWith(ProtectedUtils.PROTECTED_PREFIX)); assertEquals(testZNodeName.charAt(ProtectedUtils.PROTECTED_PREFIX_WITH_UUID_LENGTH - 1), ProtectedUtils.PROTECTED_SEPARATOR); assertTrue(ProtectedUtils.isProtectedZNode(testZNodeName)); assertEquals(ProtectedUtils.normalize(testZNodeName), "test"); assertFalse(ProtectedUtils.isProtectedZNode("parent")); assertEquals(ProtectedUtils.normalize("parent"), "parent"); } } @Test public void testProtectedUtils() throws Exception { String name = "_c_53345f98-9423-4e0c-a7b5-9f819e3ec2e1-yo"; assertTrue(ProtectedUtils.isProtectedZNode(name)); assertEquals(ProtectedUtils.normalize(name), "yo"); assertEquals(ProtectedUtils.extractProtectedId(name).get(), "53345f98-9423-4e0c-a7b5-9f819e3ec2e1"); name = "c_53345f98-9423-4e0c-a7b5-9f819e3ec2e1-yo"; assertFalse(ProtectedUtils.isProtectedZNode(name)); assertEquals(ProtectedUtils.normalize(name), name); assertEquals(ProtectedUtils.extractProtectedId(name), Optional.empty()); name = "_c_53345f98-hola-4e0c-a7b5-9f819e3ec2e1-yo"; assertFalse(ProtectedUtils.isProtectedZNode(name)); assertEquals(ProtectedUtils.normalize(name), name); assertEquals(ProtectedUtils.extractProtectedId(name), Optional.empty()); name = "_c_53345f98-hola-4e0c-a7b5-9f819e3ec2e1+yo"; assertFalse(ProtectedUtils.isProtectedZNode(name)); assertEquals(ProtectedUtils.normalize(name), name); assertEquals(ProtectedUtils.extractProtectedId(name), Optional.empty()); name = "_c_53345f98-9423-4e0c-a7b5-9f819e3ec2e1-yo"; assertEquals(name, ProtectedUtils.toProtectedZNode("yo", "53345f98-9423-4e0c-a7b5-9f819e3ec2e1")); assertEquals("yo", ProtectedUtils.toProtectedZNode("yo", null)); String path = ZKPaths.makePath("hola", "yo"); assertEquals(ProtectedUtils.toProtectedZNodePath(path, "53345f98-9423-4e0c-a7b5-9f819e3ec2e1"), ZKPaths.makePath("hola", name)); assertEquals(ProtectedUtils.toProtectedZNodePath(path, null), path); path = ZKPaths.makePath("hola", name); assertEquals(ProtectedUtils.normalizePath(path), "/hola/yo"); } } TestCreateReturningStat.java000077500000000000000000000205721442004423600402620ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; public class TestCreateReturningStat extends CuratorTestBase { private CuratorFramework createClient() { return CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); } private void compare(CuratorFramework client, String path, Stat expected) throws Exception { Stat queriedStat = client.checkExists().forPath(path); assertEquals(queriedStat, expected); } @Test public void testOrSetDataStoringStatIn() throws Exception { try (CuratorFramework client = createClient()) { client.start(); client.getZookeeperClient().blockUntilConnectedOrTimedOut(); final String path = "/test"; final Stat versionZeroStat = new Stat(); client.create().orSetData().storingStatIn(versionZeroStat).forPath(path); assertEquals(0, versionZeroStat.getVersion()); final Stat versionOneStat = new Stat(); client.create().orSetData().storingStatIn(versionOneStat).forPath(path); assertEquals(versionZeroStat.getAversion(), versionOneStat.getAversion()); assertEquals(versionZeroStat.getCtime(), versionOneStat.getCtime()); assertEquals(versionZeroStat.getCversion(), versionOneStat.getCversion()); assertEquals(versionZeroStat.getCzxid(), versionOneStat.getCzxid()); assertEquals(versionZeroStat.getDataLength(), versionOneStat.getDataLength()); assertEquals(versionZeroStat.getEphemeralOwner(), versionOneStat.getEphemeralOwner()); assertTrue(versionZeroStat.getMtime() <= versionOneStat.getMtime()); assertNotEquals(versionZeroStat.getMzxid(), versionOneStat.getMzxid()); assertEquals(versionZeroStat.getNumChildren(), versionOneStat.getNumChildren()); assertEquals(versionZeroStat.getPzxid(), versionOneStat.getPzxid()); assertEquals(1, versionOneStat.getVersion()); } } @Test public void testCreateReturningStat() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/bla"; Stat stat = new Stat(); client.create().storingStatIn(stat).forPath(path); compare(client, path, stat); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateReturningStatIncludingParents() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/bla/bla"; Stat stat = new Stat(); client.create().creatingParentsIfNeeded().storingStatIn(stat).forPath(path); compare(client, path, stat); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateReturningStatIncludingParentsReverse() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/bla/bla"; Stat stat = new Stat(); client.create().storingStatIn(stat).creatingParentsIfNeeded().forPath(path); compare(client, path, stat); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateReturningStatCompressed() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/bla"; Stat stat = new Stat(); client.create().compressed().storingStatIn(stat).forPath(path); compare(client, path, stat); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateReturningStatWithProtected() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/bla"; Stat stat = new Stat(); path = client.create().withProtection().storingStatIn(stat).forPath(path); compare(client, path, stat); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateReturningStatInBackground() throws Exception { Timing timing = new Timing(); CuratorFramework client = createClient(); try { client.start(); String path = "/bla"; Stat stat = new Stat(); final CountDownLatch latch = new CountDownLatch(1); final AtomicReference statRef = new AtomicReference<>(); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if(event.getType() == CuratorEventType.CREATE) { statRef.set(event.getStat()); latch.countDown(); } } }; client.create().storingStatIn(stat).inBackground(callback).forPath(path); if(!timing.awaitLatch(latch)) { fail("Timed out awaing latch"); } compare(client, path, statRef.get()); compare(client, path, stat); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testIdempotentCreateReturningStat() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/testidp"; client.create().forPath(path); Stat stat = new Stat(); client.create().idempotent().storingStatIn(stat).forPath(path); compare(client, path, stat); } finally { CloseableUtils.closeQuietly(client); } } } TestDelete.java000066400000000000000000000467561442004423600355400ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.apache.zookeeper.KeeperException.Code.*; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.BackgroundPathable; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.api.DeleteBuilder; import org.apache.curator.framework.api.DeleteBuilderMain; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.KeeperException; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; public class TestDelete extends BaseClassForTests { private static byte[] createData = new byte[] {5, 6, 7, 8}; private CuratorFramework createClient() { return CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); } /** * Tests normal delete operations */ @Test public void testNormal() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/test"; // test delete with no node fails check(client, client.delete(), path, NONODE.intValue()); client.create().forPath(path, createData); // test delete with no version succeeds check(client, client.delete(), path, OK.intValue()); client.create().forPath(path, createData); // test fails with wrong verion check(client, client.delete().withVersion(1), path, BADVERSION.intValue()); // test succeeds with correct version check(client, client.delete().withVersion(0), path, OK.intValue()); client.create().forPath(path, createData); } finally { CloseableUtils.closeQuietly(client); } } /** * Tests background versions of delete */ @Test public void testBackground() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/test"; // test delete with no node fails checkBackground(client, client.delete(), path, NONODE.intValue()); client.create().forPath(path, createData); // test delete with no version succeeds checkBackground(client, client.delete(), path, OK.intValue()); client.create().forPath(path, createData); // test fails with wrong verion checkBackground(client, client.delete().withVersion(1), path, BADVERSION.intValue()); // test succeeds with correct version checkBackground(client, client.delete().withVersion(0), path, OK.intValue()); } finally { CloseableUtils.closeQuietly(client); } } // TODO jslocum refactor these into common idempotent test utils? private void checkBackground(CuratorFramework client, BackgroundPathable builder, String path, int expectedCode) throws Exception { AtomicInteger actualCode = new AtomicInteger(-1); CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { actualCode.set(event.getResultCode()); latch.countDown(); } }; builder.inBackground(callback).forPath(path); assertTrue(latch.await(5000, TimeUnit.MILLISECONDS), "Callback not invoked"); assertEquals(expectedCode, actualCode.get()); if ( expectedCode == OK.intValue() ) { assertNull(client.checkExists().forPath(path)); } } private void check(CuratorFramework client, BackgroundPathable builder, String path, int expectedCode) throws Exception { try { builder.forPath(path); assertEquals(expectedCode, OK.intValue()); assertNull(client.checkExists().forPath(path)); } catch (KeeperException e) { assertEquals(expectedCode, e.getCode()); } } /** * Tests all cases of idempotent delete */ @Test public void testIdempotentDelete() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/idpset"; String pathBack = "/idpsetback"; // check foreground and background // delete idempotent when node doesn't exist should succeed, with or without version check(client, client.delete().idempotent().withVersion(0), path, OK.intValue()); checkBackground(client, client.delete().idempotent().withVersion(0), pathBack, OK.intValue()); check(client, client.delete().idempotent(), path, OK.intValue()); checkBackground(client, client.delete().idempotent(), pathBack, OK.intValue()); client.create().forPath(path, createData); client.create().forPath(pathBack, createData); // check normal delete without version succeeds check(client, client.delete().idempotent(), path, OK.intValue()); checkBackground(client, client.delete().idempotent(), pathBack, OK.intValue()); client.create().forPath(path, createData); client.create().forPath(pathBack, createData); // check normal delete with version succeeds check(client, client.delete().idempotent().withVersion(0), path, OK.intValue()); checkBackground(client, client.delete().idempotent().withVersion(0), pathBack, OK.intValue()); // create nodes, update version to 1 with setData client.create().forPath(path, createData); client.setData().forPath(path, createData); client.create().forPath(pathBack, createData); client.setData().forPath(pathBack, createData); // check that idempotent delete fails with wrong version check(client, client.delete().idempotent().withVersion(0), path, BADVERSION.intValue()); checkBackground(client, client.delete().idempotent().withVersion(0), pathBack, BADVERSION.intValue()); } finally { CloseableUtils.closeQuietly(client); } } private DeleteBuilderMain clBefore(DeleteBuilderMain builder) { ((DeleteBuilderImpl)builder).failBeforeNextDeleteForTesting = true; return builder; } private DeleteBuilderMain clAfter(DeleteBuilderMain builder) { ((DeleteBuilderImpl)builder).failNextDeleteForTesting = true; return builder; } // Test that idempotent set automatically retries correctly upon connectionLoss @Test public void testIdempotentDeleteConnectionLoss() throws Exception { CuratorFramework client = createClient(); try { client.start(); String path = "/delete"; String pathBack = "/deleteBack"; String pathNormal = "/deleteNormal"; String pathNormalBack = "/deleteNormalBack"; // test that connection loss before or after, version or no version, idempotent delete succeeds if node doesn't exist check(client, clBefore(client.delete().idempotent()).withVersion(0), path, OK.intValue()); checkBackground(client, clBefore(client.delete().idempotent()).withVersion(0), pathBack, OK.intValue()); check(client, clBefore(client.delete().idempotent()), path, OK.intValue()); checkBackground(client, clBefore(client.delete().idempotent()), pathBack, OK.intValue()); check(client, clAfter(client.delete().idempotent()).withVersion(0), path, OK.intValue()); checkBackground(client, clAfter(client.delete().idempotent()).withVersion(0), pathBack, OK.intValue()); check(client, clBefore(client.delete().idempotent()), path, OK.intValue()); checkBackground(client, clBefore(client.delete().idempotent()), pathBack, OK.intValue()); // test that connection loss before or after, correct version or no version, idempotent delete succeeds client.create().forPath(path, createData); client.create().forPath(pathBack, createData); check(client, clBefore(client.delete().idempotent()), path, OK.intValue()); checkBackground(client, clBefore(client.delete().idempotent()), pathBack, OK.intValue()); client.create().forPath(path, createData); client.create().forPath(pathBack, createData); check(client, clBefore(client.delete().idempotent()).withVersion(0), path, OK.intValue()); checkBackground(client, clBefore(client.delete().idempotent()).withVersion(0), pathBack, OK.intValue()); client.create().forPath(path, createData); client.create().forPath(pathBack, createData); check(client, clAfter(client.delete().idempotent()), path, OK.intValue()); checkBackground(client, clAfter(client.delete().idempotent()), pathBack, OK.intValue()); client.create().forPath(path, createData); client.create().forPath(pathBack, createData); check(client, clAfter(client.delete().idempotent()).withVersion(0), path, OK.intValue()); checkBackground(client, clAfter(client.delete().idempotent()).withVersion(0), pathBack, OK.intValue()); // test that connection loss, before or after, delete with version fails on wrong version client.create().forPath(path, createData); client.create().forPath(pathBack, createData); check(client, clBefore(client.delete().idempotent()).withVersion(2), path, BADVERSION.intValue()); checkBackground(client, clBefore(client.delete().idempotent()).withVersion(2), pathBack, BADVERSION.intValue()); check(client, clAfter(client.delete().idempotent()).withVersion(2), path, BADVERSION.intValue()); checkBackground(client, clAfter(client.delete().idempotent()).withVersion(2), pathBack, BADVERSION.intValue()); // test that non-idempotent delete with or without version succeeds when retrying with connectionloss before client.create().forPath(pathNormal, createData); client.create().forPath(pathNormalBack, createData); check(client, clBefore(client.delete()).withVersion(0), pathNormal, OK.intValue()); checkBackground(client, clBefore(client.delete()).withVersion(0), pathNormalBack, OK.intValue()); client.create().forPath(pathNormal, createData); client.create().forPath(pathNormalBack, createData); check(client, clBefore(client.delete()), pathNormal, OK.intValue()); checkBackground(client, clBefore(client.delete()), pathNormalBack, OK.intValue()); // but fails with connectionloss after with NONODE because the previous call actually deleted it client.create().forPath(pathNormal, createData); client.create().forPath(pathNormalBack, createData); check(client, clAfter(client.delete()).withVersion(0), pathNormal, NONODE.intValue()); checkBackground(client, clAfter(client.delete()).withVersion(0), pathNormalBack, NONODE.intValue()); client.create().forPath(pathNormal, createData); client.create().forPath(pathNormalBack, createData); check(client, clAfter(client.delete()), pathNormal, NONODE.intValue()); checkBackground(client, clAfter(client.delete()), pathNormalBack, NONODE.intValue()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testQuietDelete() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.delete().quietly().forPath("/foo/bar"); final BlockingQueue rc = new LinkedBlockingQueue<>(); BackgroundCallback backgroundCallback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { rc.add(event.getResultCode()); } }; client.delete().quietly().inBackground(backgroundCallback).forPath("/foo/bar/hey"); Integer code = rc.poll(new Timing().milliseconds(), TimeUnit.MILLISECONDS); assertNotNull(code); assertEquals(code.intValue(), OK.intValue()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundDelete() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.getCuratorListenable().addListener ( new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.DELETE ) { assertEquals(event.getPath(), "/head"); ((CountDownLatch)event.getContext()).countDown(); } } } ); client.create().forPath("/head"); assertNotNull(client.checkExists().forPath("/head")); CountDownLatch latch = new CountDownLatch(1); client.delete().inBackground(latch).forPath("/head"); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertNull(client.checkExists().forPath("/head")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundDeleteWithChildren() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.getCuratorListenable().addListener ( new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.DELETE ) { assertEquals(event.getPath(), "/one/two"); ((CountDownLatch)event.getContext()).countDown(); } } } ); client.create().creatingParentsIfNeeded().forPath("/one/two/three/four"); assertNotNull(client.checkExists().forPath("/one/two/three/four")); CountDownLatch latch = new CountDownLatch(1); client.delete().deletingChildrenIfNeeded().inBackground(latch).forPath("/one/two"); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertNull(client.checkExists().forPath("/one/two")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testDelete() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/head"); assertNotNull(client.checkExists().forPath("/head")); client.delete().forPath("/head"); assertNull(client.checkExists().forPath("/head")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testDeleteWithChildren() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build(); client.start(); try { client.create().creatingParentsIfNeeded().forPath("/one/two/three/four/five/six", "foo".getBytes()); client.delete().deletingChildrenIfNeeded().forPath("/one/two/three/four/five"); assertNull(client.checkExists().forPath("/one/two/three/four/five")); client.delete().deletingChildrenIfNeeded().forPath("/one/two"); assertNull(client.checkExists().forPath("/one/two")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testDeleteGuaranteedWithChildren() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build(); client.start(); try { client.create().creatingParentsIfNeeded().forPath("/one/two/three/four/five/six", "foo".getBytes()); client.delete().guaranteed().deletingChildrenIfNeeded().forPath("/one/two/three/four/five"); assertNull(client.checkExists().forPath("/one/two/three/four/five")); client.delete().guaranteed().deletingChildrenIfNeeded().forPath("/one/two"); assertNull(client.checkExists().forPath("/one/two")); } finally { CloseableUtils.closeQuietly(client); } } } TestEnabledSessionExpiredState.java000066400000000000000000000154671442004423600415510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestEnabledSessionExpiredState extends BaseClassForTests { private final Timing2 timing = new Timing2(); private CuratorFramework client; private BlockingQueue states; @BeforeEach @Override public void setup() throws Exception { super.setup(); client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .connectionTimeoutMs(timing.connection()) .sessionTimeoutMs(timing.session()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); states = Queues.newLinkedBlockingQueue(); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { states.add(newState); } }; client.getConnectionStateListenable().addListener(listener); } @AfterEach @Override public void teardown() throws Exception { try { CloseableUtils.closeQuietly(client); } finally { super.teardown(); } } @Test public void testResetCausesLost() throws Exception { assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED); client.checkExists().forPath("/"); // establish initial connection client.getZookeeperClient().reset(); assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST); assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.RECONNECTED); } @Test public void testInjectedWatchedEvent() throws Exception { assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED); final CountDownLatch latch = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if ( event.getType() == Event.EventType.None ) { if ( event.getState() == Event.KeeperState.Expired ) { latch.countDown(); } } } }; client.checkExists().usingWatcher(watcher).forPath("/"); server.stop(); assertTrue(timing.forSessionSleep().awaitLatch(latch)); } @Test public void testKillSession() throws Exception { assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertEquals(states.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST); assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.RECONNECTED); } @Test public void testReconnectWithoutExpiration() throws Exception { assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED); server.stop(); try { client.checkExists().forPath("/"); // any API call that will invoke the retry policy, etc. } catch ( KeeperException.ConnectionLossException ignore ) { } assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED); server.restart(); client.checkExists().forPath("/"); assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.RECONNECTED); } @Test public void testSessionExpirationFromTimeout() throws Exception { assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED); server.stop(); assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED); assertEquals(states.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST); } @Test public void testSessionExpirationFromTimeoutWithRestart() throws Exception { assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED); server.stop(); timing.forSessionSleep().sleep(); assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED); assertEquals(states.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST); server.restart(); client.checkExists().forPath("/"); assertEquals(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.RECONNECTED); assertNull(states.poll(timing.multiple(.5).milliseconds(), TimeUnit.MILLISECONDS)); // there should be no other events } } TestEnsureContainers.java000066400000000000000000000051301442004423600376020ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.EnsureContainers; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; public class TestEnsureContainers extends BaseClassForTests { @Test public void testBasic() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); EnsureContainers ensureContainers = new EnsureContainers(client, "/one/two/three"); ensureContainers.ensure(); assertNotNull(client.checkExists().forPath("/one/two/three")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSingleExecution() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); EnsureContainers ensureContainers = new EnsureContainers(client, "/one/two/three"); ensureContainers.ensure(); assertNotNull(client.checkExists().forPath("/one/two/three")); client.delete().forPath("/one/two/three"); ensureContainers.ensure(); assertNull(client.checkExists().forPath("/one/two/three")); } finally { CloseableUtils.closeQuietly(client); } } } TestExistsBuilder.java000066400000000000000000000104711442004423600371050ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.apache.zookeeper.ZooDefs.Ids.ANYONE_ID_UNSAFE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestExistsBuilder extends BaseClassForTests { /** * Tests that the ACL list provided to the exists builder is used for creating the parents, when it is applied to * parents. */ @Test public void testExistsWithParentsWithAclApplyToParents() throws Exception { CuratorFramework client = createClient(new DefaultACLProvider()); try { client.start(); String path = "/bar/foo/test"; List acl = Collections.singletonList(new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ, ANYONE_ID_UNSAFE)); assertNull(client.checkExists().creatingParentsIfNeeded().withACL(acl).forPath(path)); List actual_bar = client.getACL().forPath("/bar"); assertEquals(actual_bar, acl); List actual_bar_foo = client.getACL().forPath("/bar/foo"); assertEquals(actual_bar_foo, acl); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testExistsWithParentsWithAclApplyToParentsInBackground() throws Exception { CuratorFramework client = createClient(new DefaultACLProvider()); try { client.start(); final CountDownLatch latch = new CountDownLatch(1); String path = "/bar/foo/test"; List acl = Collections.singletonList(new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ, ANYONE_ID_UNSAFE)); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { latch.countDown(); } }; client.checkExists().creatingParentsIfNeeded().withACL(acl).inBackground(callback).forPath(path); assertTrue(latch.await(2000, TimeUnit.MILLISECONDS), "Callback not invoked"); List actual_bar = client.getACL().forPath("/bar"); assertEquals(actual_bar, acl); List actual_bar_foo = client.getACL().forPath("/bar/foo"); assertEquals(actual_bar_foo, acl); } finally { CloseableUtils.closeQuietly(client); } } private CuratorFramework createClient(ACLProvider aclProvider) { return CuratorFrameworkFactory.builder(). aclProvider(aclProvider). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); } } TestFailedDeleteManager.java000066400000000000000000000312571442004423600401260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.NoNodeException; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; public class TestFailedDeleteManager extends BaseClassForTests { @Test public void testLostSession() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); try { client.start(); client.create().forPath("/test-me"); final CountDownLatch latch = new CountDownLatch(1); final Semaphore semaphore = new Semaphore(0); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( (newState == ConnectionState.LOST) || (newState == ConnectionState.SUSPENDED) ) { semaphore.release(); } else if ( newState == ConnectionState.RECONNECTED ) { latch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); server.stop(); assertTrue(timing.acquireSemaphore(semaphore)); try { client.delete().guaranteed().forPath("/test-me"); fail(); } catch ( KeeperException.ConnectionLossException | KeeperException.SessionExpiredException e ) { // expected } assertTrue(timing.acquireSemaphore(semaphore)); timing.sleepABit(); server.restart(); assertTrue(timing.awaitLatch(latch)); timing.sleepABit(); assertNull(client.checkExists().forPath("/test-me")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testWithNamespaceAndLostSession() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()) .retryPolicy(new ExponentialBackoffRetry(100, 3)) .namespace("aisa") .build(); try { client.start(); client.create().forPath("/test-me"); final CountDownLatch latch = new CountDownLatch(1); final Semaphore semaphore = new Semaphore(0); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( (newState == ConnectionState.LOST) || (newState == ConnectionState.SUSPENDED) ) { semaphore.release(); } else if ( newState == ConnectionState.RECONNECTED ) { latch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); server.stop(); assertTrue(timing.acquireSemaphore(semaphore)); try { client.delete().guaranteed().forPath("/test-me"); fail(); } catch ( KeeperException.ConnectionLossException | KeeperException.SessionExpiredException e ) { // expected } assertTrue(timing.acquireSemaphore(semaphore)); timing.sleepABit(); server.restart(); assertTrue(timing.awaitLatch(latch)); timing.sleepABit(); assertNull(client.checkExists().forPath("/test-me")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testWithNamespaceAndLostSessionAlt() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()) .retryPolicy(new ExponentialBackoffRetry(100, 3)) .build(); try { client.start(); CuratorFramework namespaceClient = client.usingNamespace("foo"); namespaceClient.create().forPath("/test-me"); final CountDownLatch latch = new CountDownLatch(1); final Semaphore semaphore = new Semaphore(0); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( (newState == ConnectionState.LOST) || (newState == ConnectionState.SUSPENDED) ) { semaphore.release(); } else if ( newState == ConnectionState.RECONNECTED ) { latch.countDown(); } } }; namespaceClient.getConnectionStateListenable().addListener(listener); server.stop(); assertTrue(timing.acquireSemaphore(semaphore)); try { namespaceClient.delete().guaranteed().forPath("/test-me"); fail(); } catch ( KeeperException.ConnectionLossException | KeeperException.SessionExpiredException e ) { // expected } assertTrue(timing.acquireSemaphore(semaphore)); timing.sleepABit(); server.restart(); assertTrue(timing.awaitLatch(latch)); timing.sleepABit(); assertNull(namespaceClient.checkExists().forPath("/test-me")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { final String PATH = "/one/two/three"; Timing timing = new Timing(); CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).connectionTimeoutMs(timing.connection()).sessionTimeoutMs(timing.session()); CuratorFrameworkImpl client = new CuratorFrameworkImpl(builder); client.start(); try { client.create().creatingParentsIfNeeded().forPath(PATH); assertNotNull(client.checkExists().forPath(PATH)); server.stop(); // cause the next delete to fail try { client.delete().forPath(PATH); fail(); } catch ( KeeperException.ConnectionLossException | KeeperException.SessionExpiredException e ) { // expected } server.restart(); assertNotNull(client.checkExists().forPath(PATH)); server.stop(); // cause the next delete to fail try { client.delete().guaranteed().forPath(PATH); fail(); } catch ( KeeperException.ConnectionLossException | KeeperException.SessionExpiredException e ) { // expected } server.restart(); final int TRIES = 5; for ( int i = 0; i < TRIES; ++i ) { if ( client.checkExists().forPath(PATH) != null ) { timing.sleepABit(); } } assertNull(client.checkExists().forPath(PATH)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testGuaranteedDeleteOnNonExistentNodeInForeground() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); final AtomicBoolean pathAdded = new AtomicBoolean(false); ((CuratorFrameworkImpl)client).getFailedDeleteManager().debugListener = new FailedOperationManager.FailedOperationManagerListener() { @Override public void pathAddedForGuaranteedOperation(String path) { pathAdded.set(true); } }; try { client.delete().guaranteed().forPath("/nonexistent"); fail(); } catch(NoNodeException e) { //Exception is expected, the delete should not be retried assertFalse(pathAdded.get()); } finally { client.close(); } } @Test public void testGuaranteedDeleteOnNonExistentNodeInBackground() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); final AtomicBoolean pathAdded = new AtomicBoolean(false); ((CuratorFrameworkImpl)client).getFailedDeleteManager().debugListener = new FailedOperationManager.FailedOperationManagerListener() { @Override public void pathAddedForGuaranteedOperation(String path) { pathAdded.set(true); } }; final CountDownLatch backgroundLatch = new CountDownLatch(1); BackgroundCallback background = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { backgroundLatch.countDown(); } }; try { client.delete().guaranteed().inBackground(background).forPath("/nonexistent"); backgroundLatch.await(); //Exception is expected, the delete should not be retried assertFalse(pathAdded.get()); } finally { client.close(); } } } TestFramework.java000066400000000000000000001262631442004423600362630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.curator.framework.AuthInfo; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.EnsurePath; import org.apache.curator.utils.ZKPaths; import org.apache.curator.utils.ZookeeperFactory; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; @SuppressWarnings("deprecation") @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestFramework extends BaseClassForTests { private final Logger log = LoggerFactory.getLogger(getClass()); @BeforeEach @Override public void setup() throws Exception { System.setProperty("znode.container.checkIntervalMs", "1000"); super.setup(); } @AfterEach @Override public void teardown() throws Exception { System.clearProperty("znode.container.checkIntervalMs"); super.teardown(); } public void testWaitForShutdownTimeoutMs() throws Exception { final BlockingQueue timeoutQueue = new ArrayBlockingQueue<>(1); ZookeeperFactory zookeeperFactory = new ZookeeperFactory() { @Override public ZooKeeper newZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws IOException { return new ZooKeeper(connectString, sessionTimeout, watcher, canBeReadOnly) { @Override public boolean close(int waitForShutdownTimeoutMs) throws InterruptedException { timeoutQueue.add(waitForShutdownTimeoutMs); return super.close(waitForShutdownTimeoutMs); } }; } }; CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .zookeeperFactory(zookeeperFactory) .waitForShutdownTimeoutMs(10064) .build(); try { client.start(); client.checkExists().forPath("/foo"); } finally { CloseableUtils.closeQuietly(client); } Integer polledValue = timeoutQueue.poll(new Timing().milliseconds(), TimeUnit.MILLISECONDS); assertNotNull(polledValue); assertEquals(10064, polledValue.intValue()); } @Test public void testSessionLossWithLongTimeout() throws Exception { final Timing timing = new Timing(); try(final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.forWaiting().milliseconds(), timing.connection(), new RetryOneTime(1))) { final CountDownLatch connectedLatch = new CountDownLatch(1); final CountDownLatch lostLatch = new CountDownLatch(1); final CountDownLatch restartedLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.CONNECTED ) { connectedLatch.countDown(); } else if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } else if ( newState == ConnectionState.RECONNECTED ) { restartedLatch.countDown(); } } }); client.start(); assertTrue(timing.awaitLatch(connectedLatch)); server.stop(); timing.sleepABit(); assertTrue(timing.awaitLatch(lostLatch)); server.restart(); assertTrue(timing.awaitLatch(restartedLatch)); } } @Test public void testConnectionState() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { final BlockingQueue queue = new LinkedBlockingQueue(); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { queue.add(newState); } }; client.getConnectionStateListenable().addListener(listener); client.start(); assertEquals(queue.poll(timing.multiple(4).seconds(), TimeUnit.SECONDS), ConnectionState.CONNECTED); server.stop(); assertEquals(queue.poll(timing.multiple(4).seconds(), TimeUnit.SECONDS), ConnectionState.SUSPENDED); assertEquals(queue.poll(timing.multiple(4).seconds(), TimeUnit.SECONDS), ConnectionState.LOST); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateOrSetData() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); String name = client.create().forPath("/hey", "there".getBytes()); assertEquals(name, "/hey"); name = client.create().orSetData().forPath("/hey", "other".getBytes()); assertEquals(name, "/hey"); assertArrayEquals(client.getData().forPath("/hey"), "other".getBytes()); name = client.create().orSetData().creatingParentsIfNeeded().forPath("/a/b/c", "there".getBytes()); assertEquals(name, "/a/b/c"); name = client.create().orSetData().creatingParentsIfNeeded().forPath("/a/b/c", "what".getBytes()); assertEquals(name, "/a/b/c"); assertArrayEquals(client.getData().forPath("/a/b/c"), "what".getBytes()); final BlockingQueue queue = new LinkedBlockingQueue<>(); BackgroundCallback backgroundCallback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { queue.add(event); } }; client.create().orSetData().inBackground(backgroundCallback).forPath("/a/b/c", "another".getBytes()); CuratorEvent event = queue.poll(new Timing().milliseconds(), TimeUnit.MILLISECONDS); assertNotNull(event); assertEquals(event.getResultCode(), KeeperException.Code.OK.intValue()); assertEquals(event.getType(), CuratorEventType.CREATE); assertEquals(event.getPath(), "/a/b/c"); assertEquals(event.getName(), "/a/b/c"); // callback should only be called once CuratorEvent unexpectedEvent = queue.poll(new Timing().milliseconds(), TimeUnit.MILLISECONDS); assertNull(unexpectedEvent); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testNamespaceWithWatcher() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).namespace("aisa").retryPolicy(new RetryOneTime(1)).build(); client.start(); try { final BlockingQueue queue = new LinkedBlockingQueue(); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { try { queue.put(event.getPath()); } catch ( InterruptedException e ) { throw new Error(e); } } }; client.create().forPath("/base"); client.getChildren().usingWatcher(watcher).forPath("/base"); client.create().forPath("/base/child"); String path = new Timing2().takeFromQueue(queue); assertEquals(path, "/base"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testNamespaceInBackground() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).namespace("aisa").retryPolicy(new RetryOneTime(1)).build(); client.start(); try { final BlockingQueue queue = new LinkedBlockingQueue(); CuratorListener listener = new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.EXISTS ) { queue.put(event.getPath()); } } }; client.getCuratorListenable().addListener(listener); client.create().forPath("/base"); client.checkExists().inBackground().forPath("/base"); String path = queue.poll(10, TimeUnit.SECONDS); assertEquals(path, "/base"); client.getCuratorListenable().removeListener(listener); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { queue.put(event.getPath()); } }; client.getChildren().inBackground(callback).forPath("/base"); path = queue.poll(10, TimeUnit.SECONDS); assertEquals(path, "/base"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateACLSingleAuth() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder .connectString(server.getConnectString()) .authorization("digest", "me1:pass1".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { ACL acl = new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.AUTH_IDS); List aclList = Lists.newArrayList(acl); client.create().withACL(aclList).forPath("/test", "test".getBytes()); client.close(); // Try setting data with me1:pass1 client = builder .connectString(server.getConnectString()) .authorization("digest", "me1:pass1".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { client.setData().forPath("/test", "test".getBytes()); } catch ( KeeperException.NoAuthException e ) { fail("Auth failed"); } client.close(); // Try setting data with something:else client = builder .connectString(server.getConnectString()) .authorization("digest", "something:else".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { client.setData().forPath("/test", "test".getBytes()); fail("Should have failed with auth exception"); } catch ( KeeperException.NoAuthException e ) { // expected } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testACLDeprecatedApis() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)); assertNull(builder.getAuthScheme()); assertNull(builder.getAuthValue()); builder = builder.authorization("digest", "me1:pass1".getBytes()); assertEquals(builder.getAuthScheme(), "digest"); assertArrayEquals(builder.getAuthValue(), "me1:pass1".getBytes()); } @Test public void testCreateACLMultipleAuths() throws Exception { // Add a few authInfos List authInfos = new ArrayList(); authInfos.add(new AuthInfo("digest", "me1:pass1".getBytes())); authInfos.add(new AuthInfo("digest", "me2:pass2".getBytes())); CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder .connectString(server.getConnectString()) .authorization(authInfos) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { ACL acl = new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.AUTH_IDS); List aclList = Lists.newArrayList(acl); client.create().withACL(aclList).forPath("/test", "test".getBytes()); client.close(); // Try setting data with me1:pass1 client = builder .connectString(server.getConnectString()) .authorization("digest", "me1:pass1".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { client.setData().forPath("/test", "test".getBytes()); } catch ( KeeperException.NoAuthException e ) { fail("Auth failed"); } client.close(); // Try setting data with me1:pass1 client = builder .connectString(server.getConnectString()) .authorization("digest", "me2:pass2".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { client.setData().forPath("/test", "test".getBytes()); } catch ( KeeperException.NoAuthException e ) { fail("Auth failed"); } client.close(); // Try setting data with something:else client = builder .connectString(server.getConnectString()) .authorization("digest", "something:else".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { client.setData().forPath("/test", "test".getBytes()); fail("Should have failed with auth exception"); } catch ( KeeperException.NoAuthException e ) { // expected } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateACLWithReset() throws Exception { Timing timing = new Timing(); CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder .connectString(server.getConnectString()) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()) .authorization("digest", "me:pass".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { final CountDownLatch lostLatch = new CountDownLatch(1); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); ACL acl = new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.AUTH_IDS); List aclList = Lists.newArrayList(acl); client.create().withACL(aclList).forPath("/test", "test".getBytes()); server.stop(); assertTrue(timing.awaitLatch(lostLatch)); try { client.checkExists().forPath("/"); fail("Connection should be down"); } catch ( KeeperException.ConnectionLossException e ) { // expected } server.restart(); try { client.setData().forPath("/test", "test".getBytes()); } catch ( KeeperException.NoAuthException e ) { fail("Auth failed"); } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateParents() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build(); client.start(); try { client.create().creatingParentsIfNeeded().forPath("/one/two/three", "foo".getBytes()); byte[] data = client.getData().forPath("/one/two/three"); assertArrayEquals(data, "foo".getBytes()); client.create().creatingParentsIfNeeded().forPath("/one/two/another", "bar".getBytes()); data = client.getData().forPath("/one/two/another"); assertArrayEquals(data, "bar".getBytes()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testOverrideCreateParentContainers() throws Exception { if ( !checkForContainers() ) { return; } CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .dontUseContainerParents() .build(); try { client.start(); client.create().creatingParentContainersIfNeeded().forPath("/one/two/three", "foo".getBytes()); byte[] data = client.getData().forPath("/one/two/three"); assertArrayEquals(data, "foo".getBytes()); client.delete().forPath("/one/two/three"); new Timing().sleepABit(); assertNotNull(client.checkExists().forPath("/one/two")); new Timing().sleepABit(); assertNotNull(client.checkExists().forPath("/one")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateParentContainers() throws Exception { if ( !checkForContainers() ) { return; } CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build(); try { client.start(); client.create().creatingParentContainersIfNeeded().forPath("/one/two/three", "foo".getBytes()); byte[] data = client.getData().forPath("/one/two/three"); assertArrayEquals(data, "foo".getBytes()); client.delete().forPath("/one/two/three"); new Timing().sleepABit(); assertNull(client.checkExists().forPath("/one/two")); new Timing().sleepABit(); assertNull(client.checkExists().forPath("/one")); } finally { CloseableUtils.closeQuietly(client); } } private boolean checkForContainers() { if ( ZKPaths.getContainerCreateMode() == CreateMode.PERSISTENT ) { System.out.println("Not using CreateMode.CONTAINER enabled version of ZooKeeper"); return false; } return true; } @Test public void testCreatingParentsTheSame() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); assertNull(client.checkExists().forPath("/one/two")); client.create().creatingParentContainersIfNeeded().forPath("/one/two/three"); assertNotNull(client.checkExists().forPath("/one/two")); client.delete().deletingChildrenIfNeeded().forPath("/one"); assertNull(client.checkExists().forPath("/one")); assertNull(client.checkExists().forPath("/one/two")); client.checkExists().creatingParentContainersIfNeeded().forPath("/one/two/three"); assertNotNull(client.checkExists().forPath("/one/two")); assertNull(client.checkExists().forPath("/one/two/three")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testExistsCreatingParents() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); assertNull(client.checkExists().forPath("/one/two")); client.checkExists().creatingParentContainersIfNeeded().forPath("/one/two/three"); assertNotNull(client.checkExists().forPath("/one/two")); assertNull(client.checkExists().forPath("/one/two/three")); assertNull(client.checkExists().creatingParentContainersIfNeeded().forPath("/one/two/three")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testExistsCreatingParentsInBackground() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); assertNull(client.checkExists().forPath("/one/two")); final CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { latch.countDown(); } }; client.checkExists().creatingParentContainersIfNeeded().inBackground(callback).forPath("/one/two/three"); assertTrue(new Timing().awaitLatch(latch)); assertNotNull(client.checkExists().forPath("/one/two")); assertNull(client.checkExists().forPath("/one/two/three")); assertNull(client.checkExists().creatingParentContainersIfNeeded().forPath("/one/two/three")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testEnsurePathWithNamespace() throws Exception { final String namespace = "jz"; CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace(namespace).build(); client.start(); try { EnsurePath ensurePath = new EnsurePath("/pity/the/fool"); ensurePath.ensure(client.getZookeeperClient()); assertNull(client.getZookeeperClient().getZooKeeper().exists("/jz/pity/the/fool", false)); ensurePath = client.newNamespaceAwareEnsurePath("/pity/the/fool"); ensurePath.ensure(client.getZookeeperClient()); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/jz/pity/the/fool", false)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateContainersWithNamespace() throws Exception { final String namespace = "container1"; CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace(namespace).build(); try { client.start(); String path = "/path1/path2"; client.createContainers(path); assertNotNull(client.checkExists().forPath(path)); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/" + namespace + path, false)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateContainersUsingNamespace() throws Exception { final String namespace = "container2"; CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build(); try { client.start(); CuratorFramework nsClient = client.usingNamespace(namespace); String path = "/path1/path2"; nsClient.createContainers(path); assertNotNull(nsClient.checkExists().forPath(path)); assertNotNull(nsClient.getZookeeperClient().getZooKeeper().exists("/" + namespace + path, false)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testNamespace() throws Exception { final String namespace = "TestNamespace"; CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace(namespace).build(); client.start(); try { String actualPath = client.create().forPath("/test"); assertEquals(actualPath, "/test"); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/" + namespace + "/test", false)); assertNull(client.getZookeeperClient().getZooKeeper().exists("/test", false)); actualPath = client.usingNamespace(null).create().forPath("/non"); assertEquals(actualPath, "/non"); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/non", false)); client.create().forPath("/test/child", "hey".getBytes()); byte[] bytes = client.getData().forPath("/test/child"); assertArrayEquals(bytes, "hey".getBytes()); bytes = client.usingNamespace(null).getData().forPath("/" + namespace + "/test/child"); assertArrayEquals(bytes, "hey".getBytes()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCustomCallback() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.CREATE ) { if ( event.getPath().equals("/head") ) { latch.countDown(); } } } }; client.create().inBackground(callback).forPath("/head"); assertTrue(latch.await(10, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSync() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.getCuratorListenable().addListener ( new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.SYNC ) { assertEquals(event.getPath(), "/head"); ((CountDownLatch)event.getContext()).countDown(); } } } ); client.create().forPath("/head"); assertNotNull(client.checkExists().forPath("/head")); CountDownLatch latch = new CountDownLatch(1); client.sync("/head", latch); assertTrue(latch.await(10, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSyncNew() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/head"); assertNotNull(client.checkExists().forPath("/head")); final CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.SYNC ) { latch.countDown(); } } }; client.sync().inBackground(callback).forPath("/head"); assertTrue(latch.await(10, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testGetSequentialChildren() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/head"); for ( int i = 0; i < 10; ++i ) { client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/head/child"); } List children = client.getChildren().forPath("/head"); assertEquals(children.size(), 10); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundGetDataWithWatch() throws Exception { final byte[] data1 = {1, 2, 3}; final byte[] data2 = {4, 5, 6, 7}; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final CountDownLatch watchedLatch = new CountDownLatch(1); client.getCuratorListenable().addListener ( new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.GET_DATA ) { assertEquals(event.getPath(), "/test"); assertArrayEquals(event.getData(), data1); ((CountDownLatch)event.getContext()).countDown(); } else if ( event.getType() == CuratorEventType.WATCHED ) { if ( event.getWatchedEvent().getType() == Watcher.Event.EventType.NodeDataChanged ) { assertEquals(event.getPath(), "/test"); watchedLatch.countDown(); } } } } ); client.create().forPath("/test", data1); CountDownLatch backgroundLatch = new CountDownLatch(1); client.getData().watched().inBackground(backgroundLatch).forPath("/test"); assertTrue(backgroundLatch.await(10, TimeUnit.SECONDS)); client.setData().forPath("/test", data2); assertTrue(watchedLatch.await(10, TimeUnit.SECONDS)); byte[] checkData = client.getData().forPath("/test"); assertArrayEquals(checkData, data2); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundCreate() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.getCuratorListenable().addListener ( new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.CREATE ) { assertEquals(event.getPath(), "/test"); ((CountDownLatch)event.getContext()).countDown(); } } } ); CountDownLatch latch = new CountDownLatch(1); client.create().inBackground(latch).forPath("/test", new byte[]{1, 2, 3}); assertTrue(latch.await(10, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundPathWithNamespace() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); try (CuratorFramework client = builder .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .build()) { client.start(); CuratorFramework namespaceZoo = client.usingNamespace("zoo"); BlockingQueue events = new LinkedBlockingQueue<>(); BackgroundCallback callback = (CuratorFramework ignored, CuratorEvent event) -> { events.add(event); }; namespaceZoo.create().creatingParentsIfNeeded().inBackground(callback).forPath("/zoo/a"); CuratorEvent event = events.poll(10, TimeUnit.SECONDS); assertNotNull(event); assertEquals("/zoo/a", event.getPath()); assertEquals("/zoo/a", event.getName()); client.checkExists().inBackground(callback).forPath("/zoo/zoo/a"); event = events.poll(10, TimeUnit.SECONDS); assertNotNull(event); assertEquals("/zoo/zoo/a", event.getPath()); } } @Test public void testCreateModes() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { byte[] writtenBytes = {1, 2, 3}; client.create().forPath("/test", writtenBytes); // should be persistent client.close(); client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); byte[] readBytes = client.getData().forPath("/test"); assertArrayEquals(writtenBytes, readBytes); client.create().withMode(CreateMode.EPHEMERAL).forPath("/ghost", writtenBytes); client.close(); client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); readBytes = client.getData().forPath("/test"); assertArrayEquals(writtenBytes, readBytes); Stat stat = client.checkExists().forPath("/ghost"); assertNull(stat); String realPath = client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/pseq", writtenBytes); assertNotSame(realPath, "/pseq"); client.close(); client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); readBytes = client.getData().forPath(realPath); assertArrayEquals(writtenBytes, readBytes); realPath = client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/eseq", writtenBytes); assertNotSame(realPath, "/eseq"); client.close(); client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); stat = client.checkExists().forPath(realPath); assertNull(stat); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testConfigurableZookeeper() throws Exception { CuratorFramework client = null; try { ZKClientConfig zkClientConfig = new ZKClientConfig(); String zookeeperRequestTimeout = "30000"; zkClientConfig.setProperty(ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT, zookeeperRequestTimeout); client = CuratorFrameworkFactory.newClient(server.getConnectString(), 30000, 30000, new RetryOneTime(1), zkClientConfig); client.start(); byte[] writtenBytes = {1, 2, 3}; client.create().forPath("/test", writtenBytes); byte[] readBytes = client.getData().forPath("/test"); assertArrayEquals(writtenBytes, readBytes); assertEquals(zookeeperRequestTimeout, client.getZookeeperClient().getZooKeeper().getClientConfig().getProperty(ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT)); } catch (NoSuchMethodError e) { log.debug("NoSuchMethodError: ", e); log.info("Got NoSuchMethodError, meaning probably this cannot be used with ZooKeeper version < 3.6.1"); } finally { try { CloseableUtils.closeQuietly(client); } catch (NoSuchMethodError e) { log.debug("close: NoSuchMethodError: ", e); log.info("close: Got NoSuchMethodError, meaning probably this cannot be used with ZooKeeper version < 3.6.1"); } } } @Test public void testSimple() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { String path = client.create().withMode(CreateMode.PERSISTENT).forPath("/test", new byte[]{1, 2, 3}); assertEquals(path, "/test"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSequentialWithTrailingSeparator() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/test"); //This should create a node in the form of "/test/00000001" String path = client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/test/"); assertTrue(path.startsWith("/test/")); } finally { CloseableUtils.closeQuietly(client); } } } TestFrameworkBackground.java000066400000000000000000000322171442004423600402560ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.data.ACL; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; public class TestFrameworkBackground extends BaseClassForTests { private final Logger log = LoggerFactory.getLogger(getClass()); @Test public void testErrorListener() throws Exception { //The first call to the ACL provider will return a reasonable //value. The second will throw an error. This is because the ACL //provider is accessed prior to the backgrounding call. final AtomicBoolean aclProviderCalled = new AtomicBoolean(false); ACLProvider badAclProvider = new ACLProvider() { @Override public List getDefaultAcl() { if(aclProviderCalled.getAndSet(true)) { throw new UnsupportedOperationException(); } else { return new ArrayList<>(); } } @Override public List getAclForPath(String path) { if(aclProviderCalled.getAndSet(true)) { throw new UnsupportedOperationException(); } else { return new ArrayList<>(); } } }; CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .aclProvider(badAclProvider) .build(); try { client.start(); final CountDownLatch errorLatch = new CountDownLatch(1); UnhandledErrorListener listener = new UnhandledErrorListener() { @Override public void unhandledError(String message, Throwable e) { if ( e instanceof UnsupportedOperationException ) { errorLatch.countDown(); } } }; client.create().inBackground().withUnhandledErrorListener(listener).forPath("/foo"); assertTrue(new Timing().awaitLatch(errorLatch)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testListenerConnectedAtStart() throws Exception { server.stop(); Timing timing = new Timing(2); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryNTimes(0, 0)); try { client.start(); final CountDownLatch connectedLatch = new CountDownLatch(1); final AtomicBoolean firstListenerAction = new AtomicBoolean(true); final AtomicReference firstListenerState = new AtomicReference(); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( firstListenerAction.compareAndSet(true, false) ) { firstListenerState.set(newState); System.out.println("First listener state is " + newState); } if ( newState == ConnectionState.CONNECTED ) { connectedLatch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); // due to CURATOR-72, this was causing a LOST event to precede the CONNECTED event client.create().inBackground().forPath("/foo"); server.restart(); assertTrue(timing.awaitLatch(connectedLatch)); assertFalse(firstListenerAction.get()); ConnectionState firstconnectionState = firstListenerState.get(); assertEquals(firstconnectionState, ConnectionState.CONNECTED, "First listener state MUST BE CONNECTED but is " + firstconnectionState); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRetries() throws Exception { final int SLEEP = 1000; final int TIMES = 5; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryNTimes(TIMES, SLEEP)); try { client.start(); client.getZookeeperClient().blockUntilConnectedOrTimedOut(); final CountDownLatch latch = new CountDownLatch(TIMES); final List times = Lists.newArrayList(); final AtomicLong start = new AtomicLong(System.currentTimeMillis()); ((CuratorFrameworkImpl)client).debugListener = new CuratorFrameworkImpl.DebugBackgroundListener() { @Override public void listen(OperationAndData data) { if ( data.getOperation().getClass().getName().contains("CreateBuilderImpl") ) { long now = System.currentTimeMillis(); times.add(now - start.get()); start.set(now); latch.countDown(); } } }; server.stop(); client.create().inBackground().forPath("/one"); latch.await(); for ( long elapsed : times.subList(1, times.size()) ) // first one isn't a retry { assertTrue(elapsed >= SLEEP, elapsed + ": " + times); } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final BlockingQueue paths = Queues.newLinkedBlockingQueue(); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { paths.add(event.getPath()); } }; client.create().inBackground(callback).forPath("/one"); assertEquals(paths.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), "/one"); client.create().inBackground(callback).forPath("/one/two"); assertEquals(paths.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), "/one/two"); client.create().inBackground(callback).forPath("/one/two/three"); assertEquals(paths.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), "/one/two/three"); } finally { CloseableUtils.closeQuietly(client); } } /** * Attempt a background operation while Zookeeper server is down. * Return code must be {@link Code#CONNECTIONLOSS} */ @Test public void testCuratorCallbackOnError() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).sessionTimeoutMs(timing.session()).connectionTimeoutMs(timing.connection()).retryPolicy(new RetryOneTime(1000)).build(); final CountDownLatch latch = new CountDownLatch(1); try { client.start(); BackgroundCallback curatorCallback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getResultCode() == Code.CONNECTIONLOSS.intValue() ) { latch.countDown(); } } }; // Stop the Zookeeper server server.stop(); // Attempt to retrieve children list client.getChildren().inBackground(curatorCallback).forPath("/"); // Check if the callback has been called with a correct return code assertTrue(timing.awaitLatch(latch), "Callback has not been called by curator !"); } finally { client.close(); } } /** * CURATOR-126 * Shutdown the Curator client while there are still background operations running. */ @Test public void testShutdown() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory .builder() .connectString(server.getConnectString()) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()).retryPolicy(new RetryOneTime(1)) .maxCloseWaitMs(timing.forWaiting().milliseconds()) .build(); try { final AtomicBoolean hadIllegalStateException = new AtomicBoolean(false); ((CuratorFrameworkImpl)client).debugUnhandledErrorListener = new UnhandledErrorListener() { @Override public void unhandledError(String message, Throwable e) { if ( e instanceof IllegalStateException ) { hadIllegalStateException.set(true); } } }; client.start(); final CountDownLatch operationReadyLatch = new CountDownLatch(1); ((CuratorFrameworkImpl)client).debugListener = new CuratorFrameworkImpl.DebugBackgroundListener() { @Override public void listen(OperationAndData data) { try { operationReadyLatch.await(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } } }; // queue a background operation that will block due to the debugListener client.create().inBackground().forPath("/hey"); timing.sleepABit(); // close the client while the background is still blocked client.close(); // unblock the background operationReadyLatch.countDown(); timing.sleepABit(); // should not generate an exception assertFalse(hadIllegalStateException.get()); } finally { CloseableUtils.closeQuietly(client); } } } TestFrameworkEdges.java000066400000000000000000001011641442004423600372240ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Queues; import org.apache.curator.RetryPolicy; import org.apache.curator.RetrySleeper; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CreateBuilder; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.api.ErrorListenerPathAndBytesable; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryForever; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.TestingServer; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestFrameworkEdges extends BaseClassForTests { private final Logger log = LoggerFactory.getLogger(getClass()); private final Timing2 timing = new Timing2(); @BeforeAll public static void setUpClass() { System.setProperty("zookeeper.extendedTypesEnabled", "true"); } @Test @DisplayName("test case for CURATOR-525") public void testValidateConnectionEventRaces() throws Exception { // test for CURATOR-525 - there is a race whereby Curator can go to LOST // after the connection has been repaired. Prior to the fix, the Curator // instance would become a zombie, never leaving the LOST state try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), 2000, 1000, new RetryOneTime(1))) { CuratorFrameworkImpl clientImpl = (CuratorFrameworkImpl)client; client.start(); client.getChildren().forPath("/"); client.create().forPath("/foo"); BlockingQueue stateQueue = new LinkedBlockingQueue<>(); client.getConnectionStateListenable().addListener((__, newState) -> stateQueue.add(newState)); server.stop(); assertEquals(timing.takeFromQueue(stateQueue), ConnectionState.SUSPENDED); assertEquals(timing.takeFromQueue(stateQueue), ConnectionState.LOST); clientImpl.debugCheckBackgroundRetryReadyLatch = new CountDownLatch(1); clientImpl.debugCheckBackgroundRetryLatch = new CountDownLatch(1); client.delete().guaranteed().inBackground().forPath("/foo"); timing.awaitLatch(clientImpl.debugCheckBackgroundRetryReadyLatch); server.restart(); assertEquals(timing.takeFromQueue(stateQueue), ConnectionState.RECONNECTED); clientImpl.injectedCode = KeeperException.Code.SESSIONEXPIRED; // simulate an expiration being handled after the connection is repaired clientImpl.debugCheckBackgroundRetryLatch.countDown(); assertEquals(timing.takeFromQueue(stateQueue), ConnectionState.LOST); assertEquals(timing.takeFromQueue(stateQueue), ConnectionState.RECONNECTED); } } @Test public void testInjectSessionExpiration() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) { client.start(); CountDownLatch expiredLatch = new CountDownLatch(1); Watcher watcher = event -> { if ( event.getState() == Watcher.Event.KeeperState.Expired ) { expiredLatch.countDown(); } }; client.checkExists().usingWatcher(watcher).forPath("/foobar"); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertTrue(timing.awaitLatch(expiredLatch)); } } @Test public void testProtectionWithKilledSession() throws Exception { server.stop(); // not needed // see CURATOR-498 // attempt to re-create the state described in the bug report: create a 3 Instance ensemble; // have Curator connect to only 1 one of those instances; set failNextCreateForTesting to // simulate protection mode searching; kill the connected server when this happens; // wait for session timeout to elapse and then restart the instance. In most cases // this will cause the scenario as Curator will send the session cancel and do protection mode // search around the same time. The protection mode search should return first as it can be resolved // by the Instance Curator is connected to but the session kill needs a quorum vote (it's a // transaction) try (TestingCluster cluster = createAndStartCluster(3)) { InstanceSpec instanceSpec0 = cluster.getServers().get(0).getInstanceSpec(); CountDownLatch serverStoppedLatch = new CountDownLatch(1); RetryPolicy retryPolicy = new RetryForever(100) { @Override public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper) { if ( serverStoppedLatch.getCount() > 0 ) { try { cluster.killServer(instanceSpec0); } catch ( Exception e ) { // ignore } serverStoppedLatch.countDown(); } return super.allowRetry(retryCount, elapsedTimeMs, sleeper); } }; try (CuratorFramework client = CuratorFrameworkFactory.newClient(instanceSpec0.getConnectString(), timing.session(), timing.connection(), retryPolicy)) { BlockingQueue createdNode = new LinkedBlockingQueue<>(); BackgroundCallback callback = (__, event) -> { if ( event.getType() == CuratorEventType.CREATE ) { createdNode.offer(event.getPath()); } }; client.start(); client.create().forPath("/test"); ErrorListenerPathAndBytesable builder = client.create().withProtection().withMode(CreateMode.EPHEMERAL).inBackground(callback); ((CreateBuilderImpl)builder).failNextCreateForTesting = true; builder.forPath("/test/hey"); assertTrue(timing.awaitLatch(serverStoppedLatch)); timing.forSessionSleep().sleep(); // wait for session to expire cluster.restartServer(instanceSpec0); String path = timing.takeFromQueue(createdNode); List children = client.getChildren().forPath("/test"); assertEquals(Collections.singletonList(ZKPaths.getNodeFromPath(path)), children); } } } @Test public void testBackgroundLatencyUnSleep() throws Exception { server.stop(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); ((CuratorFrameworkImpl)client).sleepAndQueueOperationSeconds = Integer.MAX_VALUE; final CountDownLatch latch = new CountDownLatch(3); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( (event.getType() == CuratorEventType.CREATE) && (event.getResultCode() == KeeperException.Code.OK.intValue()) ) { latch.countDown(); } } }; // queue multiple operations for a more complete test client.create().inBackground(callback).forPath("/test"); client.create().inBackground(callback).forPath("/test/one"); client.create().inBackground(callback).forPath("/test/two"); server.restart(); assertTrue(timing.awaitLatch(latch)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateContainersForBadConnect() throws Exception { final int serverPort = server.getPort(); server.close(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), 1000, 1000, new RetryNTimes(10, timing.forSleepingABit().milliseconds())); try { new Thread() { @Override public void run() { try { Thread.sleep(3000); server = new TestingServer(serverPort, true); } catch ( Exception e ) { e.printStackTrace(); } } }.start(); client.start(); client.createContainers("/this/does/not/exist"); assertNotNull(client.checkExists().forPath("/this/does/not/exist")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testQuickClose() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), 1, new RetryNTimes(0, 0)); try { client.start(); client.close(); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testProtectedCreateNodeDeletion() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), 1, new RetryNTimes(0, 0)); try { client.start(); for ( int i = 0; i < 2; ++i ) { CuratorFramework localClient = (i == 0) ? client : client.usingNamespace("nm"); localClient.create().forPath("/parent"); assertEquals(localClient.getChildren().forPath("/parent").size(), 0); CreateBuilderImpl createBuilder = (CreateBuilderImpl)localClient.create(); createBuilder.failNextCreateForTesting = true; FindAndDeleteProtectedNodeInBackground.debugInsertError.set(true); try { createBuilder.withProtection().forPath("/parent/test"); fail("failNextCreateForTesting should have caused a ConnectionLossException"); } catch ( KeeperException.ConnectionLossException e ) { // ignore, correct } timing.sleepABit(); List children = localClient.getChildren().forPath("/parent"); assertEquals(children.size(), 0, children.toString()); // protected mode should have deleted the node localClient.delete().forPath("/parent"); } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testPathsFromProtectingInBackground() throws Exception { for ( CreateMode mode : CreateMode.values() ) { internalTestPathsFromProtectingInBackground(mode); } } private void internalTestPathsFromProtectingInBackground(CreateMode mode) throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), 1, new RetryOneTime(1)); try { client.start(); client.create().creatingParentsIfNeeded().forPath("/a/b/c"); final BlockingQueue paths = new ArrayBlockingQueue(2); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { paths.put(event.getName()); paths.put(event.getPath()); } }; final String TEST_PATH = "/a/b/c/test-"; long ttl = timing.forWaiting().milliseconds() * 1000; CreateBuilder firstCreateBuilder = client.create(); if ( mode.isTTL() ) { firstCreateBuilder.withTtl(ttl); } firstCreateBuilder.withMode(mode).inBackground(callback).forPath(TEST_PATH); String name1 = timing.takeFromQueue(paths); String path1 = timing.takeFromQueue(paths); client.close(); client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), 1, new RetryOneTime(1)); client.start(); CreateBuilderImpl createBuilder = (CreateBuilderImpl)client.create(); createBuilder.withProtection(); if ( mode.isTTL() ) { createBuilder.withTtl(ttl); } client.create().forPath(createBuilder.adjustPath(TEST_PATH)); createBuilder.debugForceFindProtectedNode = true; createBuilder.withMode(mode).inBackground(callback).forPath(TEST_PATH); String name2 = timing.takeFromQueue(paths); String path2 = timing.takeFromQueue(paths); assertEquals(ZKPaths.getPathAndNode(name1).getPath(), ZKPaths.getPathAndNode(TEST_PATH).getPath()); assertEquals(ZKPaths.getPathAndNode(name2).getPath(), ZKPaths.getPathAndNode(TEST_PATH).getPath()); assertEquals(ZKPaths.getPathAndNode(path1).getPath(), ZKPaths.getPathAndNode(TEST_PATH).getPath()); assertEquals(ZKPaths.getPathAndNode(path2).getPath(), ZKPaths.getPathAndNode(TEST_PATH).getPath()); client.delete().deletingChildrenIfNeeded().forPath("/a/b/c"); client.delete().forPath("/a/b"); client.delete().forPath("/a"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void connectionLossWithBackgroundTest() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), 1, new RetryOneTime(1)); try { final CountDownLatch latch = new CountDownLatch(1); client.start(); client.getZookeeperClient().blockUntilConnectedOrTimedOut(); server.close(); client.getChildren().inBackground(new BackgroundCallback() { public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { latch.countDown(); } }).forPath("/"); assertTrue(timing.awaitLatch(latch)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testReconnectAfterLoss() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final CountDownLatch lostLatch = new CountDownLatch(1); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); client.checkExists().forPath("/"); server.stop(); assertTrue(timing.awaitLatch(lostLatch)); try { client.checkExists().forPath("/"); fail(); } catch ( KeeperException.ConnectionLossException e ) { // correct } server.restart(); client.checkExists().forPath("/"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testGetAclNoStat() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { try { client.getACL().forPath("/"); } catch ( NullPointerException e ) { fail(); } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testMissedResponseOnBackgroundESCreate() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { CreateBuilderImpl createBuilder = (CreateBuilderImpl)client.create(); createBuilder.failNextCreateForTesting = true; final BlockingQueue queue = Queues.newArrayBlockingQueue(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { queue.put(event.getPath()); } }; createBuilder.withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).inBackground(callback).forPath("/"); String ourPath = queue.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertTrue(ourPath.startsWith(ZKPaths.makePath("/", ProtectedUtils.PROTECTED_PREFIX))); assertFalse(createBuilder.failNextCreateForTesting); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testMissedResponseOnESCreate() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { CreateBuilderImpl createBuilder = (CreateBuilderImpl)client.create(); createBuilder.failNextCreateForTesting = true; String ourPath = createBuilder.withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/"); assertTrue(ourPath.startsWith(ZKPaths.makePath("/", ProtectedUtils.PROTECTED_PREFIX))); assertFalse(createBuilder.failNextCreateForTesting); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSessionKilled() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/sessionTest"); CountDownLatch sessionDiedLatch = new CountDownLatch(1); Watcher watcher = event -> { if ( event.getState() == Watcher.Event.KeeperState.Expired ) { sessionDiedLatch.countDown(); } }; client.checkExists().usingWatcher(watcher).forPath("/sessionTest"); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertTrue(timing.awaitLatch(sessionDiedLatch)); assertNotNull(client.checkExists().forPath("/sessionTest")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testNestedCalls() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.getCuratorListenable().addListener(new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.EXISTS ) { Stat stat = client.checkExists().forPath("/yo/yo/yo"); assertNull(stat); client.create().inBackground(event.getContext()).forPath("/what"); } else if ( event.getType() == CuratorEventType.CREATE ) { ((CountDownLatch)event.getContext()).countDown(); } } }); CountDownLatch latch = new CountDownLatch(1); client.checkExists().inBackground(latch).forPath("/hey"); assertTrue(latch.await(10, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundFailure() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final CountDownLatch latch = new CountDownLatch(1); client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { latch.countDown(); } } }); client.checkExists().forPath("/hey"); client.checkExists().inBackground().forPath("/hey"); server.stop(); client.checkExists().inBackground().forPath("/hey"); assertTrue(timing.awaitLatch(latch)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testFailure() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), 100, 100, new RetryOneTime(1)); client.start(); try { client.checkExists().forPath("/hey"); client.checkExists().inBackground().forPath("/hey"); server.stop(); client.checkExists().forPath("/hey"); fail(); } catch ( KeeperException.SessionExpiredException e ) { // correct, this happens on ZK 3.6.3+ } catch ( KeeperException.ConnectionLossException e ) { // correct, this happens on ZK 3.5.x, 3.6.0 -> 3.6.2 } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRetry() throws Exception { final int MAX_RETRIES = 3; final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(10)); client.start(); try { final AtomicInteger retries = new AtomicInteger(0); final Semaphore semaphore = new Semaphore(0); RetryPolicy policy = new RetryPolicy() { @Override public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper) { semaphore.release(); if ( retries.incrementAndGet() == MAX_RETRIES ) { try { server.restart(); } catch ( Exception e ) { throw new Error(e); } } try { sleeper.sleepFor(100, TimeUnit.MILLISECONDS); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } return true; } }; client.getZookeeperClient().setRetryPolicy(policy); server.stop(); // test foreground retry client.checkExists().forPath("/hey"); assertTrue(semaphore.tryAcquire(MAX_RETRIES, timing.forWaiting().seconds(), TimeUnit.SECONDS), "Remaining leases: " + semaphore.availablePermits()); // make sure we're reconnected client.getZookeeperClient().setRetryPolicy(new RetryOneTime(100)); client.checkExists().forPath("/hey"); client.getZookeeperClient().setRetryPolicy(policy); semaphore.drainPermits(); retries.set(0); server.stop(); // test background retry client.checkExists().inBackground().forPath("/hey"); assertTrue(semaphore.tryAcquire(MAX_RETRIES, timing.forWaiting().seconds(), TimeUnit.SECONDS), "Remaining leases: " + semaphore.availablePermits()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testNotStarted() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.getData(); fail(); } catch ( Exception e ) { // correct } catch ( Throwable e ) { fail("", e); } } @Test public void testStopped() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.getData(); } finally { CloseableUtils.closeQuietly(client); } try { client.getData(); fail(); } catch ( Exception e ) { // correct } } @Test public void testDeleteChildrenConcurrently() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); CuratorFramework client2 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); ExecutorService executorService = Executors.newSingleThreadExecutor(); try { client.start(); client2.start(); int childCount = 500; for ( int i = 0; i < childCount; i++ ) { client.create().creatingParentsIfNeeded().forPath("/parent/child" + i); } final CountDownLatch latch = new CountDownLatch(1); executorService.submit(() -> { try { client.delete().deletingChildrenIfNeeded().forPath("/parent"); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } catch ( Exception e ) { if ( e instanceof KeeperException.NoNodeException ) { fail("client delete failed, shouldn't throw NoNodeException", e); } else { fail("unexpected exception", e); } } finally { latch.countDown(); } }); boolean threadDeleted = false; Random random = new Random(); for ( int i = 0; i < childCount; i++ ) { String child = "/parent/child" + random.nextInt(childCount); try { if ( !threadDeleted ) { Stat stat = client2.checkExists().forPath(child); if ( stat == null ) { // the thread client has begin deleted the children threadDeleted = true; log.info("client has deleted the child {}", child); } } else { try { client2.delete().forPath(child); log.info("client2 deleted the child {} successfully", child); break; } catch ( KeeperException.NoNodeException ignore ) { // ignore, because it's deleted by the thread client } catch ( Exception e ) { fail("unexpected exception", e); } } } catch ( Exception e ) { fail("unexpected exception", e); } } assertTrue(timing.awaitLatch(latch)); assertNull(client2.checkExists().forPath("/parent")); } finally { try { executorService.shutdownNow(); executorService.awaitTermination(timing.milliseconds(), TimeUnit.MILLISECONDS); } finally { CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(client2); } } } } TestGzipCompressionProvider.java000066400000000000000000000117001442004423600411610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.ThreadLocalRandom; import java.util.zip.GZIPOutputStream; public class TestGzipCompressionProvider { @Test public void testSimple() throws IOException { GzipCompressionProvider provider = new GzipCompressionProvider(); byte[] data = "Hello, world!".getBytes(); byte[] compressedData = provider.compress(null, data); byte[] jdkCompressedData = jdkCompress(data); assertTrue(Arrays.equals(compressedData, jdkCompressedData)); byte[] decompressedData = provider.decompress(null, compressedData); assertTrue(Arrays.equals(decompressedData, data)); } @Test public void testEmpty() throws IOException { GzipCompressionProvider provider = new GzipCompressionProvider(); byte[] compressedData = provider.compress(null, new byte[0]); byte[] compressedData2 = GzipCompressionProvider.doCompress(new byte[0]); byte[] jdkCompress = jdkCompress(new byte[0]); // Ensures GzipCompressionProvider.COMPRESSED_EMPTY_BYTES value is valid assertTrue(Arrays.equals(compressedData, compressedData2)); assertTrue(Arrays.equals(compressedData, jdkCompress)); byte[] decompressedData = provider.decompress(null, compressedData); assertEquals(0, decompressedData.length); } /** * This test ensures that in the face of corrupt data, specifically IOException is thrown, rather some other kind * of runtime exception. Users of {@link GzipCompressionProvider#decompress(String, byte[])} may depend on this. */ @Test public void testDecompressCorrupt() { GzipCompressionProvider provider = new GzipCompressionProvider(); try { provider.decompress(null, new byte[100]); fail("Expected IOException"); } catch (IOException ignore) { // expected } byte[] compressedData = provider.compress(null, new byte[0]); for (int i = 0; i < compressedData.length; i++) { try { provider.decompress(null, Arrays.copyOf(compressedData, i)); } catch (IOException ignore) { // expected } for (int change = 1; change < 256; change++) { byte b = compressedData[i]; compressedData[i] = (byte) (b + change); try { provider.decompress(null, compressedData); // No exception is OK } catch (IOException ignore) { // expected } // reset value back compressedData[i] = b; } } } @Test public void smokeTestRandomDataWithJdk() throws IOException { GzipCompressionProvider provider = new GzipCompressionProvider(); ThreadLocalRandom random = ThreadLocalRandom.current(); for (int len = 1; len < 100; len++) { byte[] data = new byte[len]; for (int i = 0; i < 100; i++) { byte[] compressedData = provider.compress(null, data); byte[] jdkCompressedData = jdkCompress(data); assertTrue(Arrays.equals(compressedData, jdkCompressedData)); byte[] decompressedData = provider.decompress(null, compressedData); assertTrue(Arrays.equals(decompressedData, data)); // in the end of the iteration to test empty array first random.nextBytes(data); } } } private static byte[] jdkCompress(byte[] data) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); try (GZIPOutputStream out = new GZIPOutputStream(bytes)) { out.write(data); out.finish(); } return bytes.toByteArray(); } } TestMultiClient.java000066400000000000000000000073751442004423600365610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.retry.RetryOneTime; import org.apache.zookeeper.Watcher; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestMultiClient extends BaseClassForTests { @Test public void testNotify() throws Exception { CuratorFramework client1 = null; CuratorFramework client2 = null; try { client1 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client2 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client1.start(); client2.start(); final CountDownLatch latch = new CountDownLatch(1); client1.getCuratorListenable().addListener ( new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.WATCHED ) { if ( event.getWatchedEvent().getType() == Watcher.Event.EventType.NodeDataChanged ) { if ( event.getPath().equals("/test") ) { latch.countDown(); } } } } } ); client1.create().forPath("/test", new byte[]{1, 2, 3}); client1.checkExists().watched().forPath("/test"); client2.getCuratorListenable().addListener ( new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.SYNC ) { client.setData().forPath("/test", new byte[]{10, 20}); } } } ); client2.sync().forPath("/test"); assertTrue(latch.await(10, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(client1); CloseableUtils.closeQuietly(client2); } } } TestNamespaceFacade.java000066400000000000000000000214171442004423600373010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.fail; import java.util.Collections; import java.util.List; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.KeeperException.NoAuthException; import org.apache.zookeeper.data.ACL; import org.junit.jupiter.api.Test; public class TestNamespaceFacade extends BaseClassForTests { @Test public void testInvalid() throws Exception { try { CuratorFrameworkFactory.builder().namespace("/snafu").retryPolicy(new RetryOneTime(1)).connectString("foo").build(); fail(); } catch ( IllegalArgumentException e ) { // correct } } @Test public void testGetNamespace() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); CuratorFramework client2 = CuratorFrameworkFactory.builder().namespace("snafu").retryPolicy(new RetryOneTime(1)).connectString("foo").build(); try { client.start(); CuratorFramework fooClient = client.usingNamespace("foo"); CuratorFramework barClient = client.usingNamespace("bar"); assertEquals(client.getNamespace(), ""); assertEquals(client2.getNamespace(), "snafu"); assertEquals(fooClient.getNamespace(), "foo"); assertEquals(barClient.getNamespace(), "bar"); } finally { CloseableUtils.closeQuietly(client2); CloseableUtils.closeQuietly(client); } } @Test public void testSimultaneous() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); CuratorFramework fooClient = client.usingNamespace("foo"); CuratorFramework barClient = client.usingNamespace("bar"); fooClient.create().forPath("/one"); barClient.create().forPath("/one"); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/foo/one", false)); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/bar/one", false)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCache() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); assertSame(client.usingNamespace("foo"), client.usingNamespace("foo")); assertNotSame(client.usingNamespace("foo"), client.usingNamespace("bar")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/one"); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/one", false)); client.usingNamespace("space").create().forPath("/one"); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/space", false)); client.usingNamespace("name").create().forPath("/one"); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/name", false)); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/name/one", false)); } finally { CloseableUtils.closeQuietly(client); } } /** * CURATOR-128: access root node within a namespace. */ @Test public void testRootAccess() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/one"); assertNotNull(client.getZookeeperClient().getZooKeeper().exists("/one", false)); assertNotNull(client.checkExists().forPath("/")); try { client.checkExists().forPath(""); fail("IllegalArgumentException expected"); } catch ( IllegalArgumentException expected ) { } assertNotNull(client.usingNamespace("one").checkExists().forPath("/")); try { client.usingNamespace("one").checkExists().forPath(""); fail("IllegalArgumentException expected"); } catch ( IllegalArgumentException expected ) { } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testIsStarted() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); CuratorFramework namespaced = client.usingNamespace(null); assertEquals(client.getState(), namespaced.getState(), "Namespaced state did not match true state after call to start."); client.close(); assertEquals(client.getState(), namespaced.getState(), "Namespaced state did not match true state after call to close."); } /** * Test that ACLs work on a NamespaceFacade. See CURATOR-132 * @throws Exception */ @Test public void testACL() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); client.getZookeeperClient().blockUntilConnectedOrTimedOut(); client.create().creatingParentsIfNeeded().forPath("/parent/child", "A string".getBytes()); CuratorFramework client2 = client.usingNamespace("parent"); assertNotNull(client2.getData().forPath("/child")); client.setACL().withACL(Collections.singletonList( new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.ANYONE_ID_UNSAFE))). forPath("/parent/child"); // This will attempt to setACL on /parent/child, Previously this failed because /child // isn't present. Using "child" would case a failure because the path didn't start with // a slash try { List acls = client2.getACL().forPath("/child"); assertNotNull(acls); assertEquals(acls.size(), 1); assertEquals(acls.get(0).getId(), ZooDefs.Ids.ANYONE_ID_UNSAFE); assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.WRITE); client2.setACL().withACL(Collections.singletonList( new ACL(ZooDefs.Perms.DELETE, ZooDefs.Ids.ANYONE_ID_UNSAFE))). forPath("/child"); fail("Expected auth exception was not thrown"); } catch(NoAuthException e) { //Expected } } @Test public void testUnfixForEmptyNamespace() { CuratorFramework client = CuratorFrameworkFactory.builder().namespace("").retryPolicy(new RetryOneTime(1)).connectString("foo").build(); CuratorFrameworkImpl clientImpl = (CuratorFrameworkImpl) client; assertEquals(clientImpl.unfixForNamespace("/foo/bar"), "/foo/bar"); CloseableUtils.closeQuietly(client); } } TestNeverConnected.java000066400000000000000000000052561442004423600372260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.common.collect.Queues; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.junit.jupiter.api.Test; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; public class TestNeverConnected { @Test public void testNeverConnected() throws Exception { Timing timing = new Timing(); // use a connection string to a non-existent server CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:1111", 100, 100, new RetryOneTime(1)); try { final BlockingQueue queue = Queues.newLinkedBlockingQueue(); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState state) { queue.add(state); } }; client.getConnectionStateListenable().addListener(listener); client.start(); client.create().inBackground().forPath("/"); ConnectionState polled = queue.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertEquals(polled, ConnectionState.SUSPENDED); polled = queue.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertEquals(polled, ConnectionState.LOST); } finally { CloseableUtils.closeQuietly(client); } } } TestReadOnly.java000066400000000000000000000143641442004423600360410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Iterator; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestReadOnly extends BaseClassForTests { @BeforeEach public void setup() { System.setProperty("readonlymode.enabled", "true"); } @AfterEach public void tearDown() { System.setProperty("readonlymode.enabled", "false"); } @Test public void testConnectionStateNewClient() throws Exception { Timing timing = new Timing(); CuratorFramework client = null; TestingCluster cluster = createAndStartCluster(3); try { final CountDownLatch lostLatch = new CountDownLatch(1); client = CuratorFrameworkFactory.newClient(cluster.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(100)); client.start(); client.checkExists().forPath("/"); client.getConnectionStateListenable().addListener((client1, newState) -> { if (newState == ConnectionState.LOST) { lostLatch.countDown(); } }); Iterator iterator = cluster.getInstances().iterator(); for ( int i = 0; i < 2; ++i ) { cluster.killServer(iterator.next()); } timing.awaitLatch(lostLatch); client.close(); client = CuratorFrameworkFactory.builder() .connectString(cluster.getConnectString()) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()) .retryPolicy(new RetryNTimes(3, timing.milliseconds())) .canBeReadOnly(true) .build(); final BlockingQueue states = Queues.newLinkedBlockingQueue(); client.getConnectionStateListenable().addListener((client12, newState) -> states.add(newState)); client.start(); client.checkExists().forPath("/"); ConnectionState state = states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); assertThat(state).isEqualTo(ConnectionState.READ_ONLY); } finally { CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(cluster); } } @Test public void testReadOnly() throws Exception { Timing timing = new Timing(); CuratorFramework client = null; TestingCluster cluster = createAndStartCluster(2); try { client = CuratorFrameworkFactory.builder().connectString(cluster.getConnectString()).canBeReadOnly(true).connectionTimeoutMs(timing.connection()).sessionTimeoutMs(timing.session()).retryPolicy(new ExponentialBackoffRetry(100, 3)).build(); client.start(); client.create().forPath("/test"); final CountDownLatch readOnlyLatch = new CountDownLatch(1); final CountDownLatch reconnectedLatch = new CountDownLatch(1); ConnectionStateListener listener = (client1, newState) -> { if ( newState == ConnectionState.READ_ONLY ) { readOnlyLatch.countDown(); } else if ( newState == ConnectionState.RECONNECTED ) { reconnectedLatch.countDown(); } }; client.getConnectionStateListenable().addListener(listener); InstanceSpec ourInstance = cluster.findConnectionInstance(client.getZookeeperClient().getZooKeeper()); Iterator iterator = cluster.getInstances().iterator(); InstanceSpec killInstance = iterator.next(); if ( killInstance.equals(ourInstance) ) { killInstance = iterator.next(); // kill the instance we're not connected to } cluster.killServer(killInstance); assertEquals(reconnectedLatch.getCount(), 1); assertTrue(timing.awaitLatch(readOnlyLatch)); assertEquals(reconnectedLatch.getCount(), 1); cluster.restartServer(killInstance); assertTrue(timing.awaitLatch(reconnectedLatch)); } finally { CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(cluster); } } @Override protected void createServer() { // NOP } } TestReconfiguration.java000066400000000000000000000670111442004423600374570ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.collect.Lists; import org.apache.curator.ensemble.EnsembleProvider; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.TestingZooKeeperServer; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class TestReconfiguration extends CuratorTestBase { private final Timing2 timing = new Timing2(); private TestingCluster cluster; private EnsembleProvider ensembleProvider; private static final String superUserPasswordDigest = "curator-test:zghsj3JfJqK7DbWf0RQ1BgbJH9w="; // ran from DigestAuthenticationProvider.generateDigest(superUserPassword); private static final String superUserPassword = "curator-test"; @BeforeEach @Override public void setup() throws Exception { super.setup(); QuorumPeerConfig.setReconfigEnabled(true); System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", superUserPasswordDigest); CloseableUtils.closeQuietly(server); cluster = createAndStartCluster(3); } @AfterEach @Override public void teardown() throws Exception { CloseableUtils.closeQuietly(cluster); ensembleProvider = null; System.clearProperty("zookeeper.DigestAuthenticationProvider.superDigest"); super.teardown(); } @SuppressWarnings("ConstantConditions") @Test @Disabled public void testApiPermutations() throws Exception { // not an actual test. Specifies all possible API possibilities Watcher watcher = null; Stat stat = null; CuratorFramework client = null; client.getConfig().forEnsemble(); client.getConfig().inBackground().forEnsemble(); client.getConfig().usingWatcher(watcher).forEnsemble(); client.getConfig().usingWatcher(watcher).inBackground().forEnsemble(); client.getConfig().storingStatIn(stat).forEnsemble(); client.getConfig().storingStatIn(stat).inBackground().forEnsemble(); client.getConfig().storingStatIn(stat).usingWatcher(watcher).forEnsemble(); client.getConfig().storingStatIn(stat).usingWatcher(watcher).inBackground().forEnsemble(); // --------- client.reconfig().leaving().forEnsemble(); client.reconfig().joining().forEnsemble(); client.reconfig().leaving().joining().forEnsemble(); client.reconfig().joining().leaving().forEnsemble(); client.reconfig().withNewMembers().forEnsemble(); client.reconfig().leaving().fromConfig(0).forEnsemble(); client.reconfig().joining().fromConfig(0).forEnsemble(); client.reconfig().leaving().joining().fromConfig(0).forEnsemble(); client.reconfig().joining().leaving().fromConfig(0).forEnsemble(); client.reconfig().withNewMembers().fromConfig(0).forEnsemble(); client.reconfig().leaving().storingStatIn(stat).forEnsemble(); client.reconfig().joining().storingStatIn(stat).forEnsemble(); client.reconfig().leaving().joining().storingStatIn(stat).forEnsemble(); client.reconfig().joining().leaving().storingStatIn(stat).forEnsemble(); client.reconfig().withNewMembers().storingStatIn(stat).forEnsemble(); client.reconfig().leaving().storingStatIn(stat).fromConfig(0).forEnsemble(); client.reconfig().joining().storingStatIn(stat).fromConfig(0).forEnsemble(); client.reconfig().leaving().joining().storingStatIn(stat).fromConfig(0).forEnsemble(); client.reconfig().joining().leaving().storingStatIn(stat).fromConfig(0).forEnsemble(); client.reconfig().withNewMembers().storingStatIn(stat).fromConfig(0).forEnsemble(); client.reconfig().inBackground().leaving().forEnsemble(); client.reconfig().inBackground().joining().forEnsemble(); client.reconfig().inBackground().leaving().joining().forEnsemble(); client.reconfig().inBackground().joining().leaving().forEnsemble(); client.reconfig().inBackground().withNewMembers().forEnsemble(); client.reconfig().inBackground().leaving().fromConfig(0).forEnsemble(); client.reconfig().inBackground().joining().fromConfig(0).forEnsemble(); client.reconfig().inBackground().leaving().joining().fromConfig(0).forEnsemble(); client.reconfig().inBackground().joining().leaving().fromConfig(0).forEnsemble(); client.reconfig().inBackground().withNewMembers().fromConfig(0).forEnsemble(); client.reconfig().inBackground().leaving().storingStatIn(stat).forEnsemble(); client.reconfig().inBackground().joining().storingStatIn(stat).forEnsemble(); client.reconfig().inBackground().leaving().joining().storingStatIn(stat).forEnsemble(); client.reconfig().inBackground().joining().leaving().storingStatIn(stat).forEnsemble(); client.reconfig().inBackground().withNewMembers().storingStatIn(stat).forEnsemble(); client.reconfig().inBackground().leaving().storingStatIn(stat).fromConfig(0).forEnsemble(); client.reconfig().inBackground().joining().storingStatIn(stat).fromConfig(0).forEnsemble(); client.reconfig().inBackground().leaving().joining().storingStatIn(stat).fromConfig(0).forEnsemble(); client.reconfig().inBackground().joining().leaving().storingStatIn(stat).fromConfig(0).forEnsemble(); client.reconfig().inBackground().withNewMembers().storingStatIn(stat).fromConfig(0).forEnsemble(); } @Test public void testBasicGetConfig() throws Exception { try ( CuratorFramework client = newClient()) { client.start(); byte[] configData = client.getConfig().forEnsemble(); QuorumVerifier quorumVerifier = toQuorumVerifier(configData); System.out.println(quorumVerifier); assertConfig(quorumVerifier, cluster.getInstances()); assertEquals(EnsembleTracker.configToConnectionString(quorumVerifier), ensembleProvider.getConnectionString()); } } @Test public void testAddWithoutEnsembleTracker() throws Exception { final String initialClusterCS = cluster.getConnectString(); try ( CuratorFramework client = newClient(cluster.getConnectString(), false)) { assertEquals(((CuratorFrameworkImpl) client).getEnsembleTracker(), null); client.start(); QuorumVerifier oldConfig = toQuorumVerifier(client.getConfig().forEnsemble()); assertConfig(oldConfig, cluster.getInstances()); CountDownLatch latch = setChangeWaiter(client); try ( TestingCluster newCluster = new TestingCluster(TestingCluster.makeSpecs(1, false)) ) { newCluster.start(); client.reconfig().joining(toReconfigSpec(newCluster.getInstances())).fromConfig(oldConfig.getVersion()).forEnsemble(); assertTrue(timing.awaitLatch(latch)); byte[] newConfigData = client.getConfig().forEnsemble(); QuorumVerifier newConfig = toQuorumVerifier(newConfigData); List newInstances = Lists.newArrayList(cluster.getInstances()); newInstances.addAll(newCluster.getInstances()); assertConfig(newConfig, newInstances); assertEquals(ensembleProvider.getConnectionString(), initialClusterCS); assertNotEquals(EnsembleTracker.configToConnectionString(newConfig), ensembleProvider.getConnectionString()); assertEquals(client.getZookeeperClient().getCurrentConnectionString(), initialClusterCS); final CountDownLatch reconnectLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener( (cfClient, newState) -> { if (newState == ConnectionState.RECONNECTED) reconnectLatch.countDown(); } ); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertTrue(reconnectLatch.await(2, TimeUnit.SECONDS)); assertEquals(client.getZookeeperClient().getCurrentConnectionString(), initialClusterCS); assertEquals(ensembleProvider.getConnectionString(), initialClusterCS); newConfigData = client.getConfig().forEnsemble(); assertNotEquals(EnsembleTracker.configToConnectionString(newConfig), ensembleProvider.getConnectionString()); } } } @Test public void testAdd() throws Exception { try ( CuratorFramework client = newClient()) { client.start(); QuorumVerifier oldConfig = toQuorumVerifier(client.getConfig().forEnsemble()); assertConfig(oldConfig, cluster.getInstances()); CountDownLatch latch = setChangeWaiter(client); try ( TestingCluster newCluster = new TestingCluster(TestingCluster.makeSpecs(1, false)) ) { newCluster.start(); client.reconfig().joining(toReconfigSpec(newCluster.getInstances())).fromConfig(oldConfig.getVersion()).forEnsemble(); assertTrue(timing.awaitLatch(latch)); byte[] newConfigData = client.getConfig().forEnsemble(); QuorumVerifier newConfig = toQuorumVerifier(newConfigData); List newInstances = Lists.newArrayList(cluster.getInstances()); newInstances.addAll(newCluster.getInstances()); assertConfig(newConfig, newInstances); assertEquals(EnsembleTracker.configToConnectionString(newConfig), ensembleProvider.getConnectionString()); } } } @Test public void testAddAsync() throws Exception { try ( CuratorFramework client = newClient()) { client.start(); QuorumVerifier oldConfig = toQuorumVerifier(client.getConfig().forEnsemble()); assertConfig(oldConfig, cluster.getInstances()); CountDownLatch latch = setChangeWaiter(client); try ( TestingCluster newCluster = new TestingCluster(TestingCluster.makeSpecs(1, false)) ) { newCluster.start(); final CountDownLatch callbackLatch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getType() == CuratorEventType.RECONFIG ) { callbackLatch.countDown(); } } }; client.reconfig().inBackground(callback).joining(toReconfigSpec(newCluster.getInstances())).fromConfig(oldConfig.getVersion()).forEnsemble(); assertTrue(timing.awaitLatch(callbackLatch)); assertTrue(timing.awaitLatch(latch)); byte[] newConfigData = client.getConfig().forEnsemble(); QuorumVerifier newConfig = toQuorumVerifier(newConfigData); List newInstances = Lists.newArrayList(cluster.getInstances()); newInstances.addAll(newCluster.getInstances()); assertConfig(newConfig, newInstances); assertEquals(EnsembleTracker.configToConnectionString(newConfig), ensembleProvider.getConnectionString()); } } } @Test public void testAddAndRemove() throws Exception { try ( CuratorFramework client = newClient()) { client.start(); QuorumVerifier oldConfig = toQuorumVerifier(client.getConfig().forEnsemble()); assertConfig(oldConfig, cluster.getInstances()); CountDownLatch latch = setChangeWaiter(client); try ( TestingCluster newCluster = new TestingCluster(TestingCluster.makeSpecs(1, false)) ) { newCluster.start(); Collection oldInstances = cluster.getInstances(); InstanceSpec us = cluster.findConnectionInstance(client.getZookeeperClient().getZooKeeper()); InstanceSpec removeSpec = oldInstances.iterator().next(); if ( us.equals(removeSpec) ) { Iterator iterator = oldInstances.iterator(); iterator.next(); removeSpec = iterator.next(); } Collection instances = newCluster.getInstances(); client.reconfig().leaving(Integer.toString(removeSpec.getServerId())).joining(toReconfigSpec(instances)).fromConfig(oldConfig.getVersion()).forEnsemble(); assertTrue(timing.awaitLatch(latch)); byte[] newConfigData = client.getConfig().forEnsemble(); QuorumVerifier newConfig = toQuorumVerifier(newConfigData); ArrayList newInstances = Lists.newArrayList(oldInstances); newInstances.addAll(instances); newInstances.remove(removeSpec); assertConfig(newConfig, newInstances); assertEquals(EnsembleTracker.configToConnectionString(newConfig), ensembleProvider.getConnectionString()); } } } @Test public void testAddAndRemoveWithEmptyList() throws Exception { try ( CuratorFramework client = newClient()) { client.start(); QuorumVerifier oldConfig = toQuorumVerifier(client.getConfig().forEnsemble()); assertConfig(oldConfig, cluster.getInstances()); CountDownLatch latch = setChangeWaiter(client); Collection oldInstances = cluster.getInstances(); client.reconfig().leaving(Collections.emptyList()).joining(Collections.emptyList()).fromConfig(oldConfig.getVersion()).forEnsemble(); assertTrue(timing.awaitLatch(latch)); byte[] newConfigData = client.getConfig().forEnsemble(); QuorumVerifier newConfig = toQuorumVerifier(newConfigData); assertConfig(newConfig, oldInstances); assertEquals(EnsembleTracker.configToConnectionString(newConfig), ensembleProvider.getConnectionString()); } } @Test public void testNewMembersWithEmptyList() throws Exception { try ( CuratorFramework client = newClient()) { client.start(); QuorumVerifier oldConfig = toQuorumVerifier(client.getConfig().forEnsemble()); assertConfig(oldConfig, cluster.getInstances()); CountDownLatch latch = setChangeWaiter(client); Collection oldInstances = cluster.getInstances(); client.reconfig().withNewMembers(Collections.emptyList()).fromConfig(oldConfig.getVersion()).forEnsemble(); assertTrue(timing.awaitLatch(latch)); byte[] newConfigData = client.getConfig().forEnsemble(); QuorumVerifier newConfig = toQuorumVerifier(newConfigData); assertConfig(newConfig, oldInstances); assertEquals(EnsembleTracker.configToConnectionString(newConfig), ensembleProvider.getConnectionString()); } } @Test @Disabled // it's what this test is inteded to do and it keeps failing - disable for now public void testNewMembers() throws Exception { cluster.close(); cluster = null; TestingCluster smallCluster = null; TestingCluster localCluster = new TestingCluster(5); try { List servers = localCluster.getServers(); List smallClusterInstances = Lists.newArrayList(); for ( int i = 0; i < 3; ++i ) // only start 3 of the 5 { TestingZooKeeperServer server = servers.get(i); server.start(); smallClusterInstances.add(server.getInstanceSpec()); } smallCluster = new TestingCluster(smallClusterInstances); try ( CuratorFramework client = newClient(smallCluster.getConnectString())) { client.start(); QuorumVerifier oldConfig = toQuorumVerifier(client.getConfig().forEnsemble()); assertEquals(oldConfig.getAllMembers().size(), 5); assertConfig(oldConfig, localCluster.getInstances()); CountDownLatch latch = setChangeWaiter(client); client.reconfig().withNewMembers(toReconfigSpec(smallClusterInstances)).forEnsemble(); assertTrue(timing.awaitLatch(latch)); byte[] newConfigData = client.getConfig().forEnsemble(); QuorumVerifier newConfig = toQuorumVerifier(newConfigData); assertEquals(newConfig.getAllMembers().size(), 3); assertConfig(newConfig, smallClusterInstances); assertEquals(EnsembleTracker.configToConnectionString(newConfig), ensembleProvider.getConnectionString()); } } finally { CloseableUtils.closeQuietly(smallCluster); CloseableUtils.closeQuietly(localCluster); } } @Test public void testConfigToConnectionStringIPv4Normal() throws Exception { String config = "server.1=10.1.2.3:2888:3888:participant;10.2.3.4:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("10.2.3.4:2181", configString); } @Test public void testConfigToConnectionStringIPv6Normal() throws Exception { String config = "server.1=[1010:0001:0002:0003:0004:0005:0006:0007]:2888:3888:participant;[2001:db8:85a3:0:0:8a2e:370:7334]:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("2001:db8:85a3:0:0:8a2e:370:7334:2181", configString); } @Test public void testConfigToConnectionStringIPv4NoClientAddr() throws Exception { String config = "server.1=10.1.2.3:2888:3888:participant;2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("10.1.2.3:2181", configString); } @Test public void testConfigToConnectionStringIPv4WildcardClientAddr() throws Exception { String config = "server.1=10.1.2.3:2888:3888:participant;0.0.0.0:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("10.1.2.3:2181", configString); } @Test public void testConfigToConnectionStringNoClientAddrOrPort() throws Exception { String config = "server.1=10.1.2.3:2888:3888:participant"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("", configString); } @Test public void testConfigToConnectionStringPreservesHostnameNormal() throws Exception { String config = "server.1=server.addr:2888:3888:participant;client.addr:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("client.addr:2181", configString); } @Test public void testConfigToConnectionStringPreservesHostnameNoClientAddr() throws Exception { String config = "server.1=server.addr:2888:3888:participant;2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("server.addr:2181", configString); } @Test public void testConfigToConnectionStringPreservesHostnameIPv4Wildcard() throws Exception { String config = "server.1=server.addr:2888:3888:participant;0.0.0.0:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("server.addr:2181", configString); } @Test public void testConfigToConnectionStringPreservesHostnameIPv6Wildcard() throws Exception { String config = "server.1=server.addr:2888:3888:participant;[::]:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("server.addr:2181", configString); } @Test public void testIPv6Wildcard1() throws Exception { String config = "server.1=[2001:db8:85a3:0:0:8a2e:370:7334]:2888:3888:participant;[::]:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("2001:db8:85a3:0:0:8a2e:370:7334:2181", configString); } @Test public void testIPv6Wildcard2() throws Exception { String config = "server.1=[1010:0001:0002:0003:0004:0005:0006:0007]:2888:3888:participant;[::0]:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("1010:1:2:3:4:5:6:7:2181", configString); } @Test public void testMixedIPv1() throws Exception { String config = "server.1=10.1.2.3:2888:3888:participant;[::]:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("10.1.2.3:2181", configString); } @Test public void testMixedIPv2() throws Exception { String config = "server.1=[2001:db8:85a3:0:0:8a2e:370:7334]:2888:3888:participant;127.0.0.1:2181"; String configString = EnsembleTracker.configToConnectionString(toQuorumVerifier(config.getBytes())); assertEquals("127.0.0.1:2181", configString); } @Override protected void createServer() throws Exception { // NOP } private CuratorFramework newClient() { return newClient(cluster.getConnectString(), true); } private CuratorFramework newClient(String connectionString) { return newClient(connectionString, true); } private CuratorFramework newClient(String connectionString, boolean withEnsembleProvider) { final AtomicReference connectString = new AtomicReference<>(connectionString); ensembleProvider = new EnsembleProvider() { @Override public void start() throws Exception { } @Override public boolean updateServerListEnabled() { return false; } @Override public String getConnectionString() { return connectString.get(); } @Override public void close() throws IOException { } @Override public void setConnectionString(String connectionString) { connectString.set(connectionString); } }; return CuratorFrameworkFactory.builder() .ensembleProvider(ensembleProvider) .ensembleTracker(withEnsembleProvider) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()) .authorization("digest", superUserPassword.getBytes()) .retryPolicy(new ExponentialBackoffRetry(timing.forSleepingABit().milliseconds(), 3)) .build(); } private CountDownLatch setChangeWaiter(CuratorFramework client) throws Exception { final CountDownLatch latch = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if ( event.getType() == Event.EventType.NodeDataChanged ) { latch.countDown(); } } }; client.getConfig().usingWatcher(watcher).forEnsemble(); return latch; } private void assertConfig(QuorumVerifier config, Collection instances) { for ( InstanceSpec instance : instances ) { QuorumPeer.QuorumServer quorumServer = config.getAllMembers().get((long)instance.getServerId()); assertNotNull(quorumServer, String.format("Looking for %s - found %s", instance.getServerId(), config.getAllMembers())); assertEquals(quorumServer.clientAddr.getPort(), instance.getPort()); } } private List toReconfigSpec(Collection instances) throws Exception { String localhost = new InetSocketAddress((InetAddress)null, 0).getAddress().getHostAddress(); List specs = Lists.newArrayList(); for ( InstanceSpec instance : instances ) { specs.add("server." + instance.getServerId() + "=" + localhost + ":" + instance.getElectionPort() + ":" + instance.getQuorumPort() + ";" + instance.getPort()); } return specs; } private static QuorumVerifier toQuorumVerifier(byte[] bytes) throws Exception { assertNotNull(bytes); Properties properties = new Properties(); properties.load(new ByteArrayInputStream(bytes)); return new QuorumMaj(properties); } } TestSetData.java000066400000000000000000000364151442004423600356520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.apache.zookeeper.KeeperException.Code.*; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.BackgroundPathAndBytesable; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.SetDataBuilder; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class TestSetData extends BaseClassForTests { private static byte[] createData = new byte[] {5, 6, 7, 8}; private CuratorFramework createClient() { return CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); } /** * Tests normal setData operations */ @Test public void testNormal() throws Exception { CuratorFramework client = createClient(); try { client.start(); Stat stat = new Stat(); String path = "/test"; byte[] data = new byte[] {1}; byte[] data2 = new byte[] {1, 2}; check(client, client.setData(), path, data, NONODE.intValue(), -1); client.create().forPath(path, createData); check(client, client.setData(), path, data, OK.intValue(), 1); // version should be 1 at this point, so test withVersion check(client, client.setData().withVersion(1), path, data2, OK.intValue(), 2); // check that set fails with version mismatch check(client, client.setData().withVersion(1), path, data, BADVERSION.intValue(), 2); // check that failed request didn't change znode assertArrayEquals(data2, client.getData().forPath(path)); } finally { CloseableUtils.closeQuietly(client); } } /** * Tests background versions of setData */ @Test public void testBackground() throws Exception { CuratorFramework client = createClient(); try { client.start(); Stat stat = new Stat(); String path = "/test"; byte[] data = new byte[] {1}; byte[] data2 = new byte[] {1, 2}; // check setData fails when node doesn't exist checkBackground(client, client.setData(), path, data, NONODE.intValue(), -1); client.create().forPath(path, createData); checkBackground(client, client.setData(), path, data, OK.intValue(), 1); // version should be 1 at this point checkBackground(client, client.setData().withVersion(1), path, data2, OK.intValue(), 2); // check setData fails when version mismatches checkBackground(client, client.setData().withVersion(1), path, data, BADVERSION.intValue(), 3); // make sure old data is still correct assertArrayEquals(data2, client.getData().forPath(path)); } finally { CloseableUtils.closeQuietly(client); } } private void check(CuratorFramework client, BackgroundPathAndBytesable builder, String path, byte[] data, int expectedCode, int expectedVersionAfter) throws Exception { try { builder.forPath(path, data); assertEquals(expectedCode, OK.intValue()); Stat stat = new Stat(); byte[] actualData = client.getData().storingStatIn(stat).forPath(path); assertTrue(IdempotentUtils.matches(expectedVersionAfter, data, stat.getVersion(), actualData)); } catch (KeeperException e) { assertEquals(expectedCode, e.getCode()); } } private void checkBackground(CuratorFramework client, BackgroundPathAndBytesable builder, String path, byte[] data, int expectedCode, int expectedVersionAfter) throws Exception { AtomicInteger actualCode = new AtomicInteger(-1); CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { actualCode.set(event.getResultCode()); latch.countDown(); } }; builder.inBackground(callback).forPath(path, data); assertTrue(latch.await(5000, TimeUnit.MILLISECONDS), "Callback not invoked"); assertEquals(expectedCode, actualCode.get()); if (expectedCode == OK.intValue()) { Stat stat = new Stat(); byte[] actualData = client.getData().storingStatIn(stat).forPath(path); assertTrue(IdempotentUtils.matches(expectedVersionAfter, data, stat.getVersion(), actualData)); } } /** * Tests all cases of idempotent set */ @Test public void testIdempotentSet() throws Exception { CuratorFramework client = createClient(); try { client.start(); Stat stat = new Stat(); String path = "/idpset"; String pathBack = "/idpsetback"; byte[] data1 = new byte[] {1, 2, 3}; byte[] data2 = new byte[] {4, 5, 6}; byte[] data3 = new byte[] {7, 8, 9}; // check foreground and background // set when node doesn't exist should fail check(client, client.setData().idempotent(), path, data1, NONODE.intValue(), -1); checkBackground(client, client.setData().idempotent(), pathBack, data1, NONODE.intValue(), -1); client.create().forPath(path, createData); client.create().forPath(pathBack, createData); // check normal set succeeds check(client, client.setData().idempotent(), path, data1, OK.intValue(), 1); checkBackground(client, client.setData().idempotent(), pathBack, data1, OK.intValue(), 1); // check normal set with version succeeds check(client, client.setData().idempotent().withVersion(1), path, data2, OK.intValue(), 2); checkBackground(client, client.setData().idempotent().withVersion(1), pathBack, data2, OK.intValue(), 2); // repeating the same op should succeed without updating the version check(client, client.setData().idempotent().withVersion(1), path, data2, OK.intValue(), 2); checkBackground(client, client.setData().idempotent().withVersion(1), pathBack, data2, OK.intValue(), 2); // same op with different data should fail even though version matches check(client, client.setData().idempotent().withVersion(1), path, data3, BADVERSION.intValue(), 1); checkBackground(client, client.setData().idempotent().withVersion(1), pathBack, data3, BADVERSION.intValue(), 1); // same op with same data should fail even because version mismatches check(client, client.setData().idempotent().withVersion(0), path, data2, BADVERSION.intValue(), 2); checkBackground(client, client.setData().idempotent().withVersion(0), pathBack, data2, BADVERSION.intValue(), 2); } finally { CloseableUtils.closeQuietly(client); } } private SetDataBuilder clBefore(SetDataBuilder builder) { ((SetDataBuilderImpl)builder).failBeforeNextSetForTesting = true; return builder; } private SetDataBuilder clAfter(SetDataBuilder builder) { ((SetDataBuilderImpl)builder).failNextSetForTesting = true; return builder; } private SetDataBuilder clCheck(SetDataBuilder builder) { ((SetDataBuilderImpl)builder).failNextIdempotentCheckForTesting = true; return builder; } // Test that idempotent set automatically retries correctly upon connectionLoss @Test public void testIdempotentSetConnectionLoss() throws Exception { CuratorFramework client = createClient(); try { client.start(); String idpPath = "/idpset"; String idpPathBack = "/idpsetback"; String path = "/set"; String pathBack = "/setBack"; byte[] data = new byte[] {1}; byte[] data2 = new byte[] {1, 2}; byte[] data3 = new byte[] {1, 2, 3}; byte[] data4 = new byte[] {1, 2, 3, 4}; // check foreground and background // test that set fails when node doesn't exist with CL before or after check(client, clBefore(client.setData().idempotent()), idpPath, data, NONODE.intValue(), -1); checkBackground(client, clBefore(client.setData().idempotent()), idpPathBack, data, NONODE.intValue(), -1); check(client, clAfter(client.setData().idempotent()), idpPath, data, NONODE.intValue(), -1); checkBackground(client, clAfter(client.setData().idempotent()), idpPathBack, data, NONODE.intValue(), -1); check(client, clCheck(client.setData().idempotent()), idpPath, data, NONODE.intValue(), -1); checkBackground(client, clCheck(client.setData().idempotent()), idpPathBack, data, NONODE.intValue(), -1); // create nodes client.create().forPath(idpPath, createData); client.create().forPath(idpPathBack, createData); // test that idempotent set succeeds with connection loss before or after first set. // the version should only go up in all cases except CL after, without a version specified, then it should up 2 check(client, clBefore(client.setData().idempotent()).withVersion(0), idpPath, data, OK.intValue(), 1); checkBackground(client, clBefore(client.setData().idempotent()).withVersion(0), idpPathBack, data, OK.intValue(), 1); check(client, clAfter(client.setData().idempotent()).withVersion(1), idpPath, data2, OK.intValue(), 2); checkBackground(client, clAfter(client.setData().idempotent()).withVersion(1), idpPathBack, data2, OK.intValue(), 2); check(client, clBefore(client.setData().idempotent()), idpPath, data3, OK.intValue(), 3); checkBackground(client, clBefore(client.setData().idempotent()), idpPathBack, data3, OK.intValue(), 3); check(client, clAfter(client.setData().idempotent()), idpPath, data4, OK.intValue(), 5); checkBackground(client, clAfter(client.setData().idempotent()), idpPathBack, data4, OK.intValue(), 5); // test that idempotency succeeds when node is already in desired final state withVersion(4) -> (version 5, data4) check(client, clBefore(client.setData().idempotent()).withVersion(4), idpPath, data4, OK.intValue(), 5); checkBackground(client, clBefore(client.setData().idempotent()).withVersion(4), idpPathBack, data4, OK.intValue(), 5); check(client, clAfter(client.setData().idempotent()).withVersion(4), idpPath, data4, OK.intValue(), 5); checkBackground(client, clAfter(client.setData().idempotent()).withVersion(4), idpPathBack, data4, OK.intValue(), 5); check(client, clCheck(client.setData().idempotent()).withVersion(4), idpPath, data4, OK.intValue(), 5); checkBackground(client, clCheck(client.setData().idempotent()).withVersion(4), idpPathBack, data4, OK.intValue(), 5); // test that idempotent set fails correctly when withVersion is set, before or after connectionloss, if data or version does not match // version is wrong (0, would have to be 4) but data is right (data4) check(client, clBefore(client.setData().idempotent()).withVersion(0), idpPath, data4, BADVERSION.intValue(), -1); checkBackground(client, clBefore(client.setData().idempotent()).withVersion(0), idpPathBack, data4, BADVERSION.intValue(), -1); check(client, clAfter(client.setData().idempotent()).withVersion(0), idpPath, data4, BADVERSION.intValue(), -1); checkBackground(client, clAfter(client.setData().idempotent()).withVersion(0), idpPathBack, data4, BADVERSION.intValue(), -1); check(client, clCheck(client.setData().idempotent()).withVersion(0), idpPath, data4, BADVERSION.intValue(), -1); checkBackground(client, clCheck(client.setData().idempotent()).withVersion(0), idpPathBack, data4, BADVERSION.intValue(), -1); // version is right (4) but data is wrong (data, would have to be data4) check(client, clBefore(client.setData().idempotent()).withVersion(4), idpPath, data, BADVERSION.intValue(), -1); checkBackground(client, clBefore(client.setData().idempotent()).withVersion(4), idpPathBack, data, BADVERSION.intValue(), -1); check(client, clAfter(client.setData().idempotent()).withVersion(4), idpPath, data, BADVERSION.intValue(), -1); checkBackground(client, clAfter(client.setData().idempotent()).withVersion(4), idpPathBack, data, BADVERSION.intValue(), -1); check(client, clCheck(client.setData().idempotent()).withVersion(4), idpPath, data, BADVERSION.intValue(), -1); checkBackground(client, clCheck(client.setData().idempotent()).withVersion(4), idpPathBack, data, BADVERSION.intValue(), -1); // test that non-idempotent set withVersion succeeds when retrying with connectionloss before, but fails with connectionloss after client.create().forPath(path, createData); client.create().forPath(pathBack, createData); check(client, clBefore(client.setData()).withVersion(0), path, data, OK.intValue(), 1); checkBackground(client, clBefore(client.setData()).withVersion(0), pathBack, data, OK.intValue(), 1); check(client, clAfter(client.setData()).withVersion(1), path, data2, BADVERSION.intValue(), -1); checkBackground(client, clAfter(client.setData()).withVersion(1), pathBack, data2, BADVERSION.intValue(), -1); } finally { CloseableUtils.closeQuietly(client); } } } TestTempFramework.java000066400000000000000000000060371442004423600371050ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNull; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.CuratorTempFramework; import org.apache.curator.retry.RetryOneTime; import org.junit.jupiter.api.Test; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class TestTempFramework extends BaseClassForTests { @Test public void testBasic() throws Exception { CuratorTempFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).buildTemp(); try { client.inTransaction().create().forPath("/foo", "data".getBytes()).and().commit(); byte[] bytes = client.getData().forPath("/foo"); assertArrayEquals(bytes, "data".getBytes()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testInactivity() throws Exception { final CuratorTempFrameworkImpl client = (CuratorTempFrameworkImpl)CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).buildTemp(1, TimeUnit.SECONDS); try { ScheduledExecutorService service = Executors.newScheduledThreadPool(1); Runnable command = new Runnable() { @Override public void run() { client.updateLastAccess(); } }; service.scheduleAtFixedRate(command, 10, 10, TimeUnit.MILLISECONDS); client.inTransaction().create().forPath("/foo", "data".getBytes()).and().commit(); service.shutdownNow(); Thread.sleep(2000); assertNull(client.getCleanup()); assertNull(client.getClient()); } finally { CloseableUtils.closeQuietly(client); } } } TestTransactionsNew.java000066400000000000000000000275511442004423600374500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Iterables; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.framework.api.transaction.OperationType; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; public class TestTransactionsNew extends BaseClassForTests { @Test public void testErrors() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); CuratorOp createOp1 = client.transactionOp().create().forPath("/bar"); CuratorOp createOp2 = client.transactionOp().create().forPath("/z/blue"); final BlockingQueue callbackQueue = new LinkedBlockingQueue<>(); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { callbackQueue.add(event); } }; client.transaction().inBackground(callback).forOperations(createOp1, createOp2); CuratorEvent event = callbackQueue.poll(new Timing().milliseconds(), TimeUnit.MILLISECONDS); assertNotNull(event); assertNotNull(event.getOpResults()); assertEquals(event.getOpResults().size(), 2); assertEquals(event.getOpResults().get(0).getError(), KeeperException.Code.OK.intValue()); assertEquals(event.getOpResults().get(1).getError(), KeeperException.Code.NONODE.intValue()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCheckVersion() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/foo"); Stat stat = client.setData().forPath("/foo", "new".getBytes()); // up the version CuratorOp statOp = client.transactionOp().check().withVersion(stat.getVersion() + 1).forPath("/foo"); CuratorOp createOp = client.transactionOp().create().forPath("/bar"); try { client.transaction().forOperations(statOp, createOp); fail(); } catch ( KeeperException.BadVersionException correct ) { // correct } assertNull(client.checkExists().forPath("/bar")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testWithNamespace() throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace("galt").build(); try { client.start(); CuratorOp createOp1 = client.transactionOp().create().forPath("/foo", "one".getBytes()); CuratorOp createOp2 = client.transactionOp().create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/test-", "one".getBytes()); CuratorOp setDataOp = client.transactionOp().setData().forPath("/foo", "two".getBytes()); CuratorOp createOp3 = client.transactionOp().create().forPath("/foo/bar"); CuratorOp deleteOp = client.transactionOp().delete().forPath("/foo/bar"); Collection results = client.transaction().forOperations(createOp1, createOp2, setDataOp, createOp3, deleteOp); assertTrue(client.checkExists().forPath("/foo") != null); assertTrue(client.usingNamespace(null).checkExists().forPath("/galt/foo") != null); assertArrayEquals(client.getData().forPath("/foo"), "two".getBytes()); assertTrue(client.checkExists().forPath("/foo/bar") == null); CuratorTransactionResult ephemeralResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/test-")); assertNotNull(ephemeralResult); assertNotEquals(ephemeralResult.getResultPath(), "/test-"); assertTrue(ephemeralResult.getResultPath().startsWith("/test-")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); CuratorOp createOp1 = client.transactionOp().create().forPath("/foo"); CuratorOp createOp2 = client.transactionOp().create().forPath("/foo/bar", "snafu".getBytes()); Collection results = client.transaction().forOperations(createOp1, createOp2); assertTrue(client.checkExists().forPath("/foo/bar") != null); assertArrayEquals(client.getData().forPath("/foo/bar"), "snafu".getBytes()); CuratorTransactionResult fooResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo")); CuratorTransactionResult fooBarResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo/bar")); assertNotNull(fooResult); assertNotNull(fooBarResult); assertNotSame(fooResult, fooBarResult); assertEquals(fooResult.getResultPath(), "/foo"); assertEquals(fooBarResult.getResultPath(), "/foo/bar"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackground() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); CuratorOp createOp1 = client.transactionOp().create().forPath("/foo"); CuratorOp createOp2 = client.transactionOp().create().forPath("/foo/bar", "snafu".getBytes()); final BlockingQueue> queue = Queues.newLinkedBlockingQueue(); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { queue.add(event.getOpResults()); } }; client.transaction().inBackground(callback).forOperations(createOp1, createOp2); Collection results = queue.poll(5, TimeUnit.SECONDS); assertNotNull(results); assertTrue(client.checkExists().forPath("/foo/bar") != null); assertArrayEquals(client.getData().forPath("/foo/bar"), "snafu".getBytes()); CuratorTransactionResult fooResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo")); CuratorTransactionResult fooBarResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo/bar")); assertNotNull(fooResult); assertNotNull(fooBarResult); assertNotSame(fooResult, fooBarResult); assertEquals(fooResult.getResultPath(), "/foo"); assertEquals(fooBarResult.getResultPath(), "/foo/bar"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundWithNamespace() throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace("galt").build(); try { client.start(); CuratorOp createOp1 = client.transactionOp().create().forPath("/foo", "one".getBytes()); CuratorOp createOp2 = client.transactionOp().create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/test-", "one".getBytes()); CuratorOp setDataOp = client.transactionOp().setData().forPath("/foo", "two".getBytes()); CuratorOp createOp3 = client.transactionOp().create().forPath("/foo/bar"); CuratorOp deleteOp = client.transactionOp().delete().forPath("/foo/bar"); final BlockingQueue> queue = Queues.newLinkedBlockingQueue(); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { queue.add(event.getOpResults()); } }; client.transaction().inBackground(callback).forOperations(createOp1, createOp2, setDataOp, createOp3, deleteOp); Collection results = queue.poll(5, TimeUnit.SECONDS); assertNotNull(results); assertTrue(client.checkExists().forPath("/foo") != null); assertTrue(client.usingNamespace(null).checkExists().forPath("/galt/foo") != null); assertArrayEquals(client.getData().forPath("/foo"), "two".getBytes()); assertTrue(client.checkExists().forPath("/foo/bar") == null); CuratorTransactionResult ephemeralResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/test-")); assertNotNull(ephemeralResult); assertNotEquals(ephemeralResult.getResultPath(), "/test-"); assertTrue(ephemeralResult.getResultPath().startsWith("/test-")); } finally { CloseableUtils.closeQuietly(client); } } } TestTransactionsOld.java000066400000000000000000000203301442004423600374210ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Iterables; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.framework.api.transaction.OperationType; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.Test; import java.util.Collection; @SuppressWarnings("deprecation") public class TestTransactionsOld extends BaseClassForTests { @Test public void testCheckVersion() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/foo"); Stat stat = client.setData().forPath("/foo", "new".getBytes()); // up the version try { client.inTransaction() .check().withVersion(stat.getVersion() + 1).forPath("/foo") // force a bad version .and() .create().forPath("/bar") .and() .commit(); fail(); } catch ( KeeperException.BadVersionException correct ) { // correct } assertNull(client.checkExists().forPath("/bar")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testWithNamespace() throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace("galt").build(); try { client.start(); Collection results = client.inTransaction() .create().forPath("/foo", "one".getBytes()) .and() .create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/test-", "one".getBytes()) .and() .setData().forPath("/foo", "two".getBytes()) .and() .create().forPath("/foo/bar") .and() .delete().forPath("/foo/bar") .and() .commit(); assertTrue(client.checkExists().forPath("/foo") != null); assertTrue(client.usingNamespace(null).checkExists().forPath("/galt/foo") != null); assertArrayEquals(client.getData().forPath("/foo"), "two".getBytes()); assertTrue(client.checkExists().forPath("/foo/bar") == null); CuratorTransactionResult ephemeralResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/test-")); assertNotNull(ephemeralResult); assertNotEquals(ephemeralResult.getResultPath(), "/test-"); assertTrue(ephemeralResult.getResultPath().startsWith("/test-")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testWithCompression() throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).namespace("galt").build(); client.start(); try { Collection results = client.inTransaction() .create().compressed().forPath("/foo", "one".getBytes()) .and() .create().compressed().withACL(ZooDefs.Ids.READ_ACL_UNSAFE).forPath("/bar", "two".getBytes()) .and() .create().compressed().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/test-", "three".getBytes()) .and() .create().compressed().withMode(CreateMode.PERSISTENT).withACL(ZooDefs.Ids.READ_ACL_UNSAFE).forPath("/baz", "four".getBytes()) .and() .setData().compressed().withVersion(0).forPath("/foo", "five".getBytes()) .and() .commit(); assertTrue(client.checkExists().forPath("/foo") != null); assertArrayEquals(client.getData().decompressed().forPath("/foo"), "five".getBytes()); assertTrue(client.checkExists().forPath("/bar") != null); assertArrayEquals(client.getData().decompressed().forPath("/bar"), "two".getBytes()); assertEquals(client.getACL().forPath("/bar"), ZooDefs.Ids.READ_ACL_UNSAFE); CuratorTransactionResult ephemeralResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/test-")); assertNotNull(ephemeralResult); assertNotEquals(ephemeralResult.getResultPath(), "/test-"); assertTrue(ephemeralResult.getResultPath().startsWith("/test-")); assertTrue(client.checkExists().forPath("/baz") != null); assertArrayEquals(client.getData().decompressed().forPath("/baz"), "four".getBytes()); assertEquals(client.getACL().forPath("/baz"), ZooDefs.Ids.READ_ACL_UNSAFE); } finally { client.close(); } } @Test public void testBasic() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); Collection results = client.inTransaction() .create().forPath("/foo") .and() .create().forPath("/foo/bar", "snafu".getBytes()) .and() .commit(); assertTrue(client.checkExists().forPath("/foo/bar") != null); assertArrayEquals(client.getData().forPath("/foo/bar"), "snafu".getBytes()); CuratorTransactionResult fooResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo")); CuratorTransactionResult fooBarResult = Iterables.find(results, CuratorTransactionResult.ofTypeAndPath(OperationType.CREATE, "/foo/bar")); assertNotNull(fooResult); assertNotNull(fooBarResult); assertNotSame(fooResult, fooBarResult); assertEquals(fooResult.getResultPath(), "/foo"); assertEquals(fooBarResult.getResultPath(), "/foo/bar"); } finally { CloseableUtils.closeQuietly(client); } } } TestTtlNodes.java000066400000000000000000000066631442004423600360630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.Timing; import org.apache.zookeeper.CreateMode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; public class TestTtlNodes extends CuratorTestBase { @BeforeAll public static void setUpClass() { System.setProperty("zookeeper.extendedTypesEnabled", "true"); } @BeforeEach @Override public void setup() throws Exception { System.setProperty("znode.container.checkIntervalMs", "1"); super.setup(); } @AfterEach @Override public void teardown() throws Exception { super.teardown(); System.clearProperty("znode.container.checkIntervalMs"); } @Test public void testBasic() throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); client.create().withTtl(10).creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_WITH_TTL).forPath("/a/b/c"); Thread.sleep(20); assertNull(client.checkExists().forPath("/a/b/c")); } } @Test public void testBasicInBackground() throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); final CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { latch.countDown(); } }; client.create().withTtl(10).creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_WITH_TTL).inBackground(callback).forPath("/a/b/c"); assertTrue(new Timing().awaitLatch(latch)); Thread.sleep(20); assertNull(client.checkExists().forPath("/a/b/c")); } } } TestWatcherIdentity.java000066400000000000000000000150151442004423600374250ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import com.google.common.collect.Sets; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; public class TestWatcherIdentity extends BaseClassForTests { private static final String PATH = "/foo"; private static final int TIMEOUT_MS = 100000; private static class CountZKWatcher implements Watcher { private final AtomicInteger count = new AtomicInteger(0); @Override public void process(WatchedEvent event) { count.incrementAndGet(); } } @Test public void testSameWatcherPerZKDocs() throws Exception { CountZKWatcher actualWatcher = new CountZKWatcher(); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/test"); // per ZK docs, this watcher should only trigger once client.checkExists().usingWatcher(actualWatcher).forPath("/test"); client.getData().usingWatcher(actualWatcher).forPath("/test"); client.setData().forPath("/test", "foo".getBytes()); client.delete().forPath("/test"); Awaitility.await() .untilAsserted(() -> assertEquals(1, actualWatcher.count.getAndSet(0))); client.create().forPath("/test"); client.checkExists().usingWatcher(actualWatcher).forPath("/test"); client.delete().forPath("/test"); Awaitility.await() .untilAsserted(() -> assertEquals(1, actualWatcher.count.get())); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSameCuratorWatcherPerZKDocs() throws Exception { // Construct mock object CuratorWatcher actualWatcher = mock(CuratorWatcher.class); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/test"); // per ZK docs, this watcher should only trigger once client.checkExists().usingWatcher(actualWatcher).forPath("/test"); client.getData().usingWatcher(actualWatcher).forPath("/test"); client.setData().forPath("/test", "foo".getBytes()); client.delete().forPath("/test"); Mockito.verify(actualWatcher, Mockito.timeout(TIMEOUT_MS).times(1)).process(any(WatchedEvent.class)); client.create().forPath("/test"); client.checkExists().usingWatcher(actualWatcher).forPath("/test"); client.delete().forPath("/test"); Mockito.verify(actualWatcher, Mockito.timeout(TIMEOUT_MS).times(2)).process(any(WatchedEvent.class)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSetAddition() { Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { } }; NamespaceWatcher namespaceWatcher1 = new NamespaceWatcher(null, watcher, "/foo"); NamespaceWatcher namespaceWatcher2 = new NamespaceWatcher(null, watcher, "/foo"); assertEquals(namespaceWatcher1, namespaceWatcher2); assertFalse(namespaceWatcher1.equals(watcher)); assertFalse(watcher.equals(namespaceWatcher1)); Set set = Sets.newHashSet(); set.add(namespaceWatcher1); set.add(namespaceWatcher2); assertEquals(set.size(), 1); } @Test public void testCuratorWatcher() throws Exception { Timing timing = new Timing(); // Construct mock object CuratorWatcher watcher = mock(CuratorWatcher.class); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().forPath(PATH); // Add twice the same watcher on the same path client.getData().usingWatcher(watcher).forPath(PATH); client.getData().usingWatcher(watcher).forPath(PATH); // Ok, let's test it client.setData().forPath(PATH, new byte[] {}); Mockito.verify(watcher, Mockito.timeout(TIMEOUT_MS).times(1)).process(any(WatchedEvent.class)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testZKWatcher() throws Exception { Timing timing = new Timing(); CountZKWatcher watcher = new CountZKWatcher(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().forPath(PATH); // Add twice the same watcher on the same path client.getData().usingWatcher(watcher).forPath(PATH); client.getData().usingWatcher(watcher).forPath(PATH); // Ok, let's test it client.setData().forPath(PATH, new byte[] {}); Awaitility.await() .untilAsserted(() -> assertEquals(1, watcher.count.get())); } finally { CloseableUtils.closeQuietly(client); } } } TestWatcherRemovalManager.java000066400000000000000000000437471442004423600405510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.Timing; import org.apache.curator.test.WatchersDebug; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.junit.jupiter.api.Test; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; public class TestWatcherRemovalManager extends CuratorTestBase { @Test public void testSameWatcherDifferentPaths1Triggered() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); final CountDownLatch latch = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { latch.countDown(); } }; removerClient.checkExists().usingWatcher(watcher).forPath("/a/b/c"); removerClient.checkExists().usingWatcher(watcher).forPath("/d/e/f"); removerClient.create().creatingParentsIfNeeded().forPath("/d/e/f"); Timing timing = new Timing(); assertTrue(timing.awaitLatch(latch)); timing.sleepABit(); removerClient.removeWatchers(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testSameWatcherDifferentPaths() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { // NOP } }; removerClient.checkExists().usingWatcher(watcher).forPath("/a/b/c"); removerClient.checkExists().usingWatcher(watcher).forPath("/d/e/f"); assertEquals(removerClient.getWatcherRemovalManager().getEntries().size(), 2); removerClient.removeWatchers(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testSameWatcherDifferentKinds1Triggered() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); final CountDownLatch latch = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { latch.countDown(); } }; removerClient.create().creatingParentsIfNeeded().forPath("/a/b/c"); removerClient.checkExists().usingWatcher(watcher).forPath("/a/b/c"); removerClient.getData().usingWatcher(watcher).forPath("/a/b/c"); removerClient.setData().forPath("/a/b/c", "new".getBytes()); Timing timing = new Timing(); assertTrue(timing.awaitLatch(latch)); timing.sleepABit(); removerClient.removeWatchers(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testSameWatcherDifferentKinds() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { // NOP } }; removerClient.create().creatingParentsIfNeeded().forPath("/a/b/c"); removerClient.checkExists().usingWatcher(watcher).forPath("/a/b/c"); removerClient.getData().usingWatcher(watcher).forPath("/a/b/c"); removerClient.removeWatchers(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testWithRetry() throws Exception { server.stop(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); Watcher w = new Watcher() { @Override public void process(WatchedEvent event) { // NOP } }; try { removerClient.checkExists().usingWatcher(w).forPath("/one/two/three"); fail("Should have thrown ConnectionLossException"); } catch ( KeeperException.ConnectionLossException expected ) { // expected } assertEquals(removerClient.getWatcherRemovalManager().getEntries().size(), 0); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testWithRetryInBackground() throws Exception { server.stop(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); Watcher w = new Watcher() { @Override public void process(WatchedEvent event) { // NOP } }; final CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { latch.countDown(); } }; removerClient.checkExists().usingWatcher(w).inBackground(callback).forPath("/one/two/three"); assertTrue(new Timing().awaitLatch(latch)); assertEquals(removerClient.getWatcherRemovalManager().getEntries().size(), 0); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testMissingNode() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); Watcher w = new Watcher() { @Override public void process(WatchedEvent event) { // NOP } }; try { removerClient.getData().usingWatcher(w).forPath("/one/two/three"); fail("Should have thrown NoNodeException"); } catch ( KeeperException.NoNodeException expected ) { // expected } removerClient.removeWatchers(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testMissingNodeInBackground() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); Callable proc = new Callable() { @Override public Void call() throws Exception { client.start(); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); Watcher w = new Watcher() { @Override public void process(WatchedEvent event) { // NOP } }; final CountDownLatch latch = new CountDownLatch(1); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { latch.countDown(); } }; removerClient.getData().usingWatcher(w).inBackground(callback).forPath("/one/two/three"); assertTrue(new Timing().awaitLatch(latch)); assertEquals(removerClient.getWatcherRemovalManager().getEntries().size(), 0); removerClient.removeWatchers(); return null; } }; TestCleanState.test(client, proc); } @Test public void testBasic() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); internalTryBasic(client); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testBasicNamespace1() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); internalTryBasic(client.usingNamespace("foo")); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testBasicNamespace2() throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .namespace("hey") .build(); try { client.start(); internalTryBasic(client); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testBasicNamespace3() throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .namespace("hey") .build(); try { client.start(); internalTryBasic(client.usingNamespace("lakjsf")); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testSameWatcher() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/test"); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { // NOP } }; removerClient.getData().usingWatcher(watcher).forPath("/test"); assertEquals(removerClient.getRemovalManager().getEntries().size(), 1); removerClient.getData().usingWatcher(watcher).forPath("/test"); assertEquals(removerClient.getRemovalManager().getEntries().size(), 1); removerClient.removeWatchers(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testTriggered() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); final CountDownLatch latch = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if ( event.getType() == Event.EventType.NodeCreated ) { latch.countDown(); } } }; removerClient.checkExists().usingWatcher(watcher).forPath("/yo"); assertEquals(removerClient.getRemovalManager().getEntries().size(), 1); removerClient.create().forPath("/yo"); assertTrue(new Timing().awaitLatch(latch)); assertEquals(removerClient.getRemovalManager().getEntries().size(), 0); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testResetFromWatcher() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); final WatcherRemovalFacade removerClient = (WatcherRemovalFacade)client.newWatcherRemoveCuratorFramework(); final CountDownLatch createdLatch = new CountDownLatch(1); final CountDownLatch deletedLatch = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if ( event.getType() == Event.EventType.NodeCreated ) { try { removerClient.checkExists().usingWatcher(this).forPath("/yo"); } catch ( Exception e ) { e.printStackTrace(); } createdLatch.countDown(); } else if ( event.getType() == Event.EventType.NodeDeleted ) { deletedLatch.countDown(); } } }; removerClient.checkExists().usingWatcher(watcher).forPath("/yo"); assertEquals(removerClient.getRemovalManager().getEntries().size(), 1); removerClient.create().forPath("/yo"); assertTrue(timing.awaitLatch(createdLatch)); assertEquals(removerClient.getRemovalManager().getEntries().size(), 1); removerClient.delete().forPath("/yo"); assertTrue(timing.awaitLatch(deletedLatch)); assertEquals(removerClient.getRemovalManager().getEntries().size(), 0); } finally { TestCleanState.closeAndTestClean(client); } } private void internalTryBasic(CuratorFramework client) throws Exception { WatcherRemoveCuratorFramework removerClient = client.newWatcherRemoveCuratorFramework(); final CountDownLatch latch = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if ( event.getType() == Event.EventType.DataWatchRemoved ) { latch.countDown(); } } }; removerClient.checkExists().usingWatcher(watcher).forPath("/hey"); List existWatches = WatchersDebug.getExistWatches(client.getZookeeperClient().getZooKeeper()); assertEquals(existWatches.size(), 1); removerClient.removeWatchers(); assertTrue(new Timing().awaitLatch(latch)); existWatches = WatchersDebug.getExistWatches(client.getZookeeperClient().getZooKeeper()); assertEquals(existWatches.size(), 0); } } TestWatchesBuilder.java000066400000000000000000000731631442004423600372330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.api.CuratorListener; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.imps.FailedRemoveWatchManager.FailedRemoveWatchDetails; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ZookeeperFactory; import org.apache.zookeeper.AddWatchMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.WatcherType; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; public class TestWatchesBuilder extends CuratorTestBase { private AtomicReference registerConnectionStateListener(CuratorFramework client) { final AtomicReference state = new AtomicReference(); client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { state.set(newState); synchronized(state) { state.notify(); } } }); return state; } private boolean blockUntilDesiredConnectionState(AtomicReference stateRef, Timing timing, final ConnectionState desiredState) { if(stateRef.get() == desiredState) { return true; } //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized(stateRef) { if(stateRef.get() == desiredState) { return true; } try { stateRef.wait(timing.milliseconds()); return stateRef.get() == desiredState; } catch(InterruptedException e) { Thread.currentThread().interrupt(); return false; } } } @Test public void testRemoveCuratorDefaultWatcher() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final CountDownLatch removedLatch = new CountDownLatch(1); final String path = "/"; client.getCuratorListenable().addListener(new CuratorListener() { @Override public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception { if(event.getType() == CuratorEventType.WATCHED && event.getWatchedEvent().getType() == EventType.DataWatchRemoved) { removedLatch.countDown(); } } }); client.checkExists().watched().forPath(path); client.watches().removeAll().forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRemoveCuratorWatch() throws Exception { Timing timing = new Timing(); CuratorFrameworkImpl client = (CuratorFrameworkImpl)CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final CountDownLatch removedLatch = new CountDownLatch(1); final String path = "/"; CuratorWatcher watcher = new CuratorWatcher() { @Override public void process(WatchedEvent event) throws Exception { if(event.getPath().equals(path) && event.getType() == EventType.DataWatchRemoved) { removedLatch.countDown(); } } }; client.checkExists().usingWatcher(watcher).forPath(path); client.watches().remove(watcher).forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRemoveWatch() throws Exception { Timing timing = new Timing(); CuratorFrameworkImpl client = (CuratorFrameworkImpl)CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final CountDownLatch removedLatch = new CountDownLatch(1); final String path = "/"; Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved); client.checkExists().usingWatcher(watcher).forPath(path); client.watches().remove(watcher).forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRemoveWatchInBackgroundWithCallback() throws Exception { Timing timing = new Timing(); CuratorFrameworkImpl client = (CuratorFrameworkImpl)CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); //Make sure that the event fires on both the watcher and the callback. final CountDownLatch removedLatch = new CountDownLatch(2); final String path = "/"; Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if(event.getType() == CuratorEventType.REMOVE_WATCHES && event.getPath().equals(path)) { removedLatch.countDown(); } } }; client.checkExists().usingWatcher(watcher).forPath(path); client.watches().remove(watcher).ofType(WatcherType.Any).inBackground(callback).forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRemoveWatchInBackgroundWithNoCallback() throws Exception { Timing timing = new Timing(); CuratorFrameworkImpl client = (CuratorFrameworkImpl)CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final String path = "/"; final CountDownLatch removedLatch = new CountDownLatch(1); Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved); client.checkExists().usingWatcher(watcher).forPath(path); client.watches().remove(watcher).inBackground().forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRemoveAllWatches() throws Exception { Timing timing = new Timing(); CuratorFrameworkImpl client = (CuratorFrameworkImpl)CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final String path = "/"; final CountDownLatch removedLatch = new CountDownLatch(2); Watcher watcher1 = new CountDownWatcher(path, removedLatch, EventType.ChildWatchRemoved); Watcher watcher2 = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved); client.getChildren().usingWatcher(watcher1).forPath(path); client.checkExists().usingWatcher(watcher2).forPath(path); client.watches().removeAll().forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRemoveAllDataWatches() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final String path = "/"; final AtomicBoolean removedFlag = new AtomicBoolean(false); final CountDownLatch removedLatch = new CountDownLatch(1); Watcher watcher1 = new BooleanWatcher(path, removedFlag, EventType.ChildWatchRemoved); Watcher watcher2 = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved); client.getChildren().usingWatcher(watcher1).forPath(path); client.checkExists().usingWatcher(watcher2).forPath(path); client.watches().removeAll().ofType(WatcherType.Data).forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); assertEquals(removedFlag.get(), false); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRemoveAllChildWatches() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final String path = "/"; final AtomicBoolean removedFlag = new AtomicBoolean(false); final CountDownLatch removedLatch = new CountDownLatch(1); Watcher watcher1 = new BooleanWatcher(path, removedFlag, EventType.DataWatchRemoved); Watcher watcher2 = new CountDownWatcher(path, removedLatch, EventType.ChildWatchRemoved); client.checkExists().usingWatcher(watcher1).forPath(path); client.getChildren().usingWatcher(watcher2).forPath(path); client.watches().removeAll().ofType(WatcherType.Children).forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); assertEquals(removedFlag.get(), false); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRemoveLocalWatch() throws Exception { Timing timing = new Timing(); CuratorFrameworkImpl client = (CuratorFrameworkImpl)CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); AtomicReference stateRef = registerConnectionStateListener(client); final String path = "/"; final CountDownLatch removedLatch = new CountDownLatch(1); Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved); client.checkExists().usingWatcher(watcher).forPath(path); //Stop the server so we can check if we can remove watches locally when offline server.stop(); assertTrue(blockUntilDesiredConnectionState(stateRef, timing, ConnectionState.SUSPENDED)); client.watches().removeAll().locally().forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRemoveLocalWatchInBackground() throws Exception { Timing timing = new Timing(); CuratorFrameworkImpl client = (CuratorFrameworkImpl)CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); AtomicReference stateRef = registerConnectionStateListener(client); final String path = "/"; final CountDownLatch removedLatch = new CountDownLatch(1); Watcher watcher = new CountDownWatcher(path, removedLatch, EventType.DataWatchRemoved); client.checkExists().usingWatcher(watcher).forPath(path); //Stop the server so we can check if we can remove watches locally when offline server.stop(); assertTrue(blockUntilDesiredConnectionState(stateRef, timing, ConnectionState.SUSPENDED)); client.watches().removeAll().locally().inBackground().forPath(path); assertTrue(timing.awaitLatch(removedLatch), "Timed out waiting for watch removal"); } finally { CloseableUtils.closeQuietly(client); } } /** * Test the case where we try and remove an unregistered watcher. In this case we expect a NoWatcherException to * be thrown. * @throws Exception */ @Test public void testRemoveUnregisteredWatcher() throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final String path = "/"; Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { } }; try { client.watches().remove(watcher).forPath(path); fail("Expected KeeperException.NoWatcherException"); } catch ( KeeperException.NoWatcherException expected ) { // expected } } finally { CloseableUtils.closeQuietly(client); } } /** * Test the case where we try and remove an unregistered watcher but have the quietly flag set. In this case we expect success. * @throws Exception */ @Test public void testRemoveUnregisteredWatcherQuietly() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); final AtomicBoolean watcherRemoved = new AtomicBoolean(false); final String path = "/"; Watcher watcher = new BooleanWatcher(path, watcherRemoved, EventType.DataWatchRemoved); client.watches().remove(watcher).quietly().forPath(path); timing.sleepABit(); //There should be no watcher removed as none were registered. assertEquals(watcherRemoved.get(), false); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testGuaranteedRemoveWatch() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new RetryOneTime(1)). build(); try { client.start(); AtomicReference stateRef = registerConnectionStateListener(client); String path = "/"; CountDownLatch removeLatch = new CountDownLatch(1); Watcher watcher = new CountDownWatcher(path, removeLatch, EventType.DataWatchRemoved); client.checkExists().usingWatcher(watcher).forPath(path); server.stop(); assertTrue(blockUntilDesiredConnectionState(stateRef, timing, ConnectionState.SUSPENDED)); //Remove the watch while we're not connected try { client.watches().remove(watcher).guaranteed().forPath(path); fail(); } catch(KeeperException.ConnectionLossException e) { //Expected } server.restart(); timing.awaitLatch(removeLatch); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testGuaranteedRemoveWatchInBackground() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); try { client.start(); AtomicReference stateRef = registerConnectionStateListener(client); final CountDownLatch guaranteeAddedLatch = new CountDownLatch(1); ((CuratorFrameworkImpl)client).getFailedRemoveWatcherManager().debugListener = new FailedOperationManager.FailedOperationManagerListener() { @Override public void pathAddedForGuaranteedOperation( FailedRemoveWatchDetails detail) { guaranteeAddedLatch.countDown(); } }; String path = "/"; CountDownLatch removeLatch = new CountDownLatch(1); Watcher watcher = new CountDownWatcher(path, removeLatch, EventType.DataWatchRemoved); client.checkExists().usingWatcher(watcher).forPath(path); server.stop(); assertTrue(blockUntilDesiredConnectionState(stateRef, timing, ConnectionState.SUSPENDED)); //Remove the watch while we're not connected client.watches().remove(watcher).guaranteed().inBackground().forPath(path); timing.awaitLatch(guaranteeAddedLatch); server.restart(); timing.awaitLatch(removeLatch); } finally { CloseableUtils.closeQuietly(client); } } @Test @Tag(CuratorTestBase.zk36Group) public void testPersistentWatch() throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); client.blockUntilConnected(); CountDownLatch latch = new CountDownLatch(3); Watcher watcher = event -> latch.countDown(); client.watchers().add().withMode(AddWatchMode.PERSISTENT).usingWatcher(watcher).forPath("/test/foo"); client.create().creatingParentsIfNeeded().forPath("/test/foo"); client.setData().forPath("/test/foo", "hey".getBytes()); client.delete().forPath("/test/foo"); assertTrue(timing.awaitLatch(latch)); } } @Test @Tag(CuratorTestBase.zk36Group) public void testPersistentWatchInBackground() throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); client.blockUntilConnected(); CountDownLatch backgroundLatch = new CountDownLatch(1); BackgroundCallback backgroundCallback = (__, ___) -> backgroundLatch.countDown(); CountDownLatch latch = new CountDownLatch(3); Watcher watcher = event -> latch.countDown(); client.watchers().add().withMode(AddWatchMode.PERSISTENT).inBackground(backgroundCallback).usingWatcher(watcher).forPath("/test/foo"); client.create().creatingParentsIfNeeded().forPath("/test/foo"); client.setData().forPath("/test/foo", "hey".getBytes()); client.delete().forPath("/test/foo"); assertTrue(timing.awaitLatch(backgroundLatch)); assertTrue(timing.awaitLatch(latch)); } } @Test @Tag(CuratorTestBase.zk36Group) public void testPersistentRecursiveWatch() throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); client.blockUntilConnected(); CountDownLatch latch = new CountDownLatch(5); Watcher watcher = event -> latch.countDown(); client.watchers().add().withMode(AddWatchMode.PERSISTENT_RECURSIVE).usingWatcher(watcher).forPath("/test"); client.create().forPath("/test"); client.create().forPath("/test/a"); client.create().forPath("/test/a/b"); client.create().forPath("/test/a/b/c"); client.create().forPath("/test/a/b/c/d"); assertTrue(timing.awaitLatch(latch)); } } @Test @Tag(CuratorTestBase.zk36Group) public void testPersistentRecursiveWatchInBackground() throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); client.blockUntilConnected(); CountDownLatch backgroundLatch = new CountDownLatch(1); BackgroundCallback backgroundCallback = (__, ___) -> backgroundLatch.countDown(); CountDownLatch latch = new CountDownLatch(5); Watcher watcher = event -> latch.countDown(); client.watchers().add().withMode(AddWatchMode.PERSISTENT_RECURSIVE).inBackground(backgroundCallback).usingWatcher(watcher).forPath("/test"); client.create().forPath("/test"); client.create().forPath("/test/a"); client.create().forPath("/test/a/b"); client.create().forPath("/test/a/b/c"); client.create().forPath("/test/a/b/c/d"); assertTrue(timing.awaitLatch(backgroundLatch)); assertTrue(timing.awaitLatch(latch)); } } @Test @Tag(CuratorTestBase.zk36Group) public void testPersistentRecursiveDefaultWatch() throws Exception { CountDownLatch latch = new CountDownLatch(6); // 5 creates plus the initial sync ZookeeperFactory zookeeperFactory = (connectString, sessionTimeout, watcher, canBeReadOnly) -> { Watcher actualWatcher = event -> { watcher.process(event); latch.countDown(); }; return new ZooKeeper(connectString, sessionTimeout, actualWatcher); }; try (CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).zookeeperFactory(zookeeperFactory).build() ) { client.start(); client.blockUntilConnected(); client.watchers().add().withMode(AddWatchMode.PERSISTENT_RECURSIVE).forPath("/test"); client.create().forPath("/test"); client.create().forPath("/test/a"); client.create().forPath("/test/a/b"); client.create().forPath("/test/a/b/c"); client.create().forPath("/test/a/b/c/d"); assertTrue(timing.awaitLatch(latch)); } } private static class CountDownWatcher implements Watcher { private String path; private EventType eventType; private CountDownLatch removeLatch; public CountDownWatcher(String path, CountDownLatch removeLatch, EventType eventType) { this.path = path; this.eventType = eventType; this.removeLatch = removeLatch; } @Override public void process(WatchedEvent event) { if(event.getPath() == null || event.getType() == null) { return; } if(event.getPath().equals(path) && event.getType() == eventType) { removeLatch.countDown(); } } } private static class BooleanWatcher implements Watcher { private String path; private EventType eventType; private AtomicBoolean removedFlag; public BooleanWatcher(String path, AtomicBoolean removedFlag, EventType eventType) { this.path = path; this.eventType = eventType; this.removedFlag = removedFlag; } @Override public void process(WatchedEvent event) { if(event.getPath() == null || event.getType() == null) { return; } if(event.getPath().equals(path) && event.getType() == eventType) { removedFlag.set(true); } } } } TestWithCluster.java000066400000000000000000000126741442004423600366030ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.Timing; import org.apache.zookeeper.CreateMode; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; public class TestWithCluster extends CuratorTestBase { @Test public void testSessionSurvives() throws Exception { Timing timing = new Timing(); CuratorFramework client = null; TestingCluster cluster = createAndStartCluster(3); try { client = CuratorFrameworkFactory.newClient(cluster.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); client.start(); final CountDownLatch reconnectedLatch = new CountDownLatch(1); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.RECONNECTED ) { reconnectedLatch.countDown();; } } }; client.getConnectionStateListenable().addListener(listener); client.create().withMode(CreateMode.EPHEMERAL).forPath("/temp", "value".getBytes()); assertNotNull(client.checkExists().forPath("/temp")); for ( InstanceSpec spec : cluster.getInstances() ) { cluster.killServer(spec); timing.sleepABit(); cluster.restartServer(spec); timing.sleepABit(); } assertTrue(timing.awaitLatch(reconnectedLatch)); assertNotNull(client.checkExists().forPath("/temp")); } finally { CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(cluster); } } @Test public void testSplitBrain() throws Exception { Timing timing = new Timing(); CuratorFramework client = null; TestingCluster cluster = createAndStartCluster(3); try { // make sure all instances are up for ( InstanceSpec instanceSpec : cluster.getInstances() ) { client = CuratorFrameworkFactory.newClient(instanceSpec.getConnectString(), new RetryOneTime(1)); client.start(); client.checkExists().forPath("/"); client.close(); client = null; } client = CuratorFrameworkFactory.newClient(cluster.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); final CountDownLatch latch = new CountDownLatch(2); client.getConnectionStateListenable().addListener ( new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( (newState == ConnectionState.SUSPENDED) || (newState == ConnectionState.LOST) ) { latch.countDown(); } } } ); client.checkExists().forPath("/"); for ( InstanceSpec instanceSpec : cluster.getInstances() ) { if ( !instanceSpec.equals(cluster.findConnectionInstance(client.getZookeeperClient().getZooKeeper())) ) { assertTrue(cluster.killServer(instanceSpec)); } } assertTrue(timing.awaitLatch(latch)); } finally { CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(cluster); } } @Override protected void createServer() throws Exception { // NOP } } curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/schema/000077500000000000000000000000001442004423600331605ustar00rootroot00000000000000TestSchema.java000066400000000000000000000253761442004423600360210ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/schema/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.schema; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.google.common.base.Charsets; import com.google.common.collect.Maps; import com.google.common.io.Resources; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.ACL; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.List; import java.util.Map; public class TestSchema extends BaseClassForTests { @Test public void testBasics() throws Exception { SchemaSet schemaSet = loadSchemaSet("schema1.json", null); Schema schema = schemaSet.getNamedSchema("test"); assertNotNull(schema); Map expectedMetadata = Maps.newHashMap(); expectedMetadata.put("one", "1"); expectedMetadata.put("two", "2"); assertEquals(schema.getMetadata(), expectedMetadata); CuratorFramework client = newClient(schemaSet); try { client.start(); try { String rawPath = schema.getRawPath(); assertEquals(rawPath, "/a/b/c"); client.create().creatingParentsIfNeeded().forPath(rawPath); fail("Should've violated schema"); } catch ( SchemaViolation dummy ) { // expected } client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/a/b/c"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSchemaValidator() throws Exception { final SchemaValidator schemaValidator = new SchemaValidator() { @Override public boolean isValid(Schema schema, String path, byte[] data, List acl) { return data.length > 0; } }; SchemaSetLoader.SchemaValidatorMapper schemaValidatorMapper = new SchemaSetLoader.SchemaValidatorMapper() { @Override public SchemaValidator getSchemaValidator(String name) { return schemaValidator; } }; SchemaSet schemaSet = loadSchemaSet("schema3.json", schemaValidatorMapper); CuratorFramework client = newClient(schemaSet); try { client.start(); try { client.create().forPath("/test", new byte[0]); fail("Should've violated schema"); } catch ( SchemaViolation dummy ) { // expected } client.create().forPath("/test", "good".getBytes()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testMulti() throws Exception { SchemaSet schemaSet = loadSchemaSet("schema2.json", null); CuratorFramework client = newClient(schemaSet); try { client.start(); try { client.create().creatingParentsIfNeeded().forPath("/a/b/c"); fail("Should've violated schema: test"); } catch ( SchemaViolation dummy ) { // expected } try { client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/a/b/c/d/e"); fail("Should've violated schema: test2"); } catch ( SchemaViolation dummy ) { // expected } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testTransaction() throws Exception { final SchemaValidator schemaValidator = new SchemaValidator() { @Override public boolean isValid(Schema schema, String path, byte[] data, List acl) { return data.length > 0; } }; SchemaSetLoader.SchemaValidatorMapper schemaValidatorMapper = new SchemaSetLoader.SchemaValidatorMapper() { @Override public SchemaValidator getSchemaValidator(String name) { return schemaValidator; } }; SchemaSet schemaSet = loadSchemaSet("schema4.json", schemaValidatorMapper); CuratorFramework client = newClient(schemaSet); try { client.start(); CuratorOp createAPersistent = client.transactionOp().create().forPath("/a"); CuratorOp createAEphemeral = client.transactionOp().create().withMode(CreateMode.EPHEMERAL).forPath("/a"); CuratorOp deleteA = client.transactionOp().delete().forPath("/a"); CuratorOp createBEmptyData = client.transactionOp().create().forPath("/b", new byte[0]); CuratorOp createBWithData = client.transactionOp().create().forPath("/b", new byte[10]); CuratorOp setBEmptyData = client.transactionOp().setData().forPath("/b", new byte[0]); CuratorOp setBWithData = client.transactionOp().setData().forPath("/b", new byte[10]); try { client.transaction().forOperations(createAPersistent, createAEphemeral); fail("Should've violated schema"); } catch ( SchemaViolation dummy ) { // expected } client.transaction().forOperations(createAEphemeral); try { client.transaction().forOperations(deleteA); fail("Should've violated schema"); } catch ( SchemaViolation dummy ) { // expected } try { client.transaction().forOperations(createBEmptyData); fail("Should've violated schema"); } catch ( SchemaViolation dummy ) { // expected } client.transaction().forOperations(createBWithData); try { client.transaction().forOperations(setBEmptyData); fail("Should've violated schema"); } catch ( SchemaViolation dummy ) { // expected } client.transaction().forOperations(setBWithData); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testYaml() throws Exception { String yaml = Resources.toString(Resources.getResource("schema.yaml"), Charsets.UTF_8); JsonNode root = new ObjectMapper(new YAMLFactory()).readTree(yaml); List schemas = new SchemaSetLoader(root, null).getSchemas(); assertEquals(schemas.size(), 2); assertEquals(schemas.get(0).getName(), "test"); assertEquals(schemas.get(0).getMetadata().size(), 0); assertEquals(schemas.get(1).getName(), "test2"); assertEquals(schemas.get(1).getMetadata().size(), 2); assertEquals(schemas.get(1).getMetadata().get("two"), "2"); } @Test public void testOrdering() throws Exception { SchemaSet schemaSet = loadSchemaSet("schema5.json", null); CuratorFramework client = newClient(schemaSet); try { client.start(); try { client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/exact/match"); fail("Should've violated schema"); } catch ( SchemaViolation dummy ) { // expected } try { client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/exact/foo/bar"); fail("Should've violated schema"); } catch ( SchemaViolation dummy ) { // expected } try { client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/exact/other/bar"); fail("Should've violated schema"); } catch ( SchemaViolation dummy ) { // expected } client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/exact/match"); // schema "1" client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/exact/other/thing"); // schema "2" client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/exact/foo/bar"); // schema "3" } finally { CloseableUtils.closeQuietly(client); } } private CuratorFramework newClient(SchemaSet schemaSet) { return CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .schemaSet(schemaSet) .build(); } private SchemaSet loadSchemaSet(String name, SchemaSetLoader.SchemaValidatorMapper schemaValidatorMapper) throws IOException { String json = Resources.toString(Resources.getResource(name), Charsets.UTF_8); return new SchemaSetLoader(json, schemaValidatorMapper).toSchemaSet(true); } } curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/state/000077500000000000000000000000001442004423600330405ustar00rootroot00000000000000TestCircuitBreaker.java000066400000000000000000000073121442004423600373650ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.RetryPolicy; import org.apache.curator.retry.RetryForever; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.retry.RetryUntilElapsed; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class TestCircuitBreaker { private static Duration[] lastDelay = new Duration[]{Duration.ZERO}; private static ScheduledThreadPoolExecutor service = new ScheduledThreadPoolExecutor(1) { @Override public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { lastDelay[0] = Duration.of(unit.toNanos(delay), ChronoUnit.NANOS); command.run(); return null; } }; @AfterAll public static void tearDown() { service.shutdownNow(); } @Test public void testBasic() { final int retryQty = 1; final Duration delay = Duration.ofSeconds(10); CircuitBreaker circuitBreaker = CircuitBreaker.build(new RetryNTimes(retryQty, (int)delay.toMillis()), service); AtomicInteger counter = new AtomicInteger(0); assertTrue(circuitBreaker.tryToOpen(counter::incrementAndGet)); assertEquals(lastDelay[0], delay); assertFalse(circuitBreaker.tryToOpen(counter::incrementAndGet)); assertEquals(circuitBreaker.getRetryCount(), 1); assertEquals(counter.get(), 1); assertFalse(circuitBreaker.tryToRetry(counter::incrementAndGet)); assertEquals(circuitBreaker.getRetryCount(), 1); assertEquals(counter.get(), 1); assertTrue(circuitBreaker.close()); assertEquals(circuitBreaker.getRetryCount(), 0); assertFalse(circuitBreaker.close()); } @Test public void testVariousOpenRetryFails() { CircuitBreaker circuitBreaker = CircuitBreaker.build(new RetryForever(1), service); assertFalse(circuitBreaker.tryToRetry(() -> {})); assertTrue(circuitBreaker.tryToOpen(() -> {})); assertFalse(circuitBreaker.tryToOpen(() -> {})); assertTrue(circuitBreaker.close()); assertFalse(circuitBreaker.close()); } @Test public void testWithRetryUntilElapsed() { RetryPolicy retryPolicy = new RetryUntilElapsed(10000, 10000); CircuitBreaker circuitBreaker = CircuitBreaker.build(retryPolicy, service); assertTrue(circuitBreaker.tryToOpen(() -> {})); assertEquals(lastDelay[0], Duration.ofMillis(10000)); } } TestCircuitBreakingConnectionStateListener.java000066400000000000000000000261541442004423600442700ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.RetryPolicy; import org.apache.curator.RetrySleeper; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryForever; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.Timing2; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledThreadPoolExecutor; @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") public class TestCircuitBreakingConnectionStateListener { private final CuratorFramework dummyClient = CuratorFrameworkFactory.newClient("foo", new RetryOneTime(1)); private final Timing2 timing = new Timing2(); private final Timing2 retryTiming = timing.multiple(.25); private volatile ScheduledThreadPoolExecutor service; private static class RecordingListener implements ConnectionStateListener { final BlockingQueue stateChanges = new LinkedBlockingQueue<>(); @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { stateChanges.offer(newState); } } private class TestRetryPolicy extends RetryForever { volatile boolean isRetrying = true; public TestRetryPolicy() { super(retryTiming.milliseconds()); } @Override public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper) { return isRetrying && super.allowRetry(retryCount, elapsedTimeMs, sleeper); } } @BeforeEach public void setup() { service = new ScheduledThreadPoolExecutor(1); } @AfterEach public void tearDown() { service.shutdownNow(); } @Test public void testBasic() throws Exception { RecordingListener recordingListener = new RecordingListener(); TestRetryPolicy retryPolicy = new TestRetryPolicy(); CircuitBreakingConnectionStateListener listener = new CircuitBreakingConnectionStateListener(dummyClient, recordingListener, retryPolicy, service); listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.RECONNECTED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); // 2nd suspended is ignored assertTrue(recordingListener.stateChanges.isEmpty()); listener.stateChanged(dummyClient, ConnectionState.LOST); assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.LOST); synchronized(listener) // don't let retry policy run while we're pushing state changes { listener.stateChanged(dummyClient, ConnectionState.READ_ONLY); // all further events are ignored listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); // all further events are ignored listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); // all further events are ignored listener.stateChanged(dummyClient, ConnectionState.LOST); // all further events are ignored listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); // all further events are ignored - this will be the last event } retryTiming.multiple(2).sleep(); assertTrue(recordingListener.stateChanges.isEmpty()); retryPolicy.isRetrying = false; // retry policy will return false assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.SUSPENDED); } @Test public void testResetsAfterReconnect() throws Exception { RecordingListener recordingListener = new RecordingListener(); TestRetryPolicy retryPolicy = new TestRetryPolicy(); CircuitBreakingConnectionStateListener listener = new CircuitBreakingConnectionStateListener(dummyClient, recordingListener, retryPolicy, service); synchronized(listener) // don't let retry policy run while we're pushing state changes { listener.stateChanged(dummyClient, ConnectionState.LOST); listener.stateChanged(dummyClient, ConnectionState.LOST); // second LOST ignored } assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.LOST); assertTrue(recordingListener.stateChanges.isEmpty()); listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); // causes circuit to close on next retry assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.RECONNECTED); } @Test public void testRetryNever() throws Exception { RecordingListener recordingListener = new RecordingListener(); RetryPolicy retryNever = (retryCount, elapsedTimeMs, sleeper) -> false; CircuitBreakingConnectionStateListener listener = new CircuitBreakingConnectionStateListener(dummyClient, recordingListener, retryNever, service); listener.stateChanged(dummyClient, ConnectionState.LOST); assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.LOST); assertFalse(listener.isOpen()); listener.stateChanged(dummyClient, ConnectionState.LOST); assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.LOST); assertFalse(listener.isOpen()); } @Test public void testRetryOnce() throws Exception { RecordingListener recordingListener = new RecordingListener(); RetryPolicy retryOnce = new RetryOneTime(retryTiming.milliseconds()); CircuitBreakingConnectionStateListener listener = new CircuitBreakingConnectionStateListener(dummyClient, recordingListener, retryOnce, service); synchronized(listener) // don't let retry policy run while we're pushing state changes { listener.stateChanged(dummyClient, ConnectionState.LOST); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); assertTrue(listener.isOpen()); } assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.LOST); assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.SUSPENDED); assertFalse(listener.isOpen()); } @Test public void testSuspendedToLostRatcheting() throws Exception { RecordingListener recordingListener = new RecordingListener(); RetryPolicy retryInfinite = new RetryForever(Integer.MAX_VALUE); CircuitBreakingConnectionStateListener listener = new CircuitBreakingConnectionStateListener(dummyClient, recordingListener, retryInfinite, service); listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); assertFalse(listener.isOpen()); assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.RECONNECTED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); assertTrue(listener.isOpen()); assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); listener.stateChanged(dummyClient, ConnectionState.READ_ONLY); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); listener.stateChanged(dummyClient, ConnectionState.READ_ONLY); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); assertTrue(recordingListener.stateChanges.isEmpty()); assertTrue(listener.isOpen()); listener.stateChanged(dummyClient, ConnectionState.LOST); assertEquals(timing.takeFromQueue(recordingListener.stateChanges), ConnectionState.LOST); assertTrue(listener.isOpen()); listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); listener.stateChanged(dummyClient, ConnectionState.READ_ONLY); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); listener.stateChanged(dummyClient, ConnectionState.READ_ONLY); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); assertTrue(recordingListener.stateChanges.isEmpty()); assertTrue(listener.isOpen()); listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); listener.stateChanged(dummyClient, ConnectionState.READ_ONLY); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.LOST); listener.stateChanged(dummyClient, ConnectionState.LOST); listener.stateChanged(dummyClient, ConnectionState.LOST); listener.stateChanged(dummyClient, ConnectionState.RECONNECTED); listener.stateChanged(dummyClient, ConnectionState.READ_ONLY); listener.stateChanged(dummyClient, ConnectionState.LOST); listener.stateChanged(dummyClient, ConnectionState.SUSPENDED); listener.stateChanged(dummyClient, ConnectionState.LOST); assertTrue(recordingListener.stateChanges.isEmpty()); assertTrue(listener.isOpen()); } } TestConnectionStateManager.java000066400000000000000000000136021442004423600410610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/java/org/apache/curator/framework/state/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.state; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestConnectionStateManager extends BaseClassForTests { @Test @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public void testSessionConnectionStateErrorPolicyWithExpirationPercent30() throws Exception { Timing2 timing = new Timing2(); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .connectionTimeoutMs(1000) .sessionTimeoutMs(timing.session()) .retryPolicy(new RetryOneTime(1)) .connectionStateErrorPolicy(new SessionConnectionStateErrorPolicy()) .simulatedSessionExpirationPercent(30) .build(); // we should get LOST around 30% of a session plus a little "slop" for processing, etc. final int lostStateExpectedMs = (timing.session() / 3) + timing.forSleepingABit().milliseconds(); try { CountDownLatch connectedLatch = new CountDownLatch(1); CountDownLatch lostLatch = new CountDownLatch(1); ConnectionStateListener stateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.CONNECTED ) { connectedLatch.countDown(); } if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } } }; timing.sleepABit(); client.getConnectionStateListenable().addListener(stateListener); client.start(); assertTrue(timing.awaitLatch(connectedLatch)); server.close(); assertTrue(lostLatch.await(lostStateExpectedMs, TimeUnit.MILLISECONDS)); } finally { CloseableUtils.closeQuietly(client); } } @Test @Tag(CuratorTestBase.zk36Group) public void testConnectionStateRecoversFromUnexpectedExpiredConnection() throws Exception { Timing2 timing = new Timing2(); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .connectionTimeoutMs(1_000) .sessionTimeoutMs(250) // try to aggressively expire the connection .retryPolicy(new RetryOneTime(1)) .connectionStateErrorPolicy(new SessionConnectionStateErrorPolicy()) .build(); final BlockingQueue queue = Queues.newLinkedBlockingQueue(); ConnectionStateListener listener = (client1, state) -> queue.add(state); client.getConnectionStateListenable().addListener(listener); client.start(); try { ConnectionState polled = queue.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertEquals(polled, ConnectionState.CONNECTED); client.getZookeeperClient().getZooKeeper().getTestable().queueEvent(new WatchedEvent( Watcher.Event.EventType.None, Watcher.Event.KeeperState.Disconnected, null)); polled = queue.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertEquals(polled, ConnectionState.SUSPENDED); assertThrows(RuntimeException.class, () -> client.getZookeeperClient() .getZooKeeper().getTestable().queueEvent(new WatchedEvent( Watcher.Event.EventType.None, Watcher.Event.KeeperState.Expired, null) { @Override public String getPath() { // exception will cause ZooKeeper to update current state but fail to notify watchers throw new RuntimeException("Path doesn't exist!"); } })); polled = queue.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertEquals(polled, ConnectionState.LOST); polled = queue.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertEquals(polled, ConnectionState.RECONNECTED); } finally { CloseableUtils.closeQuietly(client); } } } curator-apache-curator-5.5.0/curator-framework/src/test/resources/000077500000000000000000000000001442004423600253255ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-framework/src/test/resources/log4j.properties000066400000000000000000000021071442004423600304620ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. log4j.rootLogger=ERROR, console log4j.logger.org.apache.curator=DEBUG, console log4j.additivity.org.apache.curator=false log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n curator-apache-curator-5.5.0/curator-framework/src/test/resources/schema.yaml000066400000000000000000000017601442004423600274550ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - name: test path: /a/b/c documentation: This is a schema ephemeral: must sequential: cannot - name: test2 path: /a/.* isRegex: true ephemeral: cannot canBeDeleted: false metadata: one: 1 two: 2 curator-apache-curator-5.5.0/curator-framework/src/test/resources/schema1.json000066400000000000000000000003141442004423600275370ustar00rootroot00000000000000[ { "name": "test", "path": "/a/b/c", "documentation": "This is a schema", "ephemeral": "must", "sequential": "cannot", "metadata": { "one": 1, "two": "2" } } ]curator-apache-curator-5.5.0/curator-framework/src/test/resources/schema2.json000066400000000000000000000004201442004423600275360ustar00rootroot00000000000000[ { "name": "test", "path": "/a/b/c", "documentation": "This is a schema", "ephemeral": "must", "sequential": "cannot" }, { "name": "test2", "path": "/a/.*", "isRegex": true, "ephemeral": "cannot", "canBeDeleted": false } ]curator-apache-curator-5.5.0/curator-framework/src/test/resources/schema3.json000066400000000000000000000001221442004423600275360ustar00rootroot00000000000000[ { "name": "test", "path": "/test", "schemaValidator": "test" } ]curator-apache-curator-5.5.0/curator-framework/src/test/resources/schema4.json000066400000000000000000000002641442004423600275460ustar00rootroot00000000000000[ { "name": "testa", "path": "/a", "ephemeral": "must", "canBeDeleted": false }, { "name": "testb", "path": "/b", "schemaValidator": "test" } ]curator-apache-curator-5.5.0/curator-framework/src/test/resources/schema5.json000066400000000000000000000005761442004423600275550ustar00rootroot00000000000000[ { "name": "1", "path": "/exact/match", "isRegex": false, "ephemeral": "cannot", "sequential": "must" }, { "name": "2", "path": "/exact/other/.*", "isRegex": true, "ephemeral": "must", "sequential": "can" }, { "name": "3", "path": "/exact/.*", "isRegex": true, "ephemeral": "can", "sequential": "cannot" } ]curator-apache-curator-5.5.0/curator-recipes/000077500000000000000000000000001442004423600212025ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/LICENSE000066400000000000000000000261361442004423600222170ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. curator-apache-curator-5.5.0/curator-recipes/NOTICE000066400000000000000000000002501442004423600221030ustar00rootroot00000000000000Apache Curator Copyright 2013-2023 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). curator-apache-curator-5.5.0/curator-recipes/pom.xml000066400000000000000000000073071442004423600225260ustar00rootroot00000000000000 4.0.0 org.apache.curator apache-curator 5.5.0 curator-recipes 5.5.0 bundle Curator Recipes All of the recipes listed on the ZooKeeper recipes doc (except two phase commit). 2011 org.apache.zookeeper.*;version="[3.4,4.0)", * org.apache.curator.framework.recipes.*;version="${project.version}";-noimport:=true org.apache.curator curator-framework org.apache.curator curator-framework test-jar test org.apache.curator curator-test test org.mockito mockito-core test org.junit.jupiter junit-jupiter-api test org.slf4j slf4j-log4j12 test org.apache.commons commons-math test org.awaitility awaitility test org.apache.maven.plugins maven-jar-plugin test-jar curator-apache-curator-5.5.0/curator-recipes/src/000077500000000000000000000000001442004423600217715ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/000077500000000000000000000000001442004423600227155ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/000077500000000000000000000000001442004423600236365ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/000077500000000000000000000000001442004423600244255ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/000077500000000000000000000000001442004423600256465ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/000077500000000000000000000000001442004423600273255ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/000077500000000000000000000000001442004423600313225ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/000077500000000000000000000000001442004423600327545ustar00rootroot00000000000000AfterConnectionEstablished.java000066400000000000000000000052161442004423600407750ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; /** * Utility class to allow execution of logic once a ZooKeeper connection becomes available. */ public class AfterConnectionEstablished { private final static Logger log = LoggerFactory.getLogger(AfterConnectionEstablished.class); /** * Spawns a new new background thread that will block until a connection is available and * then execute the 'runAfterConnection' logic * * @param client The curator client * @param runAfterConnection The logic to run * @return future of the task so it can be canceled, etc. if needed */ public static Future execute(final CuratorFramework client, final Runnable runAfterConnection) throws Exception { //Block until connected final ExecutorService executor = ThreadUtils.newSingleThreadExecutor(ThreadUtils.getProcessName(runAfterConnection.getClass())); Runnable internalCall = new Runnable() { @Override public void run() { try { client.blockUntilConnected(); runAfterConnection.run(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("An error occurred blocking until a connection is available", e); } finally { executor.shutdown(); } } }; return executor.submit(internalCall); } private AfterConnectionEstablished() { } } atomic/000077500000000000000000000000001442004423600341515ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipesAtomicStats.java000066400000000000000000000043441442004423600372540ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; /** * Debugging stats about operations */ public class AtomicStats { private int optimisticTries = 0; private int promotedLockTries = 0; private long optimisticTimeMs = 0; private long promotedTimeMs = 0; /** * Returns the number of optimistic locks used to perform the operation * * @return qty */ public int getOptimisticTries() { return optimisticTries; } /** * Returns the number of mutex locks used to perform the operation * * @return qty */ public int getPromotedLockTries() { return promotedLockTries; } /** * Returns the time spent trying the operation with optimistic locks * * @return time in ms */ public long getOptimisticTimeMs() { return optimisticTimeMs; } /** * Returns the time spent trying the operation with mutex locks * * @return time in ms */ public long getPromotedTimeMs() { return promotedTimeMs; } void incrementOptimisticTries() { ++optimisticTries; } void incrementPromotedTries() { ++promotedLockTries; } void setOptimisticTimeMs(long optimisticTimeMs) { this.optimisticTimeMs = optimisticTimeMs; } void setPromotedTimeMs(long promotedTimeMs) { this.promotedTimeMs = promotedTimeMs; } } AtomicValue.java000066400000000000000000000031631442004423600372300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; /** * Abstracts a value returned from one of the Atomics */ public interface AtomicValue { /** * MUST be checked. Returns true if the operation succeeded. If false is returned, * the operation failed and the atomic was not updated. * * @return true/false */ public boolean succeeded(); /** * Returns the value of the counter prior to the operation * * @return pre-operation value */ public T preValue(); /** * Returns the value of the counter after to the operation * * @return post-operation value */ public T postValue(); /** * Returns debugging stats about the operation * * @return stats */ public AtomicStats getStats(); } CachedAtomicInteger.java000066400000000000000000000046641442004423600406500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; /** * Uses an {@link DistributedAtomicNumber} and allocates values in chunks for better performance */ public class CachedAtomicInteger { private final DistributedAtomicInteger number; private final int cacheFactor; private AtomicValue currentValue = null; private int currentIndex = 0; /** * @param number the number to use * @param cacheFactor the number of values to allocate at a time */ public CachedAtomicInteger(DistributedAtomicInteger number, int cacheFactor) { this.number = number; this.cacheFactor = cacheFactor; } /** * Returns the next value (incrementing by 1). If a new chunk of numbers is needed, it is * requested from the number * * @return next increment * @throws Exception errors */ public AtomicValue next() throws Exception { MutableAtomicValue result = new MutableAtomicValue(0, 0); if ( currentValue == null ) { currentValue = number.add(cacheFactor); if ( !currentValue.succeeded() ) { currentValue = null; result.succeeded = false; return result; } currentIndex = 0; } result.succeeded = true; result.preValue = currentValue.preValue() + currentIndex; result.postValue = result.preValue + 1; if ( ++currentIndex >= cacheFactor ) { currentValue = null; } return result; } } CachedAtomicLong.java000066400000000000000000000046341442004423600401470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; /** * Uses an {@link DistributedAtomicNumber} and allocates values in chunks for better performance */ public class CachedAtomicLong { private final DistributedAtomicLong number; private final long cacheFactor; private AtomicValue currentValue = null; private int currentIndex = 0; /** * @param number the number to use * @param cacheFactor the number of values to allocate at a time */ public CachedAtomicLong(DistributedAtomicLong number, int cacheFactor) { this.number = number; this.cacheFactor = cacheFactor; } /** * Returns the next value (incrementing by 1). If a new chunk of numbers is needed, it is * requested from the number * * @return next increment * @throws Exception errors */ public AtomicValue next() throws Exception { MutableAtomicValue result = new MutableAtomicValue(0L, 0L); if ( currentValue == null ) { currentValue = number.add(cacheFactor); if ( !currentValue.succeeded() ) { currentValue = null; result.succeeded = false; return result; } currentIndex = 0; } result.succeeded = true; result.preValue = currentValue.preValue() + currentIndex; result.postValue = result.preValue + 1; if ( ++currentIndex >= cacheFactor ) { currentValue = null; } return result; } } DistributedAtomicInteger.java000066400000000000000000000165241442004423600417610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; /** *

A counter that attempts atomic increments. It first tries uses optimistic locking. If that fails, * an optional {@link InterProcessMutex} is taken. For both optimistic and mutex, a retry policy is used to * retry the increment.

* *

The various increment methods return an {@link AtomicValue} object. You must always check * {@link AtomicValue#succeeded()}. None of the methods (other than get()) are guaranteed to succeed.

*/ public class DistributedAtomicInteger implements DistributedAtomicNumber { private final DistributedAtomicValue value; /** * Creates in optimistic mode only - i.e. the promotion to a mutex is not done * * @param client the client * @param counterPath path to hold the value * @param retryPolicy the retry policy to use */ public DistributedAtomicInteger(CuratorFramework client, String counterPath, RetryPolicy retryPolicy) { this(client, counterPath, retryPolicy, null); } /** * Creates in mutex promotion mode. The optimistic lock will be tried first using * the given retry policy. If the increment does not succeed, a {@link InterProcessMutex} will be tried * with its own retry policy * * @param client the client * @param counterPath path to hold the value * @param retryPolicy the retry policy to use * @param promotedToLock the arguments for the mutex promotion */ public DistributedAtomicInteger(CuratorFramework client, String counterPath, RetryPolicy retryPolicy, PromotedToLock promotedToLock) { value = new DistributedAtomicValue(client, counterPath, retryPolicy, promotedToLock); } @Override public AtomicValue get() throws Exception { return new AtomicInteger(value.get()); } @Override public void forceSet(Integer newValue) throws Exception { value.forceSet(valueToBytes(newValue)); } @Override public AtomicValue compareAndSet(Integer expectedValue, Integer newValue) throws Exception { return new AtomicInteger(value.compareAndSet(valueToBytes(expectedValue), valueToBytes(newValue))); } @Override public AtomicValue trySet(Integer newValue) throws Exception { return new AtomicInteger(value.trySet(valueToBytes(newValue))); } @Override public boolean initialize(Integer initialize) throws Exception { return value.initialize(valueToBytes(initialize)); } /** * Add 1 to the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @return value info * @throws Exception ZooKeeper errors */ @Override public AtomicValue increment() throws Exception { return worker(1); } /** * Subtract 1 from the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @return value info * @throws Exception ZooKeeper errors */ @Override public AtomicValue decrement() throws Exception { return worker(-1); } /** * Add delta to the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @param delta amount to add * @return value info * @throws Exception ZooKeeper errors */ @Override public AtomicValue add(Integer delta) throws Exception { return worker(delta); } /** * Subtract delta from the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @param delta amount to subtract * @return value info * @throws Exception ZooKeeper errors */ @Override public AtomicValue subtract(Integer delta) throws Exception { return worker(-1 * delta); } @VisibleForTesting byte[] valueToBytes(Integer newValue) { Preconditions.checkNotNull(newValue, "newValue cannot be null"); byte[] newData = new byte[4]; ByteBuffer wrapper = ByteBuffer.wrap(newData); wrapper.putInt(newValue); return newData; } @VisibleForTesting int bytesToValue(byte[] data) { if ( (data == null) || (data.length == 0) ) { return 0; } ByteBuffer wrapper = ByteBuffer.wrap(data); try { return wrapper.getInt(); } catch ( BufferUnderflowException e ) { throw value.createCorruptionException(data); } catch ( BufferOverflowException e ) { throw value.createCorruptionException(data); } } private AtomicValue worker(final Integer addAmount) throws Exception { Preconditions.checkNotNull(addAmount, "addAmount cannot be null"); MakeValue makeValue = new MakeValue() { @Override public byte[] makeFrom(byte[] previous) { int previousValue = (previous != null) ? bytesToValue(previous) : 0; int newValue = previousValue + addAmount; return valueToBytes(newValue); } }; AtomicValue result = value.trySet(makeValue); return new AtomicInteger(result); } private class AtomicInteger implements AtomicValue { private AtomicValue bytes; private AtomicInteger(AtomicValue bytes) { this.bytes = bytes; } @Override public boolean succeeded() { return bytes.succeeded(); } @Override public Integer preValue() { return bytesToValue(bytes.preValue()); } @Override public Integer postValue() { return bytesToValue(bytes.postValue()); } @Override public AtomicStats getStats() { return bytes.getStats(); } } } DistributedAtomicLong.java000066400000000000000000000164011442004423600412550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; /** *

A counter that attempts atomic increments. It first tries uses optimistic locking. If that fails, * an optional {@link InterProcessMutex} is taken. For both optimistic and mutex, a retry policy is used to * retry the increment.

* *

The various increment methods return an {@link AtomicValue} object. You must always check * {@link AtomicValue#succeeded()}. None of the methods (other than get()) are guaranteed to succeed.

*/ public class DistributedAtomicLong implements DistributedAtomicNumber { private final DistributedAtomicValue value; /** * Creates in optimistic mode only - i.e. the promotion to a mutex is not done * * @param client the client * @param counterPath path to hold the value * @param retryPolicy the retry policy to use */ public DistributedAtomicLong(CuratorFramework client, String counterPath, RetryPolicy retryPolicy) { this(client, counterPath, retryPolicy, null); } /** * Creates in mutex promotion mode. The optimistic lock will be tried first using * the given retry policy. If the increment does not succeed, a {@link InterProcessMutex} will be tried * with its own retry policy * * @param client the client * @param counterPath path to hold the value * @param retryPolicy the retry policy to use * @param promotedToLock the arguments for the mutex promotion */ public DistributedAtomicLong(CuratorFramework client, String counterPath, RetryPolicy retryPolicy, PromotedToLock promotedToLock) { value = new DistributedAtomicValue(client, counterPath, retryPolicy, promotedToLock); } @Override public AtomicValue get() throws Exception { return new AtomicLong(value.get()); } @Override public void forceSet(Long newValue) throws Exception { value.forceSet(valueToBytes(newValue)); } @Override public AtomicValue compareAndSet(Long expectedValue, Long newValue) throws Exception { return new AtomicLong(value.compareAndSet(valueToBytes(expectedValue), valueToBytes(newValue))); } @Override public AtomicValue trySet(Long newValue) throws Exception { return new AtomicLong(value.trySet(valueToBytes(newValue))); } @Override public boolean initialize(Long initialize) throws Exception { return value.initialize(valueToBytes(initialize)); } /** * Add 1 to the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @return value info * @throws Exception ZooKeeper errors */ @Override public AtomicValue increment() throws Exception { return worker(1L); } /** * Subtract 1 from the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @return value info * @throws Exception ZooKeeper errors */ @Override public AtomicValue decrement() throws Exception { return worker(-1L); } /** * Add delta to the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @param delta amount to add * @return value info * @throws Exception ZooKeeper errors */ @Override public AtomicValue add(Long delta) throws Exception { return worker(delta); } /** * Subtract delta from the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @param delta amount to subtract * @return value info * @throws Exception ZooKeeper errors */ @Override public AtomicValue subtract(Long delta) throws Exception { return worker(-1 * delta); } @VisibleForTesting byte[] valueToBytes(Long newValue) { Preconditions.checkNotNull(newValue, "newValue cannot be null"); byte[] newData = new byte[8]; ByteBuffer wrapper = ByteBuffer.wrap(newData); wrapper.putLong(newValue); return newData; } @VisibleForTesting long bytesToValue(byte[] data) { if ( (data == null) || (data.length == 0) ) { return 0; } ByteBuffer wrapper = ByteBuffer.wrap(data); try { return wrapper.getLong(); } catch ( BufferUnderflowException e ) { throw value.createCorruptionException(data); } catch ( BufferOverflowException e ) { throw value.createCorruptionException(data); } } private AtomicValue worker(final Long addAmount) throws Exception { Preconditions.checkNotNull(addAmount, "addAmount cannot be null"); MakeValue makeValue = new MakeValue() { @Override public byte[] makeFrom(byte[] previous) { long previousValue = (previous != null) ? bytesToValue(previous) : 0; long newValue = previousValue + addAmount; return valueToBytes(newValue); } }; AtomicValue result = value.trySet(makeValue); return new AtomicLong(result); } private class AtomicLong implements AtomicValue { private AtomicValue bytes; private AtomicLong(AtomicValue bytes) { this.bytes = bytes; } @Override public boolean succeeded() { return bytes.succeeded(); } @Override public Long preValue() { return bytesToValue(bytes.preValue()); } @Override public Long postValue() { return bytesToValue(bytes.postValue()); } @Override public AtomicStats getStats() { return bytes.getStats(); } } } DistributedAtomicNumber.java000066400000000000000000000074711442004423600416150ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; public interface DistributedAtomicNumber { /** * Returns the current value of the counter. NOTE: if the value has never been set, * 0 is returned. * * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue get() throws Exception; /** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * Remember to always check {@link AtomicValue#succeeded()}. * * @param expectedValue the expected value * @param newValue the new value for the counter * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue compareAndSet(T expectedValue, T newValue) throws Exception; /** * Attempt to atomically set the value to the given value. Remember to always * check {@link AtomicValue#succeeded()}. * * @param newValue the value to set * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue trySet(T newValue) throws Exception; /** * Atomic values are initially set to the equivalent of NULL in a database. * Use this method to initialize the value. The value will be set if and only iff the node does not exist. * * @param value the initial value to set * @return true if the value was set, false if the node already existed * @throws Exception ZooKeeper errors */ public boolean initialize(T value) throws Exception; /** * Forcibly sets the value of the counter without any guarantees of atomicity. * * @param newValue the new value * @throws Exception ZooKeeper errors */ public void forceSet(T newValue) throws Exception; /** * Add 1 to the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue increment() throws Exception; /** * Subtract 1 from the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue decrement() throws Exception; /** * Add delta to the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @param delta amount to add * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue add(T delta) throws Exception; /** * Subtract delta from the current value and return the new value information. Remember to always * check {@link AtomicValue#succeeded()}. * * @param delta amount to subtract * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue subtract(T delta) throws Exception; } DistributedAtomicValue.java000066400000000000000000000272111442004423600414330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; import org.apache.curator.RetryLoop; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import org.apache.curator.utils.PathUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; import java.util.Arrays; /** *

A distributed value that attempts atomic sets. It first tries uses optimistic locking. If that fails, * an optional {@link InterProcessMutex} is taken. For both optimistic and mutex, a retry policy is used to * retry the increment.

* *

The various methods return an {@link AtomicValue} object. You must always check * {@link AtomicValue#succeeded()}. None of the methods (other than get()) are guaranteed to succeed.

*/ public class DistributedAtomicValue { private final CuratorFramework client; private final String path; private final RetryPolicy retryPolicy; private final PromotedToLock promotedToLock; private final InterProcessMutex mutex; /** * Creates in optimistic mode only - i.e. the promotion to a mutex is not done * * @param client the client * @param path path to hold the value * @param retryPolicy the retry policy to use */ public DistributedAtomicValue(CuratorFramework client, String path, RetryPolicy retryPolicy) { this(client, path, retryPolicy, null); } /** * Creates in mutex promotion mode. The optimistic lock will be tried first using * the given retry policy. If the increment does not succeed, a {@link InterProcessMutex} will be tried * with its own retry policy * * @param client the client * @param path path to hold the value * @param retryPolicy the retry policy to use * @param promotedToLock the arguments for the mutex promotion */ public DistributedAtomicValue(CuratorFramework client, String path, RetryPolicy retryPolicy, PromotedToLock promotedToLock) { this.client = client; this.path = PathUtils.validatePath(path); this.retryPolicy = retryPolicy; this.promotedToLock = promotedToLock; mutex = (promotedToLock != null) ? new InterProcessMutex(client, promotedToLock.getPath()) : null; } /** * Returns the current value of the counter. NOTE: if the value has never been set, * 0 is returned. * * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue get() throws Exception { MutableAtomicValue result = new MutableAtomicValue(null, null, false); getCurrentValue(result, new Stat()); result.postValue = result.preValue; result.succeeded = true; return result; } /** * Forcibly sets the value any guarantees of atomicity. * * @param newValue the new value * @throws Exception ZooKeeper errors */ public void forceSet(byte[] newValue) throws Exception { try { client.setData().forPath(path, newValue); } catch ( KeeperException.NoNodeException dummy ) { try { client.create().creatingParentContainersIfNeeded().forPath(path, newValue); } catch ( KeeperException.NodeExistsException dummy2 ) { client.setData().forPath(path, newValue); } } } /** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * Remember to always check {@link AtomicValue#succeeded()}. * * * @param expectedValue the expected value * @param newValue the new value * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue compareAndSet(byte[] expectedValue, byte[] newValue) throws Exception { Stat stat = new Stat(); MutableAtomicValue result = new MutableAtomicValue(null, null, false); boolean createIt = getCurrentValue(result, stat); if ( !createIt && Arrays.equals(expectedValue, result.preValue) ) { try { client.setData().withVersion(stat.getVersion()).forPath(path, newValue); result.succeeded = true; result.postValue = newValue; } catch ( KeeperException.BadVersionException dummy ) { result.succeeded = false; } catch ( KeeperException.NoNodeException dummy ) { result.succeeded = false; } } else { result.succeeded = false; } return result; } /** * Attempt to atomically set the value to the given value. Remember to always * check {@link AtomicValue#succeeded()}. * * @param newValue the value to set * @return value info * @throws Exception ZooKeeper errors */ public AtomicValue trySet(final byte[] newValue) throws Exception { MutableAtomicValue result = new MutableAtomicValue(null, null, false); MakeValue makeValue = new MakeValue() { @Override public byte[] makeFrom(byte[] previous) { return newValue; } }; tryOptimistic(result, makeValue); if ( !result.succeeded() && (mutex != null) ) { tryWithMutex(result, makeValue); } return result; } /** * Atomic values are initially set to the equivalent of NULL in a database. * Use this method to initialize the value. The value will be set if and only iff the node does not exist. * * @param value the initial value to set * @return true if the value was set, false if the node already existed * @throws Exception ZooKeeper errors */ public boolean initialize(byte[] value) throws Exception { try { client.create().creatingParentContainersIfNeeded().forPath(path, value); } catch ( KeeperException.NodeExistsException ignore ) { // ignore return false; } return true; } AtomicValue trySet(MakeValue makeValue) throws Exception { MutableAtomicValue result = new MutableAtomicValue(null, null, false); tryOptimistic(result, makeValue); if ( !result.succeeded() && (mutex != null) ) { tryWithMutex(result, makeValue); } return result; } RuntimeException createCorruptionException(byte[] bytes) { StringBuilder str = new StringBuilder(); str.append('['); boolean first = true; for ( byte b : bytes ) { if ( first ) { first = false; } else { str.append(", "); } str.append("0x").append(Integer.toHexString((b & 0xff))); } str.append(']'); return new RuntimeException(String.format("Corrupted data for node \"%s\": %s", path, str.toString())); } private boolean getCurrentValue(MutableAtomicValue result, Stat stat) throws Exception { boolean createIt = false; try { result.preValue = client.getData().storingStatIn(stat).forPath(path); } catch ( KeeperException.NoNodeException e ) { result.preValue = null; createIt = true; } return createIt; } private void tryWithMutex(MutableAtomicValue result, MakeValue makeValue) throws Exception { long startMs = System.currentTimeMillis(); int retryCount = 0; if ( mutex.acquire(promotedToLock.getMaxLockTime(), promotedToLock.getMaxLockTimeUnit()) ) { try { boolean done = false; while ( !done ) { result.stats.incrementPromotedTries(); if ( tryOnce(result, makeValue) ) { result.succeeded = true; done = true; } else { if ( !promotedToLock.getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMs, RetryLoop.getDefaultRetrySleeper()) ) { done = true; } } } } finally { mutex.release(); } } result.stats.setPromotedTimeMs(System.currentTimeMillis() - startMs); } private void tryOptimistic(MutableAtomicValue result, MakeValue makeValue) throws Exception { long startMs = System.currentTimeMillis(); int retryCount = 0; boolean done = false; while ( !done ) { result.stats.incrementOptimisticTries(); if ( tryOnce(result, makeValue) ) { result.succeeded = true; done = true; } else { if ( !retryPolicy.allowRetry(retryCount++, System.currentTimeMillis() - startMs, RetryLoop.getDefaultRetrySleeper()) ) { done = true; } } } result.stats.setOptimisticTimeMs(System.currentTimeMillis() - startMs); } private boolean tryOnce(MutableAtomicValue result, MakeValue makeValue) throws Exception { Stat stat = new Stat(); boolean createIt = getCurrentValue(result, stat); boolean success = false; try { byte[] newValue = makeValue.makeFrom(result.preValue); if ( createIt ) { client.create().creatingParentContainersIfNeeded().forPath(path, newValue); } else { client.setData().withVersion(stat.getVersion()).forPath(path, newValue); } result.postValue = Arrays.copyOf(newValue, newValue.length); success = true; } catch ( KeeperException.NodeExistsException e ) { // do Retry } catch ( KeeperException.BadVersionException e ) { // do Retry } catch ( KeeperException.NoNodeException e ) { // do Retry } return success; } } MakeValue.java000066400000000000000000000016521442004423600366720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; interface MakeValue { public byte[] makeFrom(byte[] previous); } MutableAtomicValue.java000066400000000000000000000031461442004423600405430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; class MutableAtomicValue implements AtomicValue { T preValue; T postValue; boolean succeeded = false; AtomicStats stats = new AtomicStats(); MutableAtomicValue(T preValue, T postValue) { this(preValue, postValue, false); } MutableAtomicValue(T preValue, T postValue, boolean succeeded) { this.preValue = preValue; this.postValue = postValue; this.succeeded = succeeded; } @Override public T preValue() { return preValue; } @Override public T postValue() { return postValue; } @Override public boolean succeeded() { return succeeded; } @Override public AtomicStats getStats() { return stats; } } PromotedToLock.java000066400000000000000000000075771442004423600377410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; import com.google.common.base.Preconditions; import org.apache.curator.RetryPolicy; import org.apache.curator.retry.RetryNTimes; import java.util.concurrent.TimeUnit; import org.apache.curator.utils.PathUtils; /** * Abstraction of arguments for mutex promotion. Use {@link #builder()} to create. */ public class PromotedToLock { private final String path; private final long maxLockTime; private final TimeUnit maxLockTimeUnit; private final RetryPolicy retryPolicy; /** * Allocate a new builder * * @return new builder */ public static Builder builder() { return new Builder(); } public static class Builder { private PromotedToLock instance = new PromotedToLock(null, -1, null, new RetryNTimes(0, 0)); /** * Build the argument block * * @return new block */ public PromotedToLock build() { Preconditions.checkNotNull(instance.path, "path cannot be null"); Preconditions.checkNotNull(instance.retryPolicy, "retryPolicy cannot be null"); return new PromotedToLock(instance.path, instance.maxLockTime, instance.maxLockTimeUnit, instance.retryPolicy); } /** * Set the path for the mutex lock (required) * * @param path path * @return this */ public Builder lockPath(String path) { instance = new PromotedToLock(PathUtils.validatePath(path), instance.maxLockTime, instance.maxLockTimeUnit, instance.retryPolicy); return this; } /** * Set the retry policy to use when an operation does not succeed * * @param retryPolicy new policy * @return this */ public Builder retryPolicy(RetryPolicy retryPolicy) { instance = new PromotedToLock(instance.path, instance.maxLockTime, instance.maxLockTimeUnit, retryPolicy); return this; } /** * Set the timeout to use when locking (optional) * * @param maxLockTime time * @param maxLockTimeUnit unit * @return this */ public Builder timeout(long maxLockTime, TimeUnit maxLockTimeUnit) { instance = new PromotedToLock(instance.path, maxLockTime, maxLockTimeUnit, instance.retryPolicy); return this; } private Builder() { } } String getPath() { return path; } long getMaxLockTime() { return maxLockTime; } TimeUnit getMaxLockTimeUnit() { return maxLockTimeUnit; } RetryPolicy getRetryPolicy() { return retryPolicy; } private PromotedToLock(String path, long maxLockTime, TimeUnit maxLockTimeUnit, RetryPolicy retryPolicy) { this.path = path; this.maxLockTime = maxLockTime; this.maxLockTimeUnit = maxLockTimeUnit; this.retryPolicy = retryPolicy; } } barriers/000077500000000000000000000000001442004423600345065ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipesDistributedBarrier.java000066400000000000000000000102701442004423600411420ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.barriers; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import java.util.concurrent.TimeUnit; import org.apache.curator.utils.PathUtils; /** *

* A barrier as described in the ZK recipes. Quoting the recipe: *

* *
* Distributed systems use barriers to block processing of a set of nodes * until a condition is met at which time all the nodes are allowed to proceed *
*/ public class DistributedBarrier { private final CuratorFramework client; private final String barrierPath; private final Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { client.postSafeNotify(DistributedBarrier.this); } }; /** * @param client client * @param barrierPath path to use as the barrier */ public DistributedBarrier(CuratorFramework client, String barrierPath) { this.client = client; this.barrierPath = PathUtils.validatePath(barrierPath); } /** * Utility to set the barrier node * * @throws Exception errors */ public synchronized void setBarrier() throws Exception { try { client.create().creatingParentContainersIfNeeded().forPath(barrierPath); } catch ( KeeperException.NodeExistsException ignore ) { // ignore } } /** * Utility to remove the barrier node * * @throws Exception errors */ public synchronized void removeBarrier() throws Exception { try { client.delete().forPath(barrierPath); } catch ( KeeperException.NoNodeException ignore ) { // ignore } } /** * Blocks until the barrier node comes into existence * * @throws Exception errors */ public synchronized void waitOnBarrier() throws Exception { waitOnBarrier(-1, null); } /** * Blocks until the barrier no longer exists or the timeout elapses * * @param maxWait max time to block * @param unit time unit * @return true if the wait was successful, false if the timeout elapsed first * @throws Exception errors */ public synchronized boolean waitOnBarrier(long maxWait, TimeUnit unit) throws Exception { long startMs = System.currentTimeMillis(); boolean hasMaxWait = (unit != null); long maxWaitMs = hasMaxWait ? TimeUnit.MILLISECONDS.convert(maxWait, unit) : Long.MAX_VALUE; boolean result; for(;;) { result = (client.checkExists().usingWatcher(watcher).forPath(barrierPath) == null); if ( result ) { break; } if ( hasMaxWait ) { long elapsed = System.currentTimeMillis() - startMs; long thisWaitMs = maxWaitMs - elapsed; if ( thisWaitMs <= 0 ) { break; } wait(thisWaitMs); } else { wait(); } } return result; } } DistributedDoubleBarrier.java000066400000000000000000000267771442004423600423200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/barriers/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.barriers; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.curator.utils.PathUtils; /** *

* A double barrier as described in the ZK recipes. Quoting the recipe: *

* *
* Double barriers enable * clients to synchronize the beginning and the end of a computation. When enough processes * have joined the barrier, processes start their computation and leave the barrier * once they have finished. *
*/ public class DistributedDoubleBarrier { private final CuratorFramework client; private final String barrierPath; private final int memberQty; private final String ourPath; private final String readyPath; private final AtomicBoolean hasBeenNotified = new AtomicBoolean(false); private final AtomicBoolean connectionLost = new AtomicBoolean(false); private final Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { connectionLost.set(event.getState() != Event.KeeperState.SyncConnected); client.runSafe(() -> { synchronized(DistributedDoubleBarrier.this) { hasBeenNotified.set(true); DistributedDoubleBarrier.this.notifyAll(); } }); } }; private static final String READY_NODE = "ready"; /** * Creates the barrier abstraction. memberQty is the number of members in the * barrier. When {@link #enter()} is called, it blocks until all members have entered. When * {@link #leave()} is called, it blocks until all members have left. * * @param client the client * @param barrierPath path to use * @param memberQty the number of members in the barrier. NOTE: more than memberQty * can enter the barrier. memberQty is a threshold, not a limit */ public DistributedDoubleBarrier(CuratorFramework client, String barrierPath, int memberQty) { Preconditions.checkState(memberQty > 0, "memberQty cannot be 0"); this.client = client; this.barrierPath = PathUtils.validatePath(barrierPath); this.memberQty = memberQty; ourPath = ZKPaths.makePath(barrierPath, UUID.randomUUID().toString()); readyPath = ZKPaths.makePath(barrierPath, READY_NODE); } /** * Enter the barrier and block until all members have entered * * @throws Exception interruptions, errors, etc. */ public void enter() throws Exception { enter(-1, null); } /** * Enter the barrier and block until all members have entered or the timeout has * elapsed * * @param maxWait max time to block * @param unit time unit * @return true if the entry was successful, false if the timeout elapsed first * @throws Exception interruptions, errors, etc. */ public boolean enter(long maxWait, TimeUnit unit) throws Exception { long startMs = System.currentTimeMillis(); boolean hasMaxWait = (unit != null); long maxWaitMs = hasMaxWait ? TimeUnit.MILLISECONDS.convert(maxWait, unit) : Long.MAX_VALUE; boolean readyPathExists = (client.checkExists().usingWatcher(watcher).forPath(readyPath) != null); client.create().creatingParentContainersIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(ourPath); boolean result = (readyPathExists || internalEnter(startMs, hasMaxWait, maxWaitMs)); if ( connectionLost.get() ) { throw new KeeperException.ConnectionLossException(); } return result; } /** * Leave the barrier and block until all members have left * * @throws Exception interruptions, errors, etc. */ public synchronized void leave() throws Exception { leave(-1, null); } /** * Leave the barrier and block until all members have left or the timeout has * elapsed * * @param maxWait max time to block * @param unit time unit * @return true if leaving was successful, false if the timeout elapsed first * @throws Exception interruptions, errors, etc. */ public synchronized boolean leave(long maxWait, TimeUnit unit) throws Exception { long startMs = System.currentTimeMillis(); boolean hasMaxWait = (unit != null); long maxWaitMs = hasMaxWait ? TimeUnit.MILLISECONDS.convert(maxWait, unit) : Long.MAX_VALUE; return internalLeave(startMs, hasMaxWait, maxWaitMs); } @VisibleForTesting protected List getChildrenForEntering() throws Exception { return client.getChildren().forPath(barrierPath); } private List filterAndSortChildren(List children) { Iterable filtered = Iterables.filter ( children, new Predicate() { @Override public boolean apply(String name) { return !name.equals(READY_NODE); } } ); ArrayList filteredList = Lists.newArrayList(filtered); Collections.sort(filteredList); return filteredList; } private boolean internalLeave(long startMs, boolean hasMaxWait, long maxWaitMs) throws Exception { String ourPathName = ZKPaths.getNodeFromPath(ourPath); boolean ourNodeShouldExist = true; boolean result = true; for(;;) { if ( connectionLost.get() ) { throw new KeeperException.ConnectionLossException(); } List children; try { children = client.getChildren().forPath(barrierPath); } catch ( KeeperException.NoNodeException dummy ) { children = Lists.newArrayList(); } children = filterAndSortChildren(children); if ( (children == null) || (children.size() == 0) ) { break; } int ourIndex = children.indexOf(ourPathName); if ( (ourIndex < 0) && ourNodeShouldExist ) { if ( connectionLost.get() ) { break; // connection was lost but we've reconnected. However, our ephemeral node is gone } else { throw new IllegalStateException(String.format("Our path (%s) is missing", ourPathName)); } } if ( children.size() == 1 ) { if ( ourNodeShouldExist && !children.get(0).equals(ourPathName) ) { throw new IllegalStateException(String.format("Last path (%s) is not ours (%s)", children.get(0), ourPathName)); } checkDeleteOurPath(ourNodeShouldExist); break; } Stat stat; boolean IsLowestNode = (ourIndex == 0); if ( IsLowestNode ) { String highestNodePath = ZKPaths.makePath(barrierPath, children.get(children.size() - 1)); stat = client.checkExists().usingWatcher(watcher).forPath(highestNodePath); } else { String lowestNodePath = ZKPaths.makePath(barrierPath, children.get(0)); stat = client.checkExists().usingWatcher(watcher).forPath(lowestNodePath); checkDeleteOurPath(ourNodeShouldExist); ourNodeShouldExist = false; } if ( stat != null ) { if ( hasMaxWait ) { long elapsed = System.currentTimeMillis() - startMs; long thisWaitMs = maxWaitMs - elapsed; if ( thisWaitMs <= 0 ) { result = false; break; } else { wait(thisWaitMs); } } else { wait(); } } } try { client.delete().forPath(readyPath); } catch ( KeeperException.NoNodeException ignore ) { // ignore } return result; } private void checkDeleteOurPath(boolean shouldExist) throws Exception { if ( shouldExist ) { client.delete().forPath(ourPath); } } private synchronized boolean internalEnter(long startMs, boolean hasMaxWait, long maxWaitMs) throws Exception { boolean result = true; do { List children = getChildrenForEntering(); int count = (children != null) ? children.size() : 0; if ( count >= memberQty ) { try { client.create().forPath(readyPath); } catch ( KeeperException.NodeExistsException ignore ) { // ignore } break; } if ( hasMaxWait && !hasBeenNotified.get() ) { long elapsed = System.currentTimeMillis() - startMs; long thisWaitMs = maxWaitMs - elapsed; if ( thisWaitMs <= 0 ) { result = false; } else { wait(thisWaitMs); } if ( !hasBeenNotified.get() ) { result = false; } } else { wait(); } } while ( false ); return result; } } cache/000077500000000000000000000000001442004423600337405ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipesChildData.java000066400000000000000000000070271442004423600364260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.zookeeper.data.Stat; import java.util.Arrays; import org.apache.curator.utils.PathUtils; public class ChildData implements Comparable { private final String path; private final Stat stat; private final byte[] data; public ChildData(String path, Stat stat, byte[] data) { this.path = PathUtils.validatePath(path); this.stat = stat; this.data = data; } /** * {@inheritDoc} * * Note: this class has a natural ordering that is inconsistent with equals. */ @Override public int compareTo(ChildData rhs) { if ( this == rhs ) { return 0; } if ( rhs == null || getClass() != rhs.getClass() ) { return -1; } return path.compareTo(rhs.path); } @SuppressWarnings("RedundantIfStatement") @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } ChildData childData = (ChildData)o; if ( !Arrays.equals(data, childData.data) ) { return false; } if ( path != null ? !path.equals(childData.path) : childData.path != null ) { return false; } if ( stat != null ? !stat.equals(childData.stat) : childData.stat != null ) { return false; } return true; } @Override public int hashCode() { int result = path != null ? path.hashCode() : 0; result = 31 * result + (stat != null ? stat.hashCode() : 0); result = 31 * result + Arrays.hashCode(data); return result; } /** * Returns the full path of the this child * * @return full path */ public String getPath() { return path; } /** * Returns the stat data for this child * * @return stat or null */ public Stat getStat() { return stat; } /** *

Returns the node data for this child when the cache mode is set to cache data.

* *

NOTE: the byte array returned is the raw reference of this instance's field. If you change * the values in the array any other callers to this method will see the change.

* * @return node data or null */ public byte[] getData() { return data; } @Override public String toString() { return "ChildData{" + "path='" + path + '\'' + ", stat=" + stat + ", data=" + Arrays.toString(data) + '}'; } } CompatibleCuratorCacheBridge.java000066400000000000000000000104261442004423600422660ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import com.google.common.collect.Sets; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import java.util.Collections; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.stream.Stream; import java.util.stream.StreamSupport; import static org.apache.curator.framework.recipes.cache.CuratorCacheListener.Type.*; /** * Version of CuratorCacheBridge for pre-ZK 3.6 - uses TreeCache instead of CuratorCache */ @SuppressWarnings("deprecation") class CompatibleCuratorCacheBridge implements CuratorCacheBridge, TreeCacheListener { private final TreeCache cache; private final StandardListenerManager listenerManager = StandardListenerManager.standard(); CompatibleCuratorCacheBridge(CuratorFramework client, String path, CuratorCache.Options[] optionsArg, ExecutorService executorService, boolean cacheData) { Set options = (optionsArg != null) ? Sets.newHashSet(optionsArg) : Collections.emptySet(); TreeCache.Builder builder = TreeCache.newBuilder(client, path).setCacheData(cacheData); if ( options.contains(CuratorCache.Options.SINGLE_NODE_CACHE) ) { builder.setMaxDepth(0); } if ( options.contains(CuratorCache.Options.COMPRESSED_DATA) ) { builder.setDataIsCompressed(true); } if ( executorService != null ) { builder.setExecutor(executorService); } cache = builder.build(); } @Override public void start() { try { cache.getListenable().addListener(this); cache.start(); } catch ( Exception e ) { throw new RuntimeException(e); } } @Override public void close() { cache.close(); } @Override public boolean isCuratorCache() { return false; } @Override public Listenable listenable() { return listenerManager; } @Override public Optional get(String path) { return Optional.ofNullable(cache.getCurrentData(path)); } @Override public int size() { return cache.size(); } @Override public Stream stream() { Iterable iterable = cache::iterator; return StreamSupport.stream(iterable.spliterator(), false); } @Override public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { switch ( event.getType() ) { case NODE_ADDED: { listenerManager.forEach(listener -> listener.event(NODE_CREATED, null, event.getData())); break; } case NODE_REMOVED: { listenerManager.forEach(listener -> listener.event(NODE_DELETED, event.getData(), null)); break; } case NODE_UPDATED: { listenerManager.forEach(listener -> listener.event(NODE_CHANGED, event.getOldData(), event.getData())); break; } case INITIALIZED: { listenerManager.forEach(CuratorCacheListener::initialized); break; } } } } CuratorCache.java000066400000000000000000000120251442004423600371460ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.listen.Listenable; import java.io.Closeable; import java.util.Optional; import java.util.stream.Stream; /** *

* A utility that attempts to keep the data from a node locally cached. Optionally the entire * tree of children below the node can also be cached. Will respond to update/create/delete events, pull * down the data, etc. You can register listeners that will get notified when changes occur. *

* *

* IMPORTANT - Due to how ZooKeeper works you will not get notified of every single event. * For example during a network partition the cache will not get events. Imagine the following scenario: * *

    *
  • Pre-network partition the cache contains "/foo" and "/foo/bar"
  • *
  • A network partition occurs and your Curator client loses connection to the server
  • *
  • Image another client that isn't partitioned, deletes "/foo/bar" and then a third client re-creates "/foo/bar"
  • *
  • Your client's partition is fixed. The cache will only see one change - the third client's re-create
  • *
* * Additionally, remember that ZooKeeper is an eventual consistent system. Always use ZNode version * numbers when updating nodes. *

*/ public interface CuratorCache extends Closeable, CuratorCacheAccessor { /** * cache build options */ enum Options { /** * Normally the entire tree of nodes starting at the given node are cached. This option * causes only the given node to be cached (i.e. a single node cache) */ SINGLE_NODE_CACHE, /** * Decompress data via {@link org.apache.curator.framework.api.GetDataBuilder#decompressed()} */ COMPRESSED_DATA, /** * Normally, when the cache is closed via {@link CuratorCache#close()}, the storage is cleared * via {@link CuratorCacheStorage#clear()}. This option prevents the storage from being cleared. */ DO_NOT_CLEAR_ON_CLOSE } /** * Return a Curator Cache for the given path with the given options using a standard storage instance * * @param client Curator client * @param path path to cache * @param options any options * @return cache (note it must be started via {@link #start()} */ static CuratorCache build(CuratorFramework client, String path, Options... options) { return builder(client, path).withOptions(options).build(); } /** * Start a Curator Cache builder * * @param client Curator client * @param path path to cache * @return builder */ static CuratorCacheBuilder builder(CuratorFramework client, String path) { return new CuratorCacheBuilderImpl(client, path); } /** * Start a Curator Cache Bridge builder. A Curator Cache Bridge is * a facade that uses {@link org.apache.curator.framework.recipes.cache.CuratorCache} if * persistent watches are available or {@link org.apache.curator.framework.recipes.cache.TreeCache} * otherwise (i.e. if you are using ZooKeeper 3.5.x). * * @param client Curator client * @param path path to cache * @return bridge builder */ static CuratorCacheBridgeBuilder bridgeBuilder(CuratorFramework client, String path) { return new CuratorCacheBridgeBuilderImpl(client, path); } /** * Start the cache. This will cause a complete refresh from the cache's root node and generate * events for all nodes found, etc. */ void start(); /** * Close the cache, stop responding to events, etc. */ @Override void close(); /** * Return the listener container so that listeners can be registered to be notified of changes to the cache * * @return listener container */ Listenable listenable(); /** * {@inheritDoc} */ @Override Optional get(String path); /** * {@inheritDoc} */ @Override int size(); /** * {@inheritDoc} */ @Override Stream stream(); } CuratorCacheAccessor.java000066400000000000000000000046041442004423600406350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.utils.ZKPaths; import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Stream; /** * Methods to access the underlying storage */ public interface CuratorCacheAccessor { /** * Return an entry from storage * * @param path path to get * @return entry or {@code empty()} */ Optional get(String path); /** * Return the current number of entries in storage * * @return number of entries */ int size(); /** * Return a stream over the storage entries. Note: for a standard storage instance, the stream * behaves like a stream returned by {@link java.util.concurrent.ConcurrentHashMap#entrySet()} * * @return stream over entries */ Stream stream(); /** * Filter for a ChildData stream. Only ChildDatas with the given parent path * pass the filter. This is useful to stream one level below a given path in the cache. * e.g. to stream only the first level below the root of the cache. * *
     * CuratorCache cache = ...
     * cache.stream().filter(parentPathFilter(root))... // etc
     * 
* * @param parentPath the parent path to filter on * @return filtered stream */ static Predicate parentPathFilter(String parentPath) { return d -> { ZKPaths.PathAndNode pathAndNode = ZKPaths.getPathAndNode(d.getPath()); return pathAndNode.getPath().equals(parentPath); }; } } CuratorCacheBridge.java000066400000000000000000000027201442004423600402640ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; /** * A facade that uses {@link org.apache.curator.framework.recipes.cache.CuratorCache} if * persistent watches are available or a {@link org.apache.curator.framework.recipes.cache.TreeCache} * otherwise */ @SuppressWarnings("deprecation") public interface CuratorCacheBridge extends CuratorCache { /** * Returns true if the underlying cache is {@link org.apache.curator.framework.recipes.cache.CuratorCache} (i.e. ZooKeeper 3.6+). * Otherwise it is {@link org.apache.curator.framework.recipes.cache.TreeCache} (i.e. ZooKeeper 3.5.x) * * @return true/false */ boolean isCuratorCache(); } CuratorCacheBridgeBuilder.java000066400000000000000000000042671442004423600416030ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import java.util.concurrent.ExecutorService; public interface CuratorCacheBridgeBuilder { /** * @param options any options * @return this */ CuratorCacheBridgeBuilder withOptions(CuratorCache.Options... options); /** * The bridge cache will not retain the data bytes. i.e. ChildData objects * returned by the cache will always return {@code null} for {@link ChildData#getData()} * * @return this */ CuratorCacheBridgeBuilder withDataNotCached(); /** * If the old {@link org.apache.curator.framework.recipes.cache.TreeCache} is used by the bridge * (i.e. you are using ZooKeeper 3.5.x) then this executor service is passed to {@link org.apache.curator.framework.recipes.cache.TreeCache.Builder#setExecutor(java.util.concurrent.ExecutorService)}. * For {@link org.apache.curator.framework.recipes.cache.CuratorCache} this is not used and will be ignored (a warning will be logged). * * @param executorService executor to use for ZooKeeper 3.5.x * @return this */ @SuppressWarnings("deprecation") CuratorCacheBridgeBuilder withExecutorService(ExecutorService executorService); /** * Return a new Curator Cache Bridge based on the builder methods that have been called * * @return new Curator Cache Bridge */ CuratorCacheBridge build(); }CuratorCacheBridgeBuilderImpl.java000066400000000000000000000052071442004423600424200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.Compatibility; import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutorService; class CuratorCacheBridgeBuilderImpl implements CuratorCacheBridgeBuilder { private final CuratorFramework client; private final String path; private CuratorCache.Options[] options; private boolean cacheData = true; private ExecutorService executorService = null; private final boolean forceTreeCache = Boolean.getBoolean("curator-cache-bridge-force-tree-cache"); CuratorCacheBridgeBuilderImpl(CuratorFramework client, String path) { this.client = client; this.path = path; } @Override public CuratorCacheBridgeBuilder withOptions(CuratorCache.Options... options) { this.options = options; return this; } @Override public CuratorCacheBridgeBuilder withDataNotCached() { cacheData = false; return this; } @Override public CuratorCacheBridgeBuilder withExecutorService(ExecutorService executorService) { this.executorService = executorService; return this; } @Override public CuratorCacheBridge build() { if ( !forceTreeCache && Compatibility.hasPersistentWatchers() ) { if ( executorService != null ) { LoggerFactory.getLogger(getClass()).warn("CuratorCache does not support custom ExecutorService"); } CuratorCacheStorage storage = cacheData ? CuratorCacheStorage.standard() : CuratorCacheStorage.dataNotCached(); return new CuratorCacheImpl(client, storage, path, options, null); } return new CompatibleCuratorCacheBridge(client, path, options, executorService, cacheData); } }CuratorCacheBuilder.java000066400000000000000000000035151442004423600404610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import java.util.function.Consumer; public interface CuratorCacheBuilder { /** * @param options any options * @return this */ CuratorCacheBuilder withOptions(CuratorCache.Options... options); /** * Alternate storage to use. If not specified, {@link StandardCuratorCacheStorage#standard()} is used * * @param storage storage instance to use * @return this */ CuratorCacheBuilder withStorage(CuratorCacheStorage storage); /** * By default any unexpected exception is handled by logging the exception. You can change * so that a handler is called instead. Under normal circumstances, this shouldn't be necessary. * * @param exceptionHandler exception handler to use */ CuratorCacheBuilder withExceptionHandler(Consumer exceptionHandler); /** * Return a new Curator Cache based on the builder methods that have been called * * @return new Curator Cache */ CuratorCache build(); }CuratorCacheBuilderImpl.java000066400000000000000000000037231442004423600413040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import java.util.function.Consumer; class CuratorCacheBuilderImpl implements CuratorCacheBuilder { private final CuratorFramework client; private final String path; private CuratorCacheStorage storage; private Consumer exceptionHandler; private CuratorCache.Options[] options; CuratorCacheBuilderImpl(CuratorFramework client, String path) { this.client = client; this.path = path; } @Override public CuratorCacheBuilder withOptions(CuratorCache.Options... options) { this.options = options; return this; } @Override public CuratorCacheBuilder withStorage(CuratorCacheStorage storage) { this.storage = storage; return this; } @Override public CuratorCacheBuilder withExceptionHandler(Consumer exceptionHandler) { this.exceptionHandler = exceptionHandler; return this; } @Override public CuratorCache build() { return new CuratorCacheImpl(client, storage, path, options, exceptionHandler); } }CuratorCacheImpl.java000066400000000000000000000232271442004423600377760ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.recipes.watch.PersistentWatcher; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.Optional; import java.util.Set; import java.util.concurrent.Phaser; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.stream.Stream; import static org.apache.curator.framework.recipes.cache.CuratorCacheListener.Type.*; import static org.apache.zookeeper.KeeperException.Code.NONODE; import static org.apache.zookeeper.KeeperException.Code.OK; class CuratorCacheImpl implements CuratorCache, CuratorCacheBridge { private final Logger log = LoggerFactory.getLogger(getClass()); private final AtomicReference state = new AtomicReference<>(State.LATENT); private final PersistentWatcher persistentWatcher; private final CuratorFramework client; private final CuratorCacheStorage storage; private final String path; private final boolean recursive; private final boolean compressedData; private final boolean clearOnClose; private final StandardListenerManager listenerManager = StandardListenerManager.standard(); private final Consumer exceptionHandler; private final Phaser outstandingOps = new Phaser() { @Override protected boolean onAdvance(int phase, int registeredParties) { callListeners(CuratorCacheListener::initialized); return true; } }; private enum State { LATENT, STARTED, CLOSED } CuratorCacheImpl(CuratorFramework client, CuratorCacheStorage storage, String path, Options[] optionsArg, Consumer exceptionHandler) { Set options = (optionsArg != null) ? Sets.newHashSet(optionsArg) : Collections.emptySet(); this.client = client; this.storage = (storage != null) ? storage : CuratorCacheStorage.standard(); this.path = path; recursive = !options.contains(Options.SINGLE_NODE_CACHE); compressedData = options.contains(Options.COMPRESSED_DATA); clearOnClose = !options.contains(Options.DO_NOT_CLEAR_ON_CLOSE); persistentWatcher = new PersistentWatcher(client, path, recursive); persistentWatcher.getListenable().addListener(this::processEvent); persistentWatcher.getResetListenable().addListener(this::rebuild); this.exceptionHandler = (exceptionHandler != null) ? exceptionHandler : e -> log.error("CuratorCache error", e); } @Override public void start() { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Already started"); persistentWatcher.start(); } @Override public void close() { if ( state.compareAndSet(State.STARTED, State.CLOSED) ) { persistentWatcher.close(); if ( clearOnClose ) { storage.clear(); } } } @Override public boolean isCuratorCache() { return true; } @Override public Listenable listenable() { return listenerManager; } @Override public Optional get(String path) { return storage.get(path); } @Override public int size() { return storage.size(); } @Override public Stream stream() { return storage.stream(); } @VisibleForTesting CuratorCacheStorage storage() { return storage; } private void rebuild() { if ( state.get() != State.STARTED ) { return; } // rebuild from the root first nodeChanged(path); // rebuild remaining nodes - note: this may cause some nodes to be queried twice // (though versions checks will minimize that). If someone can think of a better // way let us know storage.stream() .map(ChildData::getPath) .filter(p -> !p.equals(path)) .forEach(this::nodeChanged); } private void processEvent(WatchedEvent event) { if ( state.get() != State.STARTED ) { return; } // NOTE: Persistent/Recursive watchers never trigger NodeChildrenChanged switch ( event.getType() ) { case NodeDataChanged: case NodeCreated: { nodeChanged(event.getPath()); break; } case NodeDeleted: { removeStorage(event.getPath()); break; } } } private void checkChildrenChanged(String fromPath, Stat oldStat, Stat newStat) { if ( (state.get() != State.STARTED) || !recursive ) { return; } if ( (oldStat != null) && (oldStat.getCversion() == newStat.getCversion()) ) { return; // children haven't changed } try { BackgroundCallback callback = (__, event) -> { if ( event.getResultCode() == OK.intValue() ) { event.getChildren().forEach(child -> nodeChanged(ZKPaths.makePath(fromPath, child))); } else if ( event.getResultCode() == NONODE.intValue() ) { removeStorage(event.getPath()); } else { handleException(event); } outstandingOps.arriveAndDeregister(); }; outstandingOps.register(); client.getChildren().inBackground(callback).forPath(fromPath); } catch ( Exception e ) { handleException(e); } } private void nodeChanged(String fromPath) { if ( state.get() != State.STARTED ) { return; } try { BackgroundCallback callback = (__, event) -> { if ( event.getResultCode() == OK.intValue() ) { Optional childData = putStorage(new ChildData(event.getPath(), event.getStat(), event.getData())); checkChildrenChanged(event.getPath(), childData.map(ChildData::getStat).orElse(null), event.getStat()); } else if ( event.getResultCode() == NONODE.intValue() ) { removeStorage(event.getPath()); } else { handleException(event); } outstandingOps.arriveAndDeregister(); }; outstandingOps.register(); if ( compressedData ) { client.getData().decompressed().inBackground(callback).forPath(fromPath); } else { client.getData().inBackground(callback).forPath(fromPath); } } catch ( Exception e ) { handleException(e); } } private Optional putStorage(ChildData data) { Optional previousData = storage.put(data); if ( previousData.isPresent() ) { if ( previousData.get().getStat().getVersion() != data.getStat().getVersion() ) { callListeners(l -> l.event(NODE_CHANGED, previousData.get(), data)); } } else { callListeners(l -> l.event(NODE_CREATED, null, data)); } return previousData; } private void removeStorage(String path) { storage.remove(path).ifPresent(previousData -> callListeners(l -> l.event(NODE_DELETED, previousData, null))); } private void callListeners(Consumer proc) { if ( state.get() == State.STARTED ) { client.runSafe(() -> listenerManager.forEach(proc)); } } private void handleException(CuratorEvent event) { handleException(KeeperException.create(KeeperException.Code.get(event.getResultCode()))); } private void handleException(Exception e) { ThreadUtils.checkInterrupted(e); exceptionHandler.accept(e); } } CuratorCacheListener.java000066400000000000000000000044231442004423600406570ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; /** * Listener for {@link CuratorCache} events. The main functional interface is general purpose * but you can build event specific listeners, etc. using the builder. Note: all listeners * are wrapped in {@link org.apache.curator.framework.CuratorFramework#runSafe(Runnable)} when called. */ @FunctionalInterface public interface CuratorCacheListener { /** * An enumerated type that describes a change */ enum Type { /** * A new node was added to the cache */ NODE_CREATED, /** * A node already in the cache has changed */ NODE_CHANGED, /** * A node already in the cache was deleted */ NODE_DELETED } /** * Called when a data is created, changed or deleted. * * @param type the type of event * @param oldData the old data or null * @param data the new data or null */ void event(Type type, ChildData oldData, ChildData data); /** * When the cache is started, the initial nodes are tracked and when they are finished loading * into the cache this method is called. */ default void initialized() { // NOP } /** * Returns a builder allowing type specific, and special purpose listeners. * * @return builder */ static CuratorCacheListenerBuilder builder() { return new CuratorCacheListenerBuilderImpl(); } }CuratorCacheListenerBuilder.java000066400000000000000000000117551442004423600421740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.CuratorCacheListener.Type; import java.util.function.Consumer; public interface CuratorCacheListenerBuilder { /** * Add a standard listener * * @param listener listener to add * @return this */ CuratorCacheListenerBuilder forAll(CuratorCacheListener listener); /** * Add a listener only for {@link Type#NODE_CREATED} * * @param listener listener to add * @return this */ CuratorCacheListenerBuilder forCreates(Consumer listener); @FunctionalInterface interface ChangeListener { void event(ChildData oldNode, ChildData node); } /** * Add a listener only for {@link Type#NODE_CHANGED} * * @param listener listener to add * @return this */ CuratorCacheListenerBuilder forChanges(ChangeListener listener); /** * Add a listener only both {@link Type#NODE_CREATED} and {@link Type#NODE_CHANGED} * * @param listener listener to add * @return this */ CuratorCacheListenerBuilder forCreatesAndChanges(ChangeListener listener); /** * Add a listener only for {@link Type#NODE_DELETED} * * @param listener listener to add * @return this */ CuratorCacheListenerBuilder forDeletes(Consumer listener); /** * Add a listener only for {@link CuratorCacheListener#initialized()} * * @param listener listener to add * @return this */ CuratorCacheListenerBuilder forInitialized(Runnable listener); /** * Bridge listener. You can reuse old-style {@link org.apache.curator.framework.recipes.cache.PathChildrenCacheListener}s * with CuratorCache. IMPORTANT: the connection state methods in the listener will never be called as CuratorCache * does not register the listener with the connection state listener container. Also note that CuratorCache * behaves differently than {@link org.apache.curator.framework.recipes.cache.PathChildrenCache} so * things such as event ordering will likely be different. * * @param rootPath the root path. The listener needs this information in order to bridge only events for children of this path and not the path itself * @param client the curator client * @param listener the listener to wrap * @return a CuratorCacheListener that forwards to the given listener */ CuratorCacheListenerBuilder forPathChildrenCache(String rootPath, CuratorFramework client, PathChildrenCacheListener listener); /** * Bridge listener. You can reuse old-style {@link org.apache.curator.framework.recipes.cache.TreeCacheListener}s * with CuratorCache. IMPORTANT: the connection state methods in the listener will never be called as CuratorCache * does not register the listener with the connection state listener container. Also note that CuratorCache * behaves differently than {@link org.apache.curator.framework.recipes.cache.TreeCache} so * things such as event ordering will likely be different. * * @param client the curator client * @param listener the listener to wrap * @return a CuratorCacheListener that forwards to the given listener */ CuratorCacheListenerBuilder forTreeCache(CuratorFramework client, TreeCacheListener listener); /** * Bridge listener. You can reuse old-style {@link org.apache.curator.framework.recipes.cache.NodeCacheListener}s * with CuratorCache. * * @param listener the listener to wrap * @return a CuratorCacheListener that forwards to the given listener */ CuratorCacheListenerBuilder forNodeCache(NodeCacheListener listener); /** * Make the built listener so that it only becomes active once {@link CuratorCacheListener#initialized()} has been called. * i.e. changes that occur as the cache is initializing are not sent to the listener */ CuratorCacheListenerBuilder afterInitialized(); /** * Build and return a new listener based on the methods that have been previously called * * @return new listener */ CuratorCacheListener build(); }CuratorCacheListenerBuilderImpl.java000066400000000000000000000113251442004423600430070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; class CuratorCacheListenerBuilderImpl implements CuratorCacheListenerBuilder { private final List listeners = new ArrayList<>(); private boolean afterInitializedOnly = false; @Override public CuratorCacheListenerBuilder forAll(CuratorCacheListener listener) { listeners.add(listener); return this; } @Override public CuratorCacheListenerBuilder forCreates(Consumer listener) { listeners.add((type, oldNode, node) -> { if ( type == CuratorCacheListener.Type.NODE_CREATED ) { listener.accept(node); } }); return this; } @Override public CuratorCacheListenerBuilder forChanges(ChangeListener listener) { listeners.add((type, oldNode, node) -> { if ( type == CuratorCacheListener.Type.NODE_CHANGED ) { listener.event(oldNode, node); } }); return this; } @Override public CuratorCacheListenerBuilder forCreatesAndChanges(ChangeListener listener) { listeners.add((type, oldNode, node) -> { if ( (type == CuratorCacheListener.Type.NODE_CHANGED) || (type == CuratorCacheListener.Type.NODE_CREATED) ) { listener.event(oldNode, node); } }); return this; } @Override public CuratorCacheListenerBuilder forDeletes(Consumer listener) { listeners.add((type, oldNode, node) -> { if ( type == CuratorCacheListener.Type.NODE_DELETED ) { listener.accept(oldNode); } }); return this; } @Override public CuratorCacheListenerBuilder forInitialized(Runnable listener) { CuratorCacheListener localListener = new CuratorCacheListener() { @Override public void event(Type type, ChildData oldData, ChildData data) { // NOP } @Override public void initialized() { listener.run(); } }; listeners.add(localListener); return this; } @Override public CuratorCacheListenerBuilder forPathChildrenCache(String rootPath, CuratorFramework client, PathChildrenCacheListener listener) { listeners.add(new PathChildrenCacheListenerWrapper(rootPath, client, listener)); return this; } @Override public CuratorCacheListenerBuilder forTreeCache(CuratorFramework client, TreeCacheListener listener) { listeners.add(new TreeCacheListenerWrapper(client, listener)); return this; } @Override public CuratorCacheListenerBuilder forNodeCache(NodeCacheListener listener) { listeners.add(new NodeCacheListenerWrapper(listener)); return this; } @Override public CuratorCacheListenerBuilder afterInitialized() { afterInitializedOnly = true; return this; } @Override public CuratorCacheListener build() { List copy = new ArrayList<>(listeners); return new CuratorCacheListener() { private volatile boolean isInitialized = !afterInitializedOnly; @Override public void event(Type type, ChildData oldData, ChildData data) { if ( isInitialized ) { copy.forEach(l -> l.event(type, oldData, data)); } } @Override public void initialized() { isInitialized = true; copy.forEach(CuratorCacheListener::initialized); } }; } }CuratorCacheStorage.java000066400000000000000000000046131442004423600404770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import java.util.Optional; import java.util.stream.Stream; /** * Interface for maintaining data in a {@link CuratorCache} */ public interface CuratorCacheStorage extends CuratorCacheAccessor { /** * Return a new standard storage instance * * @return storage instance */ static CuratorCacheStorage standard() { return new StandardCuratorCacheStorage(true); } /** * Return a new storage instance that does not retain the data bytes. i.e. ChildData objects * returned by this storage will always return {@code null} for {@link ChildData#getData()}. * * @return storage instance that does not retain data bytes */ static CuratorCacheStorage dataNotCached() { return new StandardCuratorCacheStorage(false); } /** * Add an entry to storage and return any previous entry at that path * * @param data entry to add * @return previous entry or {@code empty()} */ Optional put(ChildData data); /** * Remove the entry from storage and return any previous entry at that path * * @param path path to remove * @return previous entry or {@code empty()} */ Optional remove(String path); /** * Reset the storage to zero entries */ void clear(); /** * {@inheritDoc} */ @Override Optional get(String path); /** * {@inheritDoc} */ @Override int size(); /** * {@inheritDoc} */ @Override Stream stream(); } DefaultTreeCacheSelector.java000066400000000000000000000022561442004423600414410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; /** * Default TreeCache selector - returns true for all methods */ public class DefaultTreeCacheSelector implements TreeCacheSelector { @Override public boolean traverseChildren(String fullPath) { return true; } @Override public boolean acceptChild(String fullPath) { return true; } } EventOperation.java000066400000000000000000000025311442004423600375460ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; class EventOperation implements Operation { private final PathChildrenCache cache; private final PathChildrenCacheEvent event; EventOperation(PathChildrenCache cache, PathChildrenCacheEvent event) { this.cache = cache; this.event = event; } @Override public void invoke() { cache.callListeners(event); } @Override public String toString() { return "EventOperation{" + "event=" + event + '}'; } } GetDataOperation.java000066400000000000000000000037131442004423600400010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.utils.PathUtils; class GetDataOperation implements Operation { private final PathChildrenCache cache; private final String fullPath; GetDataOperation(PathChildrenCache cache, String fullPath) { this.cache = cache; this.fullPath = PathUtils.validatePath(fullPath); } @Override public void invoke() throws Exception { cache.getDataAndStat(fullPath); } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } GetDataOperation that = (GetDataOperation)o; //noinspection RedundantIfStatement if ( !fullPath.equals(that.fullPath) ) { return false; } return true; } @Override public int hashCode() { return fullPath.hashCode(); } @Override public String toString() { return "GetDataOperation{" + "fullPath='" + fullPath + '\'' + '}'; } } NodeCache.java000066400000000000000000000262451442004423600364250ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.Exchanger; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; /** *

A utility that attempts to keep the data from a node locally cached. This class * will watch the node, respond to update/create/delete events, pull down the data, etc. You can * register a listener that will get notified when changes occur.

* *

IMPORTANT - it's not possible to stay transactionally in sync. Users of this class must * be prepared for false-positives and false-negatives. Additionally, always use the version number * when updating data to avoid overwriting another process' change.

* * @deprecated replace by {@link org.apache.curator.framework.recipes.cache.CuratorCache} */ @Deprecated public class NodeCache implements Closeable { private final Logger log = LoggerFactory.getLogger(getClass()); private final WatcherRemoveCuratorFramework client; private final String path; private final boolean dataIsCompressed; private final AtomicReference data = new AtomicReference(null); private final AtomicReference state = new AtomicReference(State.LATENT); private final StandardListenerManager listeners = StandardListenerManager.standard(); private final AtomicBoolean isConnected = new AtomicBoolean(true); private ConnectionStateListener connectionStateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( (newState == ConnectionState.CONNECTED) || (newState == ConnectionState.RECONNECTED) ) { if ( isConnected.compareAndSet(false, true) ) { try { reset(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Trying to reset after reconnection", e); } } } else { isConnected.set(false); } } }; private Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { try { reset(); } catch(Exception e) { ThreadUtils.checkInterrupted(e); handleException(e); } } }; private enum State { LATENT, STARTED, CLOSED } private final BackgroundCallback backgroundCallback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { processBackgroundResult(event); } }; /** * @param client curator client * @param path the full path to the node to cache */ public NodeCache(CuratorFramework client, String path) { this(client, path, false); } /** * @param client curator client * @param path the full path to the node to cache * @param dataIsCompressed if true, data in the path is compressed */ public NodeCache(CuratorFramework client, String path, boolean dataIsCompressed) { this.client = client.newWatcherRemoveCuratorFramework(); this.path = PathUtils.validatePath(path); this.dataIsCompressed = dataIsCompressed; } public CuratorFramework getClient() { return client; } /** * Start the cache. The cache is not started automatically. You must call this method. * * @throws Exception errors */ public void start() throws Exception { start(false); } /** * Same as {@link #start()} but gives the option of doing an initial build * * @param buildInitial if true, {@link #rebuild()} will be called before this method * returns in order to get an initial view of the node * @throws Exception errors */ public void start(boolean buildInitial) throws Exception { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once"); client.getConnectionStateListenable().addListener(connectionStateListener); if ( buildInitial ) { client.checkExists().creatingParentContainersIfNeeded().forPath(path); internalRebuild(); } reset(); } @Override public void close() throws IOException { if ( state.compareAndSet(State.STARTED, State.CLOSED) ) { client.removeWatchers(); listeners.clear(); client.getConnectionStateListenable().removeListener(connectionStateListener); // TODO // From PathChildrenCache // This seems to enable even more GC - I'm not sure why yet - it // has something to do with Guava's cache and circular references connectionStateListener = null; watcher = null; } } /** * Return the cache listenable * * @return listenable */ public Listenable getListenable() { Preconditions.checkState(state.get() != State.CLOSED, "Closed"); return listeners; } /** * NOTE: this is a BLOCKING method. Completely rebuild the internal cache by querying * for all needed data WITHOUT generating any events to send to listeners. * * @throws Exception errors */ public void rebuild() throws Exception { Preconditions.checkState(state.get() == State.STARTED, "Not started"); internalRebuild(); reset(); } /** * Return the current data. There are no guarantees of accuracy. This is * merely the most recent view of the data. If the node does not exist, * this returns null * * @return data or null */ public ChildData getCurrentData() { return data.get(); } /** * Return the path this cache is watching * * @return path */ public String getPath() { return path; } @VisibleForTesting volatile Exchanger rebuildTestExchanger; private void reset() throws Exception { if ( (state.get() == State.STARTED) && isConnected.get() ) { client.checkExists().creatingParentContainersIfNeeded().usingWatcher(watcher).inBackground(backgroundCallback).forPath(path); } } private void internalRebuild() throws Exception { try { Stat stat = new Stat(); byte[] bytes = dataIsCompressed ? client.getData().decompressed().storingStatIn(stat).forPath(path) : client.getData().storingStatIn(stat).forPath(path); data.set(new ChildData(path, stat, bytes)); } catch ( KeeperException.NoNodeException e ) { data.set(null); } } private void processBackgroundResult(CuratorEvent event) throws Exception { switch ( event.getType() ) { case GET_DATA: { if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { ChildData childData = new ChildData(path, event.getStat(), event.getData()); setNewData(childData); } break; } case EXISTS: { if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() ) { setNewData(null); } else if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { if ( dataIsCompressed ) { client.getData().decompressed().usingWatcher(watcher).inBackground(backgroundCallback).forPath(path); } else { client.getData().usingWatcher(watcher).inBackground(backgroundCallback).forPath(path); } } break; } } } private void setNewData(ChildData newData) throws InterruptedException { ChildData previousData = data.getAndSet(newData); if ( !Objects.equal(previousData, newData) ) { listeners.forEach(listener -> { try { listener.nodeChanged(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Calling listener", e); } }); if ( rebuildTestExchanger != null ) { try { rebuildTestExchanger.exchange(new Object()); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } } } } /** * Default behavior is just to log the exception * * @param e the exception */ protected void handleException(Throwable e) { log.error("", e); } } NodeCacheListener.java000066400000000000000000000017621442004423600401300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; public interface NodeCacheListener { /** * Called when a change has occurred */ public void nodeChanged() throws Exception; } NodeCacheListenerWrapper.java000066400000000000000000000026711442004423600414710ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.NodeCacheListener; class NodeCacheListenerWrapper implements CuratorCacheListener { private final NodeCacheListener listener; NodeCacheListenerWrapper(NodeCacheListener listener) { this.listener = listener; } @Override public void event(Type type, ChildData oldData, ChildData data) { try { listener.nodeChanged(); } catch ( Exception e ) { throw new RuntimeException(e); } } }Operation.java000066400000000000000000000016451442004423600365510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; interface Operation { public void invoke() throws Exception; } PathChildrenCache.java000066400000000000000000000743351442004423600401100ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.util.function.Supplier; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.EnsureContainers; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.CloseableExecutorService; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; /** *

A utility that attempts to keep all data from all children of a ZK path locally cached. This class * will watch the ZK path, respond to update/create/delete events, pull down the data, etc. You can * register a listener that will get notified when changes occur.

*

*

IMPORTANT - it's not possible to stay transactionally in sync. Users of this class must * be prepared for false-positives and false-negatives. Additionally, always use the version number * when updating data to avoid overwriting another process' change.

* * @deprecated replace by {@link org.apache.curator.framework.recipes.cache.CuratorCache} */ @Deprecated public class PathChildrenCache implements Closeable { private final Logger log = LoggerFactory.getLogger(getClass()); private final WatcherRemoveCuratorFramework client; private final String path; private final CloseableExecutorService executorService; private final boolean cacheData; private final boolean dataIsCompressed; private final StandardListenerManager listeners = StandardListenerManager.standard(); private final ConcurrentMap currentData = Maps.newConcurrentMap(); private final AtomicReference> initialSet = new AtomicReference>(); private final Set operationsQuantizer = Sets.newSetFromMap(Maps.newConcurrentMap()); private final AtomicReference state = new AtomicReference(State.LATENT); private final EnsureContainers ensureContainers; private enum State { LATENT, STARTED, CLOSED } private static final ChildData NULL_CHILD_DATA = new ChildData("/", null, null); private static final boolean USE_EXISTS = Boolean.getBoolean("curator-path-children-cache-use-exists"); private volatile Watcher childrenWatcher = new Watcher() { @Override public void process(WatchedEvent event) { offerOperation(new RefreshOperation(PathChildrenCache.this, RefreshMode.STANDARD)); } }; private volatile Watcher dataWatcher = new Watcher() { @Override public void process(WatchedEvent event) { try { if ( event.getType() == Event.EventType.NodeDeleted ) { remove(event.getPath()); } else if ( event.getType() == Event.EventType.NodeDataChanged ) { offerOperation(new GetDataOperation(PathChildrenCache.this, event.getPath())); } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } } }; @VisibleForTesting volatile Exchanger rebuildTestExchanger; private volatile ConnectionStateListener connectionStateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { handleStateChange(newState); } }; public static final Supplier defaultThreadFactorySupplier = () -> ThreadUtils.newThreadFactory("PathChildrenCache"); /** * @param client the client * @param path path to watch * @param mode caching mode * @deprecated use {@link #PathChildrenCache(CuratorFramework, String, boolean)} instead */ @Deprecated @SuppressWarnings("deprecation") public PathChildrenCache(CuratorFramework client, String path, PathChildrenCacheMode mode) { this(client, path, mode != PathChildrenCacheMode.CACHE_PATHS_ONLY, false, new CloseableExecutorService(Executors.newSingleThreadExecutor(defaultThreadFactorySupplier.get()), true)); } /** * @param client the client * @param path path to watch * @param mode caching mode * @param threadFactory factory to use when creating internal threads * @deprecated use {@link #PathChildrenCache(CuratorFramework, String, boolean, ThreadFactory)} instead */ @Deprecated @SuppressWarnings("deprecation") public PathChildrenCache(CuratorFramework client, String path, PathChildrenCacheMode mode, ThreadFactory threadFactory) { this(client, path, mode != PathChildrenCacheMode.CACHE_PATHS_ONLY, false, new CloseableExecutorService(Executors.newSingleThreadExecutor(threadFactory), true)); } /** * @param client the client * @param path path to watch * @param cacheData if true, node contents are cached in addition to the stat */ public PathChildrenCache(CuratorFramework client, String path, boolean cacheData) { this(client, path, cacheData, false, new CloseableExecutorService(Executors.newSingleThreadExecutor(defaultThreadFactorySupplier.get()), true)); } /** * @param client the client * @param path path to watch * @param cacheData if true, node contents are cached in addition to the stat * @param threadFactory factory to use when creating internal threads */ public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, ThreadFactory threadFactory) { this(client, path, cacheData, false, new CloseableExecutorService(Executors.newSingleThreadExecutor(threadFactory), true)); } /** * @param client the client * @param path path to watch * @param cacheData if true, node contents are cached in addition to the stat * @param dataIsCompressed if true, data in the path is compressed * @param threadFactory factory to use when creating internal threads */ public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, ThreadFactory threadFactory) { this(client, path, cacheData, dataIsCompressed, new CloseableExecutorService(Executors.newSingleThreadExecutor(threadFactory), true)); } /** * @param client the client * @param path path to watch * @param cacheData if true, node contents are cached in addition to the stat * @param dataIsCompressed if true, data in the path is compressed * @param executorService ExecutorService to use for the PathChildrenCache's background thread. This service should be single threaded, otherwise the cache may see inconsistent results. */ public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, final ExecutorService executorService) { this(client, path, cacheData, dataIsCompressed, new CloseableExecutorService(executorService)); } /** * @param client the client * @param path path to watch * @param cacheData if true, node contents are cached in addition to the stat * @param dataIsCompressed if true, data in the path is compressed * @param executorService Closeable ExecutorService to use for the PathChildrenCache's background thread. This service should be single threaded, otherwise the cache may see inconsistent results. */ public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, final CloseableExecutorService executorService) { this.client = client.newWatcherRemoveCuratorFramework(); this.path = PathUtils.validatePath(path); this.cacheData = cacheData; this.dataIsCompressed = dataIsCompressed; this.executorService = executorService; ensureContainers = new EnsureContainers(client, path); } /** * Start the cache. The cache is not started automatically. You must call this method. * * @throws Exception errors */ public void start() throws Exception { start(StartMode.NORMAL); } /** * Same as {@link #start()} but gives the option of doing an initial build * * @param buildInitial if true, {@link #rebuild()} will be called before this method * returns in order to get an initial view of the node; otherwise, * the cache will be initialized asynchronously * @throws Exception errors * @deprecated use {@link #start(StartMode)} */ @Deprecated public void start(boolean buildInitial) throws Exception { start(buildInitial ? StartMode.BUILD_INITIAL_CACHE : StartMode.NORMAL); } /** * Method of priming cache on {@link PathChildrenCache#start(StartMode)} */ public enum StartMode { /** * The cache will be primed (in the background) with initial values. * Events for existing and new nodes will be posted. */ NORMAL, /** * The cache will be primed (in the foreground) with initial values. * {@link PathChildrenCache#rebuild()} will be called before * the {@link PathChildrenCache#start(StartMode)} method returns * in order to get an initial view of the node. */ BUILD_INITIAL_CACHE, /** * After cache is primed with initial values (in the background) a * {@link PathChildrenCacheEvent.Type#INITIALIZED} will be posted. */ POST_INITIALIZED_EVENT } /** * Start the cache. The cache is not started automatically. You must call this method. * * @param mode Method for priming the cache * @throws Exception errors */ public void start(StartMode mode) throws Exception { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "already started"); mode = Preconditions.checkNotNull(mode, "mode cannot be null"); client.getConnectionStateListenable().addListener(connectionStateListener); switch ( mode ) { case NORMAL: { offerOperation(new RefreshOperation(this, RefreshMode.STANDARD)); break; } case BUILD_INITIAL_CACHE: { rebuild(); break; } case POST_INITIALIZED_EVENT: { initialSet.set(Maps.newConcurrentMap()); offerOperation(new RefreshOperation(this, RefreshMode.POST_INITIALIZED)); break; } } } /** * NOTE: this is a BLOCKING method. Completely rebuild the internal cache by querying * for all needed data WITHOUT generating any events to send to listeners. * * @throws Exception errors */ public void rebuild() throws Exception { Preconditions.checkState(state.get() == State.STARTED, "cache has been closed"); ensurePath(); clear(); List children = client.getChildren().forPath(path); for ( String child : children ) { String fullPath = ZKPaths.makePath(path, child); internalRebuildNode(fullPath); if ( rebuildTestExchanger != null ) { rebuildTestExchanger.exchange(new Object()); } } // this is necessary so that any updates that occurred while rebuilding are taken offerOperation(new RefreshOperation(this, RefreshMode.FORCE_GET_DATA_AND_STAT)); } /** * NOTE: this is a BLOCKING method. Rebuild the internal cache for the given node by querying * for all needed data WITHOUT generating any events to send to listeners. * * @param fullPath full path of the node to rebuild * @throws Exception errors */ public void rebuildNode(String fullPath) throws Exception { Preconditions.checkArgument(ZKPaths.getPathAndNode(fullPath).getPath().equals(path), "Node is not part of this cache: " + fullPath); Preconditions.checkState(state.get() == State.STARTED, "cache has been closed"); ensurePath(); internalRebuildNode(fullPath); // this is necessary so that any updates that occurred while rebuilding are taken // have to rebuild entire tree in case this node got deleted in the interim offerOperation(new RefreshOperation(this, RefreshMode.FORCE_GET_DATA_AND_STAT)); } /** * Close/end the cache * * @throws IOException errors */ @Override public void close() throws IOException { if ( state.compareAndSet(State.STARTED, State.CLOSED) ) { client.getConnectionStateListenable().removeListener(connectionStateListener); listeners.clear(); executorService.close(); client.removeWatchers(); // TODO // This seems to enable even more GC - I'm not sure why yet - it // has something to do with Guava's cache and circular references connectionStateListener = null; childrenWatcher = null; dataWatcher = null; } } /** * Return the cache listenable * * @return listenable */ public Listenable getListenable() { return listeners; } /** * Return the current data. There are no guarantees of accuracy. This is * merely the most recent view of the data. The data is returned in sorted order. * * @return list of children and data */ public List getCurrentData() { return ImmutableList.copyOf(Sets.newTreeSet(currentData.values())); } /** * Return the current data for the given path. There are no guarantees of accuracy. This is * merely the most recent view of the data. If there is no child with that path, null * is returned. * * @param fullPath full path to the node to check * @return data or null */ public ChildData getCurrentData(String fullPath) { return currentData.get(fullPath); } /** * As a memory optimization, you can clear the cached data bytes for a node. Subsequent * calls to {@link ChildData#getData()} for this node will return null. * * @param fullPath the path of the node to clear */ public void clearDataBytes(String fullPath) { clearDataBytes(fullPath, -1); } /** * As a memory optimization, you can clear the cached data bytes for a node. Subsequent * calls to {@link ChildData#getData()} for this node will return null. * * @param fullPath the path of the node to clear * @param ifVersion if non-negative, only clear the data if the data's version matches this version * @return true if the data was cleared */ public boolean clearDataBytes(String fullPath, int ifVersion) { ChildData data = currentData.get(fullPath); if ( data != null ) { if ( (ifVersion < 0) || (ifVersion == data.getStat().getVersion()) ) { if ( data.getData() != null ) { currentData.replace(fullPath, data, new ChildData(data.getPath(), data.getStat(), null)); } return true; } } return false; } /** * Clear out current data and begin a new query on the path * * @throws Exception errors */ public void clearAndRefresh() throws Exception { currentData.clear(); offerOperation(new RefreshOperation(this, RefreshMode.STANDARD)); } /** * Clears the current data without beginning a new query and without generating any events * for listeners. */ public void clear() { currentData.clear(); } enum RefreshMode { STANDARD, FORCE_GET_DATA_AND_STAT, POST_INITIALIZED, NO_NODE_EXCEPTION } void refresh(final RefreshMode mode) throws Exception { ensurePath(); final BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( reRemoveWatchersOnBackgroundClosed() ) { return; } if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { processChildren(event.getChildren(), mode); } else if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() ) { if ( mode == RefreshMode.NO_NODE_EXCEPTION ) { log.debug("KeeperException.NoNodeException received for getChildren() and refresh has failed. Resetting ensureContainers but not refreshing. Path: [{}]", path); ensureContainers.reset(); } else { log.debug("KeeperException.NoNodeException received for getChildren(). Resetting ensureContainers. Path: [{}]", path); ensureContainers.reset(); offerOperation(new RefreshOperation(PathChildrenCache.this, RefreshMode.NO_NODE_EXCEPTION)); } } } }; client.getChildren().usingWatcher(childrenWatcher).inBackground(callback).forPath(path); } void callListeners(final PathChildrenCacheEvent event) { listeners.forEach(listener -> { try { listener.childEvent(client, event); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } }); } void getDataAndStat(final String fullPath) throws Exception { BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( reRemoveWatchersOnBackgroundClosed() ) { return; } applyNewData(fullPath, event.getResultCode(), event.getStat(), cacheData ? event.getData() : null); } }; if ( USE_EXISTS && !cacheData ) { client.checkExists().usingWatcher(dataWatcher).inBackground(callback).forPath(fullPath); } else { // always use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak if ( dataIsCompressed && cacheData ) { client.getData().decompressed().usingWatcher(dataWatcher).inBackground(callback).forPath(fullPath); } else { client.getData().usingWatcher(dataWatcher).inBackground(callback).forPath(fullPath); } } } /** * Default behavior is just to log the exception * * @param e the exception */ protected void handleException(Throwable e) { log.error("", e); } protected void ensurePath() throws Exception { ensureContainers.ensure(); } @VisibleForTesting protected void remove(String fullPath) { ChildData data = currentData.remove(fullPath); if ( data != null ) { offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CHILD_REMOVED, data))); } Map localInitialSet = initialSet.get(); if ( localInitialSet != null ) { localInitialSet.remove(ZKPaths.getNodeFromPath(fullPath)); maybeOfferInitializedEvent(localInitialSet); } } private boolean reRemoveWatchersOnBackgroundClosed() { if ( state.get().equals(State.CLOSED)) { client.removeWatchers(); return true; } return false; } private void internalRebuildNode(String fullPath) throws Exception { if ( cacheData ) { try { Stat stat = new Stat(); byte[] bytes = dataIsCompressed ? client.getData().decompressed().storingStatIn(stat).forPath(fullPath) : client.getData().storingStatIn(stat).forPath(fullPath); currentData.put(fullPath, new ChildData(fullPath, stat, bytes)); } catch ( KeeperException.NoNodeException ignore ) { // node no longer exists - remove it currentData.remove(fullPath); } } else { Stat stat = client.checkExists().forPath(fullPath); if ( stat != null ) { currentData.put(fullPath, new ChildData(fullPath, stat, null)); } else { // node no longer exists - remove it currentData.remove(fullPath); } } } private void handleStateChange(ConnectionState newState) { switch ( newState ) { case SUSPENDED: { offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED, null))); break; } case LOST: { offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CONNECTION_LOST, null))); break; } case CONNECTED: case RECONNECTED: { try { offerOperation(new RefreshOperation(this, RefreshMode.FORCE_GET_DATA_AND_STAT)); offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED, null))); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } break; } } } private void processChildren(List children, RefreshMode mode) throws Exception { Set removedNodes = Sets.newHashSet(currentData.keySet()); for ( String child : children ) { removedNodes.remove(ZKPaths.makePath(path, child)); } for ( String fullPath : removedNodes ) { remove(fullPath); } for ( String name : children ) { String fullPath = ZKPaths.makePath(path, name); if ( (mode == RefreshMode.FORCE_GET_DATA_AND_STAT) || !currentData.containsKey(fullPath) ) { getDataAndStat(fullPath); } updateInitialSet(name, NULL_CHILD_DATA); } maybeOfferInitializedEvent(initialSet.get()); } private void applyNewData(String fullPath, int resultCode, Stat stat, byte[] bytes) { if ( resultCode == KeeperException.Code.OK.intValue() ) // otherwise - node must have dropped or something - we should be getting another event { ChildData data = new ChildData(fullPath, stat, bytes); ChildData previousData = currentData.put(fullPath, data); if ( previousData == null ) // i.e. new { offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CHILD_ADDED, data))); } else if ( stat.getMzxid() != previousData.getStat().getMzxid() ) { offerOperation(new EventOperation(this, new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.CHILD_UPDATED, data))); } updateInitialSet(ZKPaths.getNodeFromPath(fullPath), data); } else if ( resultCode == KeeperException.Code.NONODE.intValue() ) { log.debug("NoNode at path {}, removing child from initialSet", fullPath); remove(fullPath); } } private void updateInitialSet(String name, ChildData data) { Map localInitialSet = initialSet.get(); if ( localInitialSet != null ) { localInitialSet.put(name, data); maybeOfferInitializedEvent(localInitialSet); } } private void maybeOfferInitializedEvent(Map localInitialSet) { if ( !hasUninitialized(localInitialSet) ) { // all initial children have been processed - send initialized message if ( initialSet.getAndSet(null) != null ) // avoid edge case - don't send more than 1 INITIALIZED event { final List children = ImmutableList.copyOf(localInitialSet.values()); PathChildrenCacheEvent event = new PathChildrenCacheEvent(PathChildrenCacheEvent.Type.INITIALIZED, null) { @Override public List getInitialData() { return children; } }; offerOperation(new EventOperation(this, event)); } } } private boolean hasUninitialized(Map localInitialSet) { if ( localInitialSet == null ) { return false; } Map uninitializedChildren = Maps.filterValues ( localInitialSet, new Predicate() { @Override public boolean apply(ChildData input) { return (input == NULL_CHILD_DATA); // check against ref intentional } } ); return (uninitializedChildren.size() != 0); } void offerOperation(final Operation operation) { if ( operationsQuantizer.add(operation) ) { submitToExecutor ( new Runnable() { @Override public void run() { try { operationsQuantizer.remove(operation); operation.invoke(); } catch ( InterruptedException e ) { //We expect to get interrupted during shutdown, //so just ignore these events if ( state.get() != State.CLOSED ) { handleException(e); } Thread.currentThread().interrupt(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } } } ); } } /** * Submits a runnable to the executor. *

* This method is synchronized because it has to check state about whether this instance is still open. Without this check * there is a race condition with the dataWatchers that get set. Even after this object is closed() it can still be * called by those watchers, because the close() method cannot actually disable the watcher. *

* The synchronization overhead should be minimal if non-existant as this is generally only called from the * ZK client thread and will only contend if close() is called in parallel with an update, and that's the exact state * we want to protect from. * * @param command The runnable to run */ private synchronized void submitToExecutor(final Runnable command) { if ( state.get() == State.STARTED ) { executorService.submit(command); } } } PathChildrenCacheEvent.java000066400000000000000000000121311442004423600410740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.state.ConnectionState; import java.util.List; /** * POJO that abstracts a change to a path */ public class PathChildrenCacheEvent { private final Type type; private final ChildData data; /** * Type of change */ public enum Type { /** * A child was added to the path */ CHILD_ADDED, /** * A child's data was changed */ CHILD_UPDATED, /** * A child was removed from the path */ CHILD_REMOVED, /** * Called when the connection has changed to {@link ConnectionState#SUSPENDED} * * This is exposed so that users of the class can be notified of issues that *might* affect normal operation. * The PathChildrenCache is written such that listeners are not expected to do anything special on this * event, except for those people who want to cause some application-specific logic to fire when this occurs. * While the connection is down, the PathChildrenCache will continue to have its state from before it lost * the connection and after the connection is restored, the PathChildrenCache will emit normal child events * for all of the adds, deletes and updates that happened during the time that it was disconnected. */ CONNECTION_SUSPENDED, /** * Called when the connection has changed to {@link ConnectionState#RECONNECTED} * * This is exposed so that users of the class can be notified of issues that *might* affect normal operation. * The PathChildrenCache is written such that listeners are not expected to do anything special on this * event, except for those people who want to cause some application-specific logic to fire when this occurs. * While the connection is down, the PathChildrenCache will continue to have its state from before it lost * the connection and after the connection is restored, the PathChildrenCache will emit normal child events * for all of the adds, deletes and updates that happened during the time that it was disconnected. */ CONNECTION_RECONNECTED, /** * Called when the connection has changed to {@link ConnectionState#LOST} * * This is exposed so that users of the class can be notified of issues that *might* affect normal operation. * The PathChildrenCache is written such that listeners are not expected to do anything special on this * event, except for those people who want to cause some application-specific logic to fire when this occurs. * While the connection is down, the PathChildrenCache will continue to have its state from before it lost * the connection and after the connection is restored, the PathChildrenCache will emit normal child events * for all of the adds, deletes and updates that happened during the time that it was disconnected. */ CONNECTION_LOST, /** * Posted when {@link PathChildrenCache#start(PathChildrenCache.StartMode)} is called * with {@link PathChildrenCache.StartMode#POST_INITIALIZED_EVENT}. This * event signals that the initial cache has been populated. */ INITIALIZED } /** * @param type event type * @param data event data or null */ public PathChildrenCacheEvent(Type type, ChildData data) { this.type = type; this.data = data; } /** * @return change type */ public Type getType() { return type; } /** * @return the node's data */ public ChildData getData() { return data; } /** * Special purpose method. When an {@link Type#INITIALIZED} * event is received, you can call this method to * receive the initial state of the cache. * * @return initial state of cache for {@link Type#INITIALIZED} events. Otherwise, null. */ public List getInitialData() { return null; } @Override public String toString() { return "PathChildrenCacheEvent{" + "type=" + type + ", data=" + data + '}'; } } PathChildrenCacheListener.java000066400000000000000000000024071442004423600416050ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; /** * Listener for PathChildrenCache changes */ public interface PathChildrenCacheListener { /** * Called when a change has occurred * * @param client the client * @param event describes the change * @throws Exception errors */ public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception; } PathChildrenCacheListenerWrapper.java000066400000000000000000000054161442004423600431510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import java.util.Objects; import org.apache.curator.framework.CuratorFramework; class PathChildrenCacheListenerWrapper implements CuratorCacheListener { private final PathChildrenCacheListener listener; private final CuratorFramework client; private final String rootPath; PathChildrenCacheListenerWrapper(String rootPath, CuratorFramework client, PathChildrenCacheListener listener) { Objects.requireNonNull(rootPath,"rootPath cannot be null"); this.rootPath = rootPath; this.listener = listener; this.client = client; } @Override public void event(Type type, ChildData oldData, ChildData data) { switch ( type ) { case NODE_CREATED: { if (rootPath.equals(data.getPath())) { return; } sendEvent(data, PathChildrenCacheEvent.Type.CHILD_ADDED); break; } case NODE_CHANGED: { if (rootPath.equals(data.getPath())) { return; } sendEvent(data, PathChildrenCacheEvent.Type.CHILD_UPDATED); break; } case NODE_DELETED: { if (rootPath.equals(oldData.getPath())) { return; } sendEvent(oldData, PathChildrenCacheEvent.Type.CHILD_REMOVED); break; } } } @Override public void initialized() { sendEvent(null, PathChildrenCacheEvent.Type.INITIALIZED); } private void sendEvent(ChildData node, PathChildrenCacheEvent.Type type) { PathChildrenCacheEvent event = new PathChildrenCacheEvent(type, node); try { listener.childEvent(client, event); } catch ( Exception e ) { throw new RuntimeException(e); } } }PathChildrenCacheMode.java000066400000000000000000000034251442004423600407050ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import java.util.concurrent.ThreadFactory; /** * Controls which data is cached * * @deprecated no longer used. Instead use either {@link PathChildrenCache#PathChildrenCache(CuratorFramework, String, boolean)} * or {@link PathChildrenCache#PathChildrenCache(CuratorFramework, String, boolean, ThreadFactory)} */ @Deprecated public enum PathChildrenCacheMode { /** * The cache will hold all the children, the data for each child node * and the stat for each child node */ CACHE_DATA_AND_STAT, /** * The cache will hold all the children and the data for each child node. * {@link ChildData#getStat()} will return null. */ CACHE_DATA, /** * The cache will hold only the children path names. * {@link ChildData#getStat()} and {@link ChildData#getData()} will both return null. */ CACHE_PATHS_ONLY } RefreshOperation.java000066400000000000000000000035301442004423600400630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; class RefreshOperation implements Operation { private final PathChildrenCache cache; private final PathChildrenCache.RefreshMode mode; RefreshOperation(PathChildrenCache cache, PathChildrenCache.RefreshMode mode) { this.cache = cache; this.mode = mode; } @Override public void invoke() throws Exception { cache.refresh(mode); } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } RefreshOperation that = (RefreshOperation)o; //noinspection RedundantIfStatement if ( mode != that.mode ) { return false; } return true; } @Override public int hashCode() { return mode.hashCode(); } @Override public String toString() { return "RefreshOperation(" + mode + "){}"; } } StandardCuratorCacheStorage.java000066400000000000000000000040461442004423600421600ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; class StandardCuratorCacheStorage implements CuratorCacheStorage { private final Map dataMap; private final boolean cacheBytes; StandardCuratorCacheStorage(boolean cacheBytes) { this.dataMap = new ConcurrentHashMap<>(); this.cacheBytes = cacheBytes; } @Override public Optional put(ChildData data) { ChildData localData = cacheBytes ? data : new ChildData(data.getPath(), data.getStat(), null); return Optional.ofNullable(dataMap.put(data.getPath(), localData)); } @Override public Optional remove(String path) { return Optional.ofNullable(dataMap.remove(path)); } @Override public Optional get(String path) { return Optional.ofNullable(dataMap.get(path)); } @Override public int size() { return dataMap.size(); } @Override public Stream stream() { return dataMap.values().stream(); } @Override public void clear() { dataMap.clear(); } }TreeCache.java000066400000000000000000000776501442004423600364450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import java.util.function.Supplier; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.BackgroundPathable; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.Pathable; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.api.Watchable; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import static com.google.common.base.Preconditions.checkNotNull; import static org.apache.curator.utils.PathUtils.validatePath; /** *

A utility that attempts to keep all data from all children of a ZK path locally cached. This class * will watch the ZK path, respond to update/create/delete events, pull down the data, etc. You can * register a listener that will get notified when changes occur.

*

*

IMPORTANT - it's not possible to stay transactionally in sync. Users of this class must * be prepared for false-positives and false-negatives. Additionally, always use the version number * when updating data to avoid overwriting another process' change.

* * @deprecated replace by {@link org.apache.curator.framework.recipes.cache.CuratorCache} */ @Deprecated public class TreeCache implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(TreeCache.class); private final boolean createParentNodes; private final boolean disableZkWatches; private final TreeCacheSelector selector; public static final class Builder { private final CuratorFramework client; private final String path; private boolean cacheData = true; private boolean dataIsCompressed = false; private ExecutorService executorService = null; private int maxDepth = Integer.MAX_VALUE; private boolean createParentNodes = false; private boolean disableZkWatches = false; private TreeCacheSelector selector = new DefaultTreeCacheSelector(); private Builder(CuratorFramework client, String path) { this.client = checkNotNull(client); this.path = validatePath(path); } /** * Builds the {@link TreeCache} based on configured values. */ public TreeCache build() { ExecutorService executor = executorService; if ( executor == null ) { executor = Executors.newSingleThreadExecutor(defaultThreadFactorySupplier.get()); } return new TreeCache(client, path, cacheData, dataIsCompressed, maxDepth, executor, createParentNodes, disableZkWatches, selector); } /** * Sets whether or not to cache byte data per node; default {@code true}. */ public Builder setCacheData(boolean cacheData) { this.cacheData = cacheData; return this; } /** * Sets whether or to decompress node data; default {@code false}. */ public Builder setDataIsCompressed(boolean dataIsCompressed) { this.dataIsCompressed = dataIsCompressed; return this; } /** * Sets the executor to publish events; a default executor will be created if not specified. */ public Builder setExecutor(ThreadFactory threadFactory) { return setExecutor(Executors.newSingleThreadExecutor(threadFactory)); } /** * Sets the executor to publish events; a default executor will be created if not specified. */ public Builder setExecutor(ExecutorService executorService) { this.executorService = checkNotNull(executorService); return this; } /** * Sets the maximum depth to explore/watch. A {@code maxDepth} of {@code 0} will watch only * the root node (like {@link NodeCache}); a {@code maxDepth} of {@code 1} will watch the * root node and its immediate children (kind of like {@link PathChildrenCache}. * Default: {@code Integer.MAX_VALUE} */ public Builder setMaxDepth(int maxDepth) { this.maxDepth = maxDepth; return this; } /** * By default, TreeCache does not auto-create parent nodes for the cached path. Change * this behavior with this method. NOTE: parent nodes are created as containers * * @param createParentNodes true to create parent nodes * @return this for chaining */ public Builder setCreateParentNodes(boolean createParentNodes) { this.createParentNodes = createParentNodes; return this; } /** * By default, TreeCache creates {@link org.apache.zookeeper.ZooKeeper} watches for every created path. * Change this behavior with this method. * @param disableZkWatches true to disable zk watches * @return this for chaining */ public Builder disableZkWatches(boolean disableZkWatches) { this.disableZkWatches = disableZkWatches; return this; } /** * By default, {@link DefaultTreeCacheSelector} is used. Change the selector here. * * @param selector new selector * @return this for chaining */ public Builder setSelector(TreeCacheSelector selector) { this.selector = selector; return this; } } /** * Create a TreeCache builder for the given client and path to configure advanced options. *

* If the client is namespaced, all operations on the resulting TreeCache will be in terms of * the namespace, including all published events. The given path is the root at which the * TreeCache will watch and explore. If no node exists at the given path, the TreeCache will * be initially empty. * * @param client the client to use; may be namespaced * @param path the path to the root node to watch/explore; this path need not actually exist on * the server * @return a new builder */ public static Builder newBuilder(CuratorFramework client, String path) { return new Builder(client, path); } private static final ChildData DEAD = new ChildData("/", null, null); static boolean isLive(ChildData cd) { return cd != null && cd != DEAD; } private static final AtomicReferenceFieldUpdater childDataUpdater = AtomicReferenceFieldUpdater.newUpdater(TreeNode.class, ChildData.class, "childData"); private static final AtomicReferenceFieldUpdater> childrenUpdater = (AtomicReferenceFieldUpdater)AtomicReferenceFieldUpdater.newUpdater(TreeNode.class, ConcurrentMap.class, "children"); final class TreeNode implements Watcher, BackgroundCallback { volatile ChildData childData; final TreeNode parent; final String path; volatile ConcurrentMap children; final int depth; TreeNode(String path, TreeNode parent) { this.path = path; this.parent = parent; this.depth = parent == null ? 0 : parent.depth + 1; } private void refresh() throws Exception { if ((depth < maxDepth) && selector.traverseChildren(path)) { outstandingOps.addAndGet(2); doRefreshData(); doRefreshChildren(); } else { refreshData(); } } private void refreshChildren() throws Exception { if ((depth < maxDepth) && selector.traverseChildren(path)) { outstandingOps.incrementAndGet(); doRefreshChildren(); } } private void refreshData() throws Exception { outstandingOps.incrementAndGet(); doRefreshData(); } private void doRefreshChildren() throws Exception { if ( treeState.get() == TreeState.STARTED ) { maybeWatch(client.getChildren()).forPath(path); } } private void doRefreshData() throws Exception { if ( treeState.get() == TreeState.STARTED ) { if ( dataIsCompressed ) { maybeWatch(client.getData().decompressed()).forPath(path); } else { maybeWatch(client.getData()).forPath(path); } } } private > & BackgroundPathable> Pathable maybeWatch( P dataBuilder) { if (disableZkWatches) { return dataBuilder.inBackground(this); } else { return dataBuilder.usingWatcher(this).inBackground(this); } } void wasReconnected() throws Exception { refresh(); ConcurrentMap childMap = children; if ( childMap != null ) { for ( TreeNode child : childMap.values() ) { child.wasReconnected(); } } } void wasCreated() throws Exception { refresh(); } void wasDeleted() throws Exception { ChildData oldChildData = childDataUpdater.getAndSet(this, DEAD); if ( oldChildData == DEAD ) { return; } ConcurrentMap childMap = childrenUpdater.getAndSet(this, null); if ( childMap != null ) { ArrayList childCopy = new ArrayList(childMap.values()); childMap.clear(); for ( TreeNode child : childCopy ) { child.wasDeleted(); } } if ( treeState.get() == TreeState.CLOSED ) { return; } if ( isLive(oldChildData) ) { publishEvent(TreeCacheEvent.Type.NODE_REMOVED, oldChildData, null); } if ( parent == null ) { // Root node; use an exist query to watch for existence. maybeWatch(client.checkExists()).forPath(path); } else { // Remove from parent if we're currently a child ConcurrentMap parentChildMap = parent.children; if ( parentChildMap != null ) { parentChildMap.remove(ZKPaths.getNodeFromPath(path), this); } } } @Override public void process(WatchedEvent event) { LOG.debug("process: {}", event); try { switch ( event.getType() ) { case NodeCreated: Preconditions.checkState(parent == null, "unexpected NodeCreated on non-root node"); wasCreated(); break; case NodeChildrenChanged: refreshChildren(); break; case NodeDataChanged: refreshData(); break; case NodeDeleted: wasDeleted(); break; } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } } @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { LOG.debug("processResult: {}", event); Stat newStat = event.getStat(); switch ( event.getType() ) { case EXISTS: Preconditions.checkState(parent == null, "unexpected EXISTS on non-root node"); if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { childDataUpdater.compareAndSet(this, DEAD, null); wasCreated(); } break; case CHILDREN: if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { ChildData oldChildData = childData; //TODO consider doing update of cversion, pzxid, numChildren only if ( isLive(oldChildData) && oldChildData.getStat().getMzxid() == newStat.getMzxid() ) { // Only update stat if mzxid is same, otherwise we might obscure // GET_DATA event updates. childDataUpdater.compareAndSet(this, oldChildData, new ChildData(oldChildData.getPath(), newStat, oldChildData.getData())); } if ( event.getChildren().isEmpty() ) { break; } ConcurrentMap childMap = children; while ( childMap == null ) { childMap = Maps.newConcurrentMap(); if ( !childrenUpdater.compareAndSet(this, null, childMap) ) { childMap = children; } } // Present new children in sorted order for test determinism. List newChildren = new ArrayList(); for ( String child : event.getChildren() ) { if ( !childMap.containsKey(child) && selector.acceptChild(ZKPaths.makePath(path, child)) ) { newChildren.add(child); } } Collections.sort(newChildren); for ( String child : newChildren ) { String fullPath = ZKPaths.makePath(path, child); TreeNode node = new TreeNode(fullPath, this); if ( childMap.putIfAbsent(child, node) == null ) { node.wasCreated(); } } } else if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() ) { wasDeleted(); } break; case GET_DATA: if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { String eventPath = event.getPath(); ChildData toPublish = new ChildData(eventPath, newStat, event.getData()); ChildData toUpdate = cacheData ? toPublish : new ChildData(eventPath, newStat, null); while ( true ) { final ChildData oldChildData = childData; // Ignore this event if we've already processed a newer update for this node. if ( isLive(oldChildData) && newStat.getMzxid() <= oldChildData.getStat().getMzxid() ) { break; } // Non-root nodes are not allowed to transition from dead -> live; // make sure this isn't a delayed response that came in after death. if ( parent != null && oldChildData == DEAD ) { break; } if ( childDataUpdater.compareAndSet(this, oldChildData, toUpdate) ) { if ( isLive(oldChildData) ) { publishEvent(TreeCacheEvent.Type.NODE_UPDATED, toPublish, oldChildData); } else { publishEvent(TreeCacheEvent.Type.NODE_ADDED, toPublish, null); } break; } } } else if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() ) { wasDeleted(); } break; default: // An unknown event, probably an error of some sort like connection loss. LOG.info(String.format("Unknown event %s", event)); // Don't produce an initialized event on error; reconnect can fix this. outstandingOps.decrementAndGet(); return; } if ( outstandingOps.decrementAndGet() == 0 ) { if ( isInitialized.compareAndSet(false, true) ) { publishEvent(TreeCacheEvent.Type.INITIALIZED); } } } } private enum TreeState { LATENT, STARTED, CLOSED } /** * Tracks the number of outstanding background requests in flight. The first time this count reaches 0, we publish the initialized event. */ private final AtomicLong outstandingOps = new AtomicLong(0); /** * Have we published the {@link TreeCacheEvent.Type#INITIALIZED} event yet? */ private final AtomicBoolean isInitialized = new AtomicBoolean(false); private final TreeNode root; private final WatcherRemoveCuratorFramework client; private final ExecutorService executorService; private final boolean cacheData; private final boolean dataIsCompressed; private final int maxDepth; private final StandardListenerManager listeners = StandardListenerManager.standard(); private final StandardListenerManager errorListeners = StandardListenerManager.standard(); private final AtomicReference treeState = new AtomicReference(TreeState.LATENT); private final ConnectionStateListener connectionStateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { handleStateChange(newState); } }; private static final Supplier defaultThreadFactorySupplier = () -> ThreadUtils.newThreadFactory("TreeCache"); /** * Create a TreeCache for the given client and path with default options. *

* If the client is namespaced, all operations on the resulting TreeCache will be in terms of * the namespace, including all published events. The given path is the root at which the * TreeCache will watch and explore. If no node exists at the given path, the TreeCache will * be initially empty. * * @param client the client to use; may be namespaced * @param path the path to the root node to watch/explore; this path need not actually exist on * the server * @see #newBuilder(CuratorFramework, String) */ public TreeCache(CuratorFramework client, String path) { this(client, path, true, false, Integer.MAX_VALUE, Executors.newSingleThreadExecutor(defaultThreadFactorySupplier.get()), false, false, new DefaultTreeCacheSelector()); } /** * @param client the client * @param path path to watch * @param cacheData if true, node contents are cached in addition to the stat * @param dataIsCompressed if true, data in the path is compressed * @param executorService Closeable ExecutorService to use for the TreeCache's background thread * @param createParentNodes true to create parent nodes as containers * @param disableZkWatches true to disable Zookeeper watches * @param selector the selector to use */ TreeCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, int maxDepth, final ExecutorService executorService, boolean createParentNodes, boolean disableZkWatches, TreeCacheSelector selector) { this.createParentNodes = createParentNodes; this.selector = Preconditions.checkNotNull(selector, "selector cannot be null"); this.root = new TreeNode(validatePath(path), null); Preconditions.checkNotNull(client, "client cannot be null"); this.client = client.newWatcherRemoveCuratorFramework(); this.cacheData = cacheData; this.dataIsCompressed = dataIsCompressed; this.maxDepth = maxDepth; this.disableZkWatches = disableZkWatches; this.executorService = Preconditions.checkNotNull(executorService, "executorService cannot be null"); } /** * Start the cache. The cache is not started automatically. You must call this method. * * @return this * @throws Exception errors */ public TreeCache start() throws Exception { Preconditions.checkState(treeState.compareAndSet(TreeState.LATENT, TreeState.STARTED), "already started"); if ( createParentNodes ) { client.createContainers(root.path); } client.getConnectionStateListenable().addListener(connectionStateListener); if ( client.getZookeeperClient().isConnected() ) { root.wasCreated(); } return this; } /** * Close/end the cache. */ @Override public void close() { if ( treeState.compareAndSet(TreeState.STARTED, TreeState.CLOSED) ) { client.removeWatchers(); client.getConnectionStateListenable().removeListener(connectionStateListener); listeners.clear(); executorService.shutdown(); try { root.wasDeleted(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } } } /** * Return the cache listenable * * @return listenable */ public Listenable getListenable() { return listeners; } /** * Allows catching unhandled errors in asynchronous operations. * * TODO: consider making public. */ @VisibleForTesting public Listenable getUnhandledErrorListenable() { return errorListeners; } private TreeNode find(String findPath) { PathUtils.validatePath(findPath); LinkedList rootElements = new LinkedList(ZKPaths.split(root.path)); LinkedList findElements = new LinkedList(ZKPaths.split(findPath)); while (!rootElements.isEmpty()) { if (findElements.isEmpty()) { // Target path shorter than root path return null; } String nextRoot = rootElements.removeFirst(); String nextFind = findElements.removeFirst(); if (!nextFind.equals(nextRoot)) { // Initial root path does not match return null; } } TreeNode current = root; while (!findElements.isEmpty()) { String nextFind = findElements.removeFirst(); ConcurrentMap map = current.children; if ( map == null ) { return null; } current = map.get(nextFind); if ( current == null ) { return null; } } return current; } /** * Return the current set of children at the given path, mapped by child name. There are no * guarantees of accuracy; this is merely the most recent view of the data. If there is no * node at this path, {@code null} is returned. * * @param fullPath full path to the node to check * @return a possibly-empty list of children if the node is alive, or null */ public Map getCurrentChildren(String fullPath) { TreeNode node = find(fullPath); if ( node == null || !isLive(node.childData) ) { return null; } ConcurrentMap map = node.children; Map result; if ( map == null ) { result = ImmutableMap.of(); } else { ImmutableMap.Builder builder = ImmutableMap.builder(); for ( Map.Entry entry : map.entrySet() ) { ChildData childData = entry.getValue().childData; // Double-check liveness after retrieving data. if ( isLive(childData) ) { builder.put(entry.getKey(), childData); } } result = builder.build(); } // Double-check liveness after retrieving children. return isLive(node.childData) ? result : null; } /** * Return the current data for the given path. There are no guarantees of accuracy. This is * merely the most recent view of the data. If there is no node at the given path, * {@code null} is returned. * * @param fullPath full path to the node to check * @return data if the node is alive, or null */ public ChildData getCurrentData(String fullPath) { TreeNode node = find(fullPath); if ( node == null ) { return null; } ChildData result = node.childData; return isLive(result) ? result : null; } /** * Return an iterator over all nodes in the cache. There are no * guarantees of accuracy; this is merely the most recent view of the data. * * @return a possibly-empty iterator of nodes in the cache */ public Iterator iterator() { return new TreeCacheIterator(root); } /** * Return the number of nodes in the cache. There are no * guarantees of accuracy; this is merely the most recent view of the data. * * @return size */ public int size() { return size(root); } private int size(TreeNode node) { int size; if ( isLive(node.childData) ) { size = 1; if ( node.children != null ) { for ( TreeNode child : node.children.values() ) { size += size(child); } } } else { size = 0; } return size; } private void callListeners(final TreeCacheEvent event) { listeners.forEach(listener -> { try { listener.childEvent(client, event); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } }); } /** * Send an exception to any listeners, or else log the error if there are none. */ private void handleException(final Throwable e) { if ( errorListeners.size() == 0 ) { LOG.error("", e); } else { errorListeners.forEach(listener -> { try { listener.unhandledError("", e); } catch ( Exception e2 ) { ThreadUtils.checkInterrupted(e2); LOG.error("Exception handling exception", e2); } }); } } private void handleStateChange(ConnectionState newState) { switch ( newState ) { case SUSPENDED: publishEvent(TreeCacheEvent.Type.CONNECTION_SUSPENDED); break; case LOST: isInitialized.set(false); publishEvent(TreeCacheEvent.Type.CONNECTION_LOST); break; case CONNECTED: try { root.wasCreated(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } break; case RECONNECTED: try { root.wasReconnected(); publishEvent(TreeCacheEvent.Type.CONNECTION_RECONNECTED); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } break; } } private void publishEvent(TreeCacheEvent.Type type) { publishEvent(new TreeCacheEvent(type, null)); } private void publishEvent(TreeCacheEvent.Type type, ChildData data, ChildData oldData) { publishEvent(new TreeCacheEvent(type, data, oldData)); } private void publishEvent(final TreeCacheEvent event) { if ( treeState.get() != TreeState.CLOSED ) { LOG.debug("publishEvent: {}", event); executorService.submit(new Runnable() { @Override public void run() { try { callListeners(event); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); handleException(e); } } }); } } } TreeCacheEvent.java000066400000000000000000000140461442004423600374350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; /** * POJO that abstracts a change to a path */ public class TreeCacheEvent { private final Type type; private final ChildData data; private final ChildData oldData; /** * Type of change */ public enum Type { /** * A node was added. */ NODE_ADDED, /** * A node's data was changed */ NODE_UPDATED, /** * A node was removed from the tree */ NODE_REMOVED, /** * Called when the connection has changed to {@link org.apache.curator.framework.state.ConnectionState#SUSPENDED} *

* This is exposed so that users of the class can be notified of issues that *might* affect normal operation. * The TreeCache is written such that listeners are not expected to do anything special on this * event, except for those people who want to cause some application-specific logic to fire when this occurs. * While the connection is down, the TreeCache will continue to have its state from before it lost * the connection and after the connection is restored, the TreeCache will emit normal child events * for all of the adds, deletes and updates that happened during the time that it was disconnected. *

*/ CONNECTION_SUSPENDED, /** * Called when the connection has changed to {@link org.apache.curator.framework.state.ConnectionState#RECONNECTED} *

* This is exposed so that users of the class can be notified of issues that *might* affect normal operation. * The TreeCache is written such that listeners are not expected to do anything special on this * event, except for those people who want to cause some application-specific logic to fire when this occurs. * While the connection is down, the TreeCache will continue to have its state from before it lost * the connection and after the connection is restored, the TreeCache will emit normal child events * for all of the adds, deletes and updates that happened during the time that it was disconnected. *

* After reconnection, the cache will resynchronize its internal state with the server, then fire a * {@link #INITIALIZED} event. *

*/ CONNECTION_RECONNECTED, /** * Called when the connection has changed to {@link org.apache.curator.framework.state.ConnectionState#LOST} *

* This is exposed so that users of the class can be notified of issues that *might* affect normal operation. * The TreeCache is written such that listeners are not expected to do anything special on this * event, except for those people who want to cause some application-specific logic to fire when this occurs. * While the connection is down, the TreeCache will continue to have its state from before it lost * the connection and after the connection is restored, the TreeCache will emit normal child events * for all of the adds, deletes and updates that happened during the time that it was disconnected. *

*/ CONNECTION_LOST, /** * Posted after the initial cache has been fully populated. *

* On startup, the cache synchronizes its internal * state with the server, publishing a series of {@link #NODE_ADDED} events as new nodes are discovered. Once * the cachehas been fully synchronized, this {@link #INITIALIZED} this event is published. All events * published after this event represent actual server-side mutations. *

* On reconnection, the cache will resynchronize its internal state with the server, and fire this event again * once its internal state is completely refreshed. *

* Note: because the initial population is inherently asynchronous, so it's possible to observe server-side changes * (such as a {@link #NODE_UPDATED}) prior to this event being published. *

*/ INITIALIZED } /** * @param type event type * @param data event data or null */ public TreeCacheEvent(Type type, ChildData data) { this(type, data, null); } /** * @param type event type * @param data event data or null * @param oldData event oldData or null */ public TreeCacheEvent(Type type, ChildData data, ChildData oldData) { this.type = type; this.data = data; this.oldData = oldData; } /** * @return change type */ public Type getType() { return type; } /** * @return the node's data */ public ChildData getData() { return data; } /** * @return the node's old data when the type is {@link org.apache.curator.framework.recipes.cache.TreeCacheEvent.Type#NODE_UPDATED} */ public ChildData getOldData() { return oldData; } @Override public String toString() { return TreeCacheEvent.class.getSimpleName() + "{" + "type=" + type + ", data=" + data + '}'; } } TreeCacheIterator.java000066400000000000000000000055041442004423600401440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import com.google.common.collect.Iterators; import java.util.Iterator; import java.util.LinkedList; import java.util.NoSuchElementException; // depth first iterator over tree cache nodes class TreeCacheIterator implements Iterator { private final LinkedList stack = new LinkedList<>(); private Current current; private static class Current { final Iterator iterator; TreeCache.TreeNode node; Current(Iterator iterator) { this.iterator = iterator; node = iterator.next(); } } TreeCacheIterator(TreeCache.TreeNode root) { current = new Current(Iterators.forArray(root)); stack.push(current); } @Override public boolean hasNext() { return (current != null) && TreeCache.isLive(current.node.childData); } @Override public ChildData next() { if ( current == null ) { throw new NoSuchElementException(); } ChildData result = current.node.childData; // result of next iteration is current node's data // set the next node for the next iteration (or note completion) do { setNext(); } while ( (current != null) && !TreeCache.isLive(current.node.childData) ); return result; } private void setNext() { if ( current.node.children != null ) { stack.push(current); current = new Current(current.node.children.values().iterator()); } else while ( true ) { if ( current.iterator.hasNext() ) { current.node = current.iterator.next(); break; } else if ( stack.size() > 0 ) { current = stack.pop(); } else { current = null; // done break; } } } } TreeCacheListener.java000066400000000000000000000023641442004423600401410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; /** * Listener for {@link TreeCache} changes */ public interface TreeCacheListener { /** * Called when a change has occurred * * @param client the client * @param event describes the change * @throws Exception errors */ public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception; } TreeCacheListenerWrapper.java000066400000000000000000000044041442004423600414770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; class TreeCacheListenerWrapper implements CuratorCacheListener { private final CuratorFramework client; private final TreeCacheListener listener; TreeCacheListenerWrapper(CuratorFramework client, TreeCacheListener listener) { this.client = client; this.listener = listener; } @Override public void event(Type type, ChildData oldData, ChildData data) { switch ( type ) { case NODE_CREATED: { sendEvent(data, null, TreeCacheEvent.Type.NODE_ADDED); break; } case NODE_CHANGED: { sendEvent(data, oldData, TreeCacheEvent.Type.NODE_UPDATED); break; } case NODE_DELETED: { sendEvent(oldData, null, TreeCacheEvent.Type.NODE_REMOVED); break; } } } @Override public void initialized() { sendEvent(null, null, TreeCacheEvent.Type.INITIALIZED); } private void sendEvent(ChildData node, ChildData oldNode, TreeCacheEvent.Type type) { TreeCacheEvent event = new TreeCacheEvent(type, node, oldNode); try { listener.childEvent(client, event); } catch ( Exception e ) { throw new RuntimeException(e); } } }TreeCacheSelector.java000066400000000000000000000041671442004423600401370ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; /** *

* Controls which nodes a TreeCache processes. When iterating * over the children of a parent node, a given node's children are * queried only if {@link #traverseChildren(String)} returns true. * When caching the list of nodes for a parent node, a given node is * stored only if {@link #acceptChild(String)} returns true. *

* *

* E.g. Given: *

 * root
 *     n1-a
 *     n1-b
 *         n2-a
 *         n2-b
 *             n3-a
 *     n1-c
 *     n1-d
 * 
* You could have a TreeCache only work with the nodes: n1-a, n1-b, n2-a, n2-b, n1-d * by returning false from traverseChildren() for "/root/n1-b/n2-b" and returning * false from acceptChild("/root/n1-c"). *

*/ public interface TreeCacheSelector { /** * Return true if children of this path should be cached. * i.e. if false is returned, this node is not queried to * determine if it has children or not * * @param fullPath full path of the ZNode * @return true/false */ boolean traverseChildren(String fullPath); /** * Return true if this node should be returned from the cache * * @param fullPath full path of the ZNode * @return true/false */ boolean acceptChild(String fullPath); } leader/000077500000000000000000000000001442004423600341315ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipesCancelLeadershipException.java000066400000000000000000000032511442004423600420420ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.state.ConnectionState; /** * When thrown from {@link LeaderSelectorListener#stateChanged(CuratorFramework, ConnectionState)}, will * cause {@link LeaderSelector#interruptLeadership()} to get called. IMPORTANT: this is only supported * when thrown from {@link LeaderSelectorListener#stateChanged(CuratorFramework, ConnectionState)}. */ public class CancelLeadershipException extends RuntimeException { public CancelLeadershipException() { } public CancelLeadershipException(String message) { super(message); } public CancelLeadershipException(String message, Throwable cause) { super(message, cause); } public CancelLeadershipException(Throwable cause) { super(cause); } } LeaderLatch.java000066400000000000000000000627611442004423600371600ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.recipes.AfterConnectionEstablished; import org.apache.curator.framework.recipes.locks.LockInternals; import org.apache.curator.framework.recipes.locks.LockInternalsSorter; import org.apache.curator.framework.recipes.locks.StandardLockInternalsDriver; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.EOFException; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.curator.utils.PathUtils; /** *

* Abstraction to select a "leader" amongst multiple contenders in a group of JVMs connected to * a Zookeeper cluster. If a group of N thread/processes contend for leadership one will * randomly be assigned leader until it releases leadership at which time another one from the * group will randomly be chosen *

*/ public class LeaderLatch implements Closeable { private final Logger log = LoggerFactory.getLogger(getClass()); private final WatcherRemoveCuratorFramework client; private final String latchPath; private final String id; private final AtomicReference state = new AtomicReference(State.LATENT); private final AtomicBoolean hasLeadership = new AtomicBoolean(false); private final AtomicReference ourPath = new AtomicReference(); private final AtomicReference lastPathIsLeader = new AtomicReference(); private final StandardListenerManager listeners = StandardListenerManager.standard(); private final CloseMode closeMode; private final AtomicReference> startTask = new AtomicReference>(); private final ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { handleStateChange(newState); } }; private static final String LOCK_NAME = "latch-"; private static final LockInternalsSorter sorter = new LockInternalsSorter() { @Override public String fixForSorting(String str, String lockName) { return StandardLockInternalsDriver.standardFixForSorting(str, lockName); } }; public enum State { LATENT, STARTED, CLOSED } /** * How to handle listeners when the latch is closed */ public enum CloseMode { /** * When the latch is closed, listeners will *not* be notified (default behavior) */ SILENT, /** * When the latch is closed, listeners *will* be notified */ NOTIFY_LEADER } /** * @param client the client * @param latchPath the path for this leadership group */ public LeaderLatch(CuratorFramework client, String latchPath) { this(client, latchPath, "", CloseMode.SILENT); } /** * @param client the client * @param latchPath the path for this leadership group * @param id participant ID */ public LeaderLatch(CuratorFramework client, String latchPath, String id) { this(client, latchPath, id, CloseMode.SILENT); } /** * @param client the client * @param latchPath the path for this leadership group * @param id participant ID * @param closeMode behaviour of listener on explicit close. */ public LeaderLatch(CuratorFramework client, String latchPath, String id, CloseMode closeMode) { this.client = Preconditions.checkNotNull(client, "client cannot be null").newWatcherRemoveCuratorFramework(); this.latchPath = PathUtils.validatePath(latchPath); this.id = Preconditions.checkNotNull(id, "id cannot be null"); this.closeMode = Preconditions.checkNotNull(closeMode, "closeMode cannot be null"); } /** * Add this instance to the leadership election and attempt to acquire leadership. * * @throws Exception errors */ public void start() throws Exception { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once"); startTask.set(AfterConnectionEstablished.execute(client, new Runnable() { @Override public void run() { try { internalStart(); } finally { startTask.set(null); } } })); } /** * Remove this instance from the leadership election. If this instance is the leader, leadership * is released. IMPORTANT: the only way to release leadership is by calling close(). All LeaderLatch * instances must eventually be closed. * * @throws IOException errors */ @Override public void close() throws IOException { close(closeMode); } @VisibleForTesting void closeOnDemand() throws IOException { internalClose(closeMode, false); } /** * Remove this instance from the leadership election. If this instance is the leader, leadership * is released. IMPORTANT: the only way to release leadership is by calling close(). All LeaderLatch * instances must eventually be closed. * * @param closeMode allows the default close mode to be overridden at the time the latch is closed. * @throws IOException errors */ public void close(CloseMode closeMode) throws IOException { internalClose(closeMode, true); } private synchronized void internalClose(CloseMode closeMode, boolean failOnClosed) throws IOException { if (!state.compareAndSet(State.STARTED, State.CLOSED)) { if (failOnClosed) { throw new IllegalStateException("Already closed or has not been started"); } else { return; } } Preconditions.checkNotNull(closeMode, "closeMode cannot be null"); cancelStartTask(); try { setNode(null); client.removeWatchers(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new IOException(e); } finally { client.getConnectionStateListenable().removeListener(listener); switch ( closeMode ) { case NOTIFY_LEADER: { setLeadership(false); listeners.clear(); break; } default: { listeners.clear(); setLeadership(false); break; } } } } @VisibleForTesting protected boolean cancelStartTask() { Future localStartTask = startTask.getAndSet(null); if ( localStartTask != null ) { localStartTask.cancel(true); return true; } return false; } /** * Attaches a listener to this LeaderLatch *

* Attaching the same listener multiple times is a noop from the second time on. *

* All methods for the listener are run using the provided Executor. It is common to pass in a single-threaded * executor so that you can be certain that listener methods are called in sequence, but if you are fine with * them being called out of order you are welcome to use multiple threads. *

* * @param listener the listener to attach */ public void addListener(LeaderLatchListener listener) { listeners.addListener(listener); } /** * Attaches a listener to this LeaderLatch *

* Attaching the same listener multiple times is a noop from the second time on. *

* All methods for the listener are run using the provided Executor. It is common to pass in a single-threaded * executor so that you can be certain that listener methods are called in sequence, but if you are fine with * them being called out of order you are welcome to use multiple threads. *

* * @param listener the listener to attach * @param executor An executor to run the methods for the listener on. */ public void addListener(LeaderLatchListener listener, Executor executor) { listeners.addListener(listener, executor); } /** * Removes a given listener from this LeaderLatch * * @param listener the listener to remove */ public void removeListener(LeaderLatchListener listener) { listeners.removeListener(listener); } /** *

Causes the current thread to wait until this instance acquires leadership * unless the thread is {@linkplain Thread#interrupt interrupted} or {@linkplain #close() closed}.

*

If this instance already is the leader then this method returns immediately.

*

*

Otherwise the current * thread becomes disabled for thread scheduling purposes and lies * dormant until one of three things happen:

*
    *
  • This instance becomes the leader
  • *
  • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread
  • *
  • The instance is {@linkplain #close() closed}
  • *
*

If the current thread:

*
    *
  • has its interrupted status set on entry to this method; or *
  • is {@linkplain Thread#interrupt interrupted} while waiting, *
*

then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared.

* * @throws InterruptedException if the current thread is interrupted * while waiting * @throws EOFException if the instance is {@linkplain #close() closed} * while waiting */ public void await() throws InterruptedException, EOFException { synchronized(this) { while ( (state.get() == State.STARTED) && !hasLeadership.get() ) { wait(); } } if ( state.get() != State.STARTED ) { throw new EOFException(); } } /** *

Causes the current thread to wait until this instance acquires leadership * unless the thread is {@linkplain Thread#interrupt interrupted}, * the specified waiting time elapses or the instance is {@linkplain #close() closed}.

*

*

If this instance already is the leader then this method returns immediately * with the value {@code true}.

*

*

Otherwise the current * thread becomes disabled for thread scheduling purposes and lies * dormant until one of four things happen:

*
    *
  • This instance becomes the leader
  • *
  • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread
  • *
  • The specified waiting time elapses.
  • *
  • The instance is {@linkplain #close() closed}
  • *
*

*

If the current thread:

*
    *
  • has its interrupted status set on entry to this method; or *
  • is {@linkplain Thread#interrupt interrupted} while waiting, *
*

then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared.

*

*

If the specified waiting time elapses or the instance is {@linkplain #close() closed} * then the value {@code false} is returned. If the time is less than or equal to zero, the method * will not wait at all.

* * @param timeout the maximum time to wait * @param unit the time unit of the {@code timeout} argument * @return {@code true} if the count reached zero and {@code false} * if the waiting time elapsed before the count reached zero or the instances was closed * @throws InterruptedException if the current thread is interrupted * while waiting */ public boolean await(long timeout, TimeUnit unit) throws InterruptedException { long waitNanos = TimeUnit.NANOSECONDS.convert(timeout, unit); synchronized(this) { while ( true ) { if ( state.get() != State.STARTED ) { return false; } if ( hasLeadership() ) { return true; } if ( waitNanos <= 0 ) { return false; } long startNanos = System.nanoTime(); TimeUnit.NANOSECONDS.timedWait(this, waitNanos); long elapsed = System.nanoTime() - startNanos; waitNanos -= elapsed; } } } /** * Return this instance's participant Id * * @return participant Id */ public String getId() { return id; } /** * Returns this instances current state, this is the only way to verify that the object has been closed before * closing again. If you try to close a latch multiple times, the close() method will throw an * IllegalArgumentException which is often not caught and ignored (CloseableUtils.closeQuietly() only looks for * IOException). * * @return the state of the current instance */ public State getState() { return state.get(); } /** *

* Returns the set of current participants in the leader selection *

*

*

* NOTE - this method polls the ZK server. Therefore it can possibly * return a value that does not match {@link #hasLeadership()} as hasLeadership * uses a local field of the class. *

* * @return participants * @throws Exception ZK errors, interruptions, etc. */ public Collection getParticipants() throws Exception { Collection participantNodes = LockInternals.getParticipantNodes(client, latchPath, LOCK_NAME, sorter); return LeaderSelector.getParticipants(client, participantNodes); } /** *

* Return the id for the current leader. If for some reason there is no * current leader, a dummy participant is returned. *

*

*

* NOTE - this method polls the ZK server. Therefore it can possibly * return a value that does not match {@link #hasLeadership()} as hasLeadership * uses a local field of the class. *

* * @return leader * @throws Exception ZK errors, interruptions, etc. */ public Participant getLeader() throws Exception { Collection participantNodes = LockInternals.getParticipantNodes(client, latchPath, LOCK_NAME, sorter); return LeaderSelector.getLeader(client, participantNodes); } /** * Return true if leadership is currently held by this instance * * @return true/false */ public boolean hasLeadership() { return (state.get() == State.STARTED) && hasLeadership.get(); } /** * Return this instance's lock node path. IMPORTANT: this instance * owns the path returned. This method is meant for reference only. Also, * it is possible for null to be returned. The path, if any, * returned is not guaranteed to be valid at any point in the future as internal * state changes might require the instance to delete and create a new path. * * However, the existence of ourPath doesn't mean that this instance * holds leadership. * * @see #getLastPathIsLeader * * @return lock node path or null */ public String getOurPath() { return ourPath.get(); } /** * Return last of this instance's lock node path that was leader ever. * IMPORTANT: this instance owns the path returned. This method is meant for reference only. * Also, it is possible for null to be returned (for this instance never becomes * a leader). The path, if any, returned is not guaranteed to be valid at any point in the future * as internal state changes might require the instance to delete the path. * * The existence of lastPathIsLeader means that this instance holds leadership. * * @return last lock node path that was leader ever or null */ public String getLastPathIsLeader() { return lastPathIsLeader.get(); } @VisibleForTesting volatile CountDownLatch debugResetWaitLatch = null; @VisibleForTesting volatile CountDownLatch debugResetWaitBeforeNodeDeleteLatch = null; @VisibleForTesting void reset() throws Exception { setLeadership(false); if ( debugResetWaitBeforeNodeDeleteLatch != null ) { debugResetWaitBeforeNodeDeleteLatch.await(); } setNode(null); BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( debugResetWaitLatch != null ) { debugResetWaitLatch.await(); debugResetWaitLatch = null; } if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { setNode(event.getName()); if ( state.get() == State.CLOSED ) { setNode(null); } else { getChildren(); } } else { log.error("getChildren() failed. rc = " + event.getResultCode()); } } }; client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).inBackground(callback).forPath(ZKPaths.makePath(latchPath, LOCK_NAME), LeaderSelector.getIdBytes(id)); } private synchronized void internalStart() { if ( state.get() == State.STARTED ) { client.getConnectionStateListenable().addListener(listener); try { reset(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("An error occurred checking resetting leadership.", e); } } } @VisibleForTesting volatile CountDownLatch debugCheckLeaderShipLatch = null; private void checkLeadership(List children) throws Exception { if ( debugCheckLeaderShipLatch != null ) { debugCheckLeaderShipLatch.await(); } final String localOurPath = ourPath.get(); List sortedChildren = LockInternals.getSortedChildren(LOCK_NAME, sorter, children); int ourIndex = (localOurPath != null) ? sortedChildren.indexOf(ZKPaths.getNodeFromPath(localOurPath)) : -1; log.debug("checkLeadership with id: {}, ourPath: {}, children: {}", id, localOurPath, sortedChildren); if ( ourIndex < 0 ) { log.error("Can't find our node. Resetting. Index: " + ourIndex); reset(); } else if ( ourIndex == 0 ) { lastPathIsLeader.set(localOurPath); setLeadership(true); } else { setLeadership(false); String watchPath = sortedChildren.get(ourIndex - 1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if ( state.get() == State.STARTED && event.getType() == Event.EventType.NodeDeleted ) { try { getChildren(); } catch ( Exception ex ) { ThreadUtils.checkInterrupted(ex); log.error("An error occurred checking the leadership.", ex); } } } }; BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() ) { // previous node is gone - retry getChildren getChildren(); } } }; // use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak client.getData().usingWatcher(watcher).inBackground(callback).forPath(ZKPaths.makePath(latchPath, watchPath)); } } private void getChildren() throws Exception { BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { checkLeadership(event.getChildren()); } } }; client.getChildren().inBackground(callback).forPath(ZKPaths.makePath(latchPath, null)); } @VisibleForTesting protected void handleStateChange(ConnectionState newState) { switch ( newState ) { default: { // NOP break; } case RECONNECTED: { try { if ( client.getConnectionStateErrorPolicy().isErrorState(ConnectionState.SUSPENDED) || !hasLeadership.get() ) { getChildren(); } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Could not reset leader latch", e); setLeadership(false); } break; } case SUSPENDED: { if ( client.getConnectionStateErrorPolicy().isErrorState(ConnectionState.SUSPENDED) ) { setLeadership(false); } break; } case LOST: { setLeadership(false); break; } } } private synchronized void setLeadership(boolean newValue) { boolean oldValue = hasLeadership.getAndSet(newValue); if ( oldValue && !newValue ) { // Lost leadership, was true, now false listeners.forEach(LeaderLatchListener::notLeader); } else if ( !oldValue && newValue ) { // Gained leadership, was false, now true listeners.forEach(LeaderLatchListener::isLeader); } notifyAll(); } private void setNode(String newValue) throws Exception { String oldPath = ourPath.getAndSet(newValue); log.debug("setNode with id: {}, oldPath: {}, newValue: {}", id, oldPath, newValue); if ( oldPath != null ) { client.delete().guaranteed().inBackground().forPath(oldPath); } } } LeaderLatchListener.java000066400000000000000000000040331442004423600406520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; /** * A LeaderLatchListener can be used to be notified asynchronously about when the state of the LeaderLatch has changed. * * Note that just because you are in the middle of one of these method calls, it does not necessarily mean that * hasLeadership() is the corresponding true/false value. It is possible for the state to change behind the scenes * before these methods get called. The contract is that if that happens, you should see another call to the other * method pretty quickly. */ public interface LeaderLatchListener { /** * This is called when the LeaderLatch's state goes from hasLeadership = false to hasLeadership = true. * * Note that it is possible that by the time this method call happens, hasLeadership has fallen back to false. If * this occurs, you can expect {@link #notLeader()} to also be called. */ public void isLeader(); /** * This is called when the LeaderLatch's state goes from hasLeadership = true to hasLeadership = false. * * Note that it is possible that by the time this method call happens, hasLeadership has become true. If * this occurs, you can expect {@link #isLeader()} to also be called. */ public void notLeader(); } LeaderSelector.java000066400000000000000000000465561442004423600377110ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.utils.CloseableExecutorService; import org.apache.curator.utils.ThreadUtils; import org.apache.zookeeper.KeeperException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.apache.curator.utils.PathUtils; /** *

* Abstraction to select a "leader" amongst multiple contenders in a group of JMVs connected * to a Zookeeper cluster. If a group of N thread/processes contends for leadership, one will * be assigned leader until it releases leadership at which time another one from the group will * be chosen. *

*

* Note that this class uses an underlying {@link InterProcessMutex} and as a result leader * election is "fair" - each user will become leader in the order originally requested * (from ZK's point of view). *

*/ public class LeaderSelector implements Closeable { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFramework client; private final LeaderSelectorListener listener; private final CloseableExecutorService executorService; private final InterProcessMutex mutex; private final AtomicReference state = new AtomicReference(State.LATENT); private final AtomicBoolean autoRequeue = new AtomicBoolean(false); // guarded by synchronization private Future ourTask = null; private Thread ourThread = null; private volatile boolean hasLeadership; private volatile String id = ""; @VisibleForTesting volatile CountDownLatch debugLeadershipLatch = null; volatile CountDownLatch debugLeadershipWaitLatch = null; private enum State { LATENT, STARTED, CLOSED } private static final ThreadFactory defaultThreadFactory = ThreadUtils.newThreadFactory("LeaderSelector"); /** * @param client the client * @param leaderPath the path for this leadership group * @param listener listener */ public LeaderSelector(CuratorFramework client, String leaderPath, LeaderSelectorListener listener) { this(client, leaderPath, new CloseableExecutorService(Executors.newSingleThreadExecutor(defaultThreadFactory), true), listener); } /** * @param client the client * @param leaderPath the path for this leadership group * @param threadFactory factory to use for making internal threads * @param executor the executor to run in * @param listener listener * @deprecated This constructor was poorly thought out. Custom executor is useless. Use this version instead: {@link #LeaderSelector(CuratorFramework, String, ExecutorService, LeaderSelectorListener)} */ @SuppressWarnings("UnusedParameters") @Deprecated public LeaderSelector(CuratorFramework client, String leaderPath, ThreadFactory threadFactory, Executor executor, LeaderSelectorListener listener) { this(client, leaderPath, new CloseableExecutorService(wrapExecutor(executor), true), listener); } /** * @param client the client * @param leaderPath the path for this leadership group * @param executorService thread pool to use * @param listener listener */ public LeaderSelector(CuratorFramework client, String leaderPath, ExecutorService executorService, LeaderSelectorListener listener) { this(client, leaderPath, new CloseableExecutorService(executorService), listener); } /** * @param client the client * @param leaderPath the path for this leadership group * @param executorService thread pool to use * @param listener listener */ public LeaderSelector(CuratorFramework client, String leaderPath, CloseableExecutorService executorService, LeaderSelectorListener listener) { Preconditions.checkNotNull(client, "client cannot be null"); PathUtils.validatePath(leaderPath); Preconditions.checkNotNull(listener, "listener cannot be null"); this.client = client; this.listener = new WrappedListener(this, listener); hasLeadership = false; this.executorService = executorService; mutex = new InterProcessMutex(client, leaderPath) { @Override protected byte[] getLockNodeBytes() { return (id.length() > 0) ? getIdBytes(id) : null; } }; } static byte[] getIdBytes(String id) { try { return id.getBytes("UTF-8"); } catch ( UnsupportedEncodingException e ) { throw new Error(e); // this should never happen } } /** * By default, when {@link LeaderSelectorListener#takeLeadership(CuratorFramework)} returns, this * instance is not requeued. Calling this method puts the leader selector into a mode where it * will always requeue itself. */ public void autoRequeue() { autoRequeue.set(true); } /** * Sets the ID to store for this leader. Will be the value returned * when {@link #getParticipants()} is called. IMPORTANT: must be called * prior to {@link #start()} to have effect. * * @param id ID */ public void setId(String id) { Preconditions.checkNotNull(id, "id cannot be null"); this.id = id; } /** * Return the ID that was set via {@link #setId(String)} * * @return id */ public String getId() { return id; } /** * Attempt leadership. This attempt is done in the background - i.e. this method returns * immediately.

* IMPORTANT: previous versions allowed this method to be called multiple times. This * is no longer supported. Use {@link #requeue()} for this purpose. */ public void start() { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once"); Preconditions.checkState(!executorService.isShutdown(), "Already started"); Preconditions.checkState(!hasLeadership, "Already has leadership"); client.getConnectionStateListenable().addListener(listener); requeue(); } /** * Re-queue an attempt for leadership. If this instance is already queued, nothing * happens and false is returned. If the instance was not queued, it is re-queued and true * is returned * *

The attempt will finish after session error, leadership release. This method is inherently * hard to use as there is no public API to guarantee successful requeue. Try {@link #autoRequeue()} * if you are in doubt.

* * @return true if re-queue is successful */ public boolean requeue() { Preconditions.checkState(state.get() == State.STARTED, "close() has already been called"); return internalRequeue(); } private synchronized boolean internalRequeue() { if ( ourTask == null && (state.get() == State.STARTED) ) { ourTask = executorService.submit(new Callable() { @Override public Void call() throws Exception { try { taskStarted(); doWorkLoop(); } finally { taskDone(); } return null; } }); return true; } return false; } /** * Shutdown this selector and remove yourself from the leadership group */ public synchronized void close() { Preconditions.checkState(state.compareAndSet(State.STARTED, State.CLOSED), "Already closed or has not been started"); client.getConnectionStateListenable().removeListener(listener); executorService.close(); ourTask = null; } /** *

* Returns the set of current participants in the leader selection *

*

*

* NOTE - this method polls the ZK server. Therefore it can possibly * return a value that does not match {@link #hasLeadership()} as hasLeadership * uses a local field of the class. *

* * @return participants * @throws Exception ZK errors, interruptions, etc. */ public Collection getParticipants() throws Exception { Collection participantNodes = mutex.getParticipantNodes(); return getParticipants(client, participantNodes); } static Collection getParticipants(CuratorFramework client, Collection participantNodes) throws Exception { ImmutableList.Builder builder = ImmutableList.builder(); boolean isLeader = true; for ( String path : participantNodes ) { Participant participant = participantForPath(client, path, isLeader); if( participant != null ) { builder.add(participant); isLeader = false; // by definition the first node is the leader } } return builder.build(); } /** *

* Return the id for the current leader. If for some reason there is no * current leader, a dummy participant is returned. *

*

*

* NOTE - this method polls the ZK server. Therefore it can possibly * return a value that does not match {@link #hasLeadership()} as hasLeadership * uses a local field of the class. *

* * @return leader * @throws Exception ZK errors, interruptions, etc. */ public Participant getLeader() throws Exception { Collection participantNodes = mutex.getParticipantNodes(); return getLeader(client, participantNodes); } static Participant getLeader(CuratorFramework client, Collection participantNodes) throws Exception { Participant result = null; if ( participantNodes.size() > 0 ) { Iterator iter = participantNodes.iterator(); while ( iter.hasNext() ) { result = participantForPath(client, iter.next(), true); if ( result != null ) { break; } } } if( result == null ) { result = new Participant(); } return result; } /** * Return true if leadership is currently held by this instance * * @return true/false */ public boolean hasLeadership() { return hasLeadership; } private synchronized void taskStarted() { ourThread = Thread.currentThread(); } private synchronized void taskDone() { ourTask = null; ourThread = null; if (autoRequeue.get()) { internalRequeue(); } } /** * Cancel ongoing election regardless of leadership. */ private synchronized void cancelElection() { // Correctness with requeue: // * Cancel, taskStarted and taskDone are guarded by synchronized(this). // * If ourThread is null, new task will observe this cancellation after taskStarted. // * If ourThread is not null, old task will be cancelled and new task will observe // this cancellation. if (ourThread != null) { ourThread.interrupt(); } } /** * Attempt to cancel and interrupt the current leadership if this instance has leadership */ public synchronized void interruptLeadership() { if (hasLeadership) { cancelElection(); } } private static Participant participantForPath(CuratorFramework client, String path, boolean markAsLeader) throws Exception { try { byte[] bytes = client.getData().forPath(path); String thisId = new String(bytes, "UTF-8"); return new Participant(thisId, markAsLeader); } catch ( KeeperException.NoNodeException e ) { return null; } } @VisibleForTesting volatile AtomicInteger failedMutexReleaseCount = null; /** * This method must not be called concurrently to obey guarantee to * {@link LeaderSelectorListener#takeLeadership(CuratorFramework)}. */ @VisibleForTesting void doWork() throws Exception { hasLeadership = false; try { mutex.acquire(); hasLeadership = true; try { if ( debugLeadershipLatch != null ) { debugLeadershipLatch.countDown(); } if ( debugLeadershipWaitLatch != null ) { debugLeadershipWaitLatch.await(); } listener.takeLeadership(client); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); throw e; } catch ( Throwable e ) { ThreadUtils.checkInterrupted(e); } } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); throw e; } finally { if ( hasLeadership ) { hasLeadership = false; boolean wasInterrupted = Thread.interrupted(); // clear any interrupted tatus so that mutex.release() works immediately try { mutex.release(); } catch ( Exception e ) { if ( failedMutexReleaseCount != null ) { failedMutexReleaseCount.incrementAndGet(); } ThreadUtils.checkInterrupted(e); log.error("The leader threw an exception", e); // ignore errors - this is just a safety } finally { if ( wasInterrupted ) { Thread.currentThread().interrupt(); } } } } } private void doWorkLoop() throws Exception { KeeperException exception = null; try { doWork(); } catch ( KeeperException.ConnectionLossException e ) { exception = e; } catch ( KeeperException.SessionExpiredException e ) { exception = e; } catch ( InterruptedException ignore ) { Thread.currentThread().interrupt(); } if ( (exception != null) && !autoRequeue.get() ) // autoRequeue should ignore connection loss or session expired and just keep trying { throw exception; } } // temporary wrapper for deprecated constructor private static ExecutorService wrapExecutor(final Executor executor) { return new AbstractExecutorService() { private volatile boolean isShutdown = false; private volatile boolean isTerminated = false; @Override public void shutdown() { isShutdown = true; } @Override public List shutdownNow() { return Lists.newArrayList(); } @Override public boolean isShutdown() { return isShutdown; } @Override public boolean isTerminated() { return isTerminated; } @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException(); } @Override public void execute(Runnable command) { try { executor.execute(command); } finally { isShutdown = true; isTerminated = true; } } }; } private static class WrappedListener implements LeaderSelectorListener { private final LeaderSelector leaderSelector; private final LeaderSelectorListener listener; public WrappedListener(LeaderSelector leaderSelector, LeaderSelectorListener listener) { this.leaderSelector = leaderSelector; this.listener = listener; } @Override public void takeLeadership(CuratorFramework client) throws Exception { listener.takeLeadership(client); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { try { listener.stateChanged(client, newState); } catch ( CancelLeadershipException dummy ) { // If we cancel only leadership but not whole election, then we could hand over // dated leadership to client with no further cancellation. Dated leadership is // possible due to separated steps in leadership acquire: server data(e.g. election sequence) // change and client flag(e.g. hasLeadership) set. leaderSelector.cancelElection(); } } } } LeaderSelectorListener.java000066400000000000000000000036021442004423600414000ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; /** * Notification for leadership * * @see LeaderSelector */ public interface LeaderSelectorListener extends ConnectionStateListener { /** * Called when your instance has been granted leadership. This method * should not return until you wish to release leadership. * *

It is guaranteed that there is no concurrent executions of this * method.

* *

It is guaranteed that this method will be interrupted if * {@link #stateChanged(CuratorFramework, ConnectionState)} * throws {@link CancelLeadershipException}. After interrupted, this * method should exit(either return or throw) promptly, otherwise it * will block following elections.

* * @param client the client * @throws Exception any errors */ public void takeLeadership(CuratorFramework client) throws Exception; } LeaderSelectorListenerAdapter.java000066400000000000000000000026541442004423600427070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.state.ConnectionState; /** * An implementation of {@link LeaderSelectorListener} that adds the recommended handling * for connection state problems */ public abstract class LeaderSelectorListenerAdapter implements LeaderSelectorListener { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( client.getConnectionStateErrorPolicy().isErrorState(newState) ) { throw new CancelLeadershipException(); } } } Participant.java000066400000000000000000000046551442004423600372640ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; /** * Describes a participant in a leadership selection */ @SuppressWarnings({"RedundantIfStatement"}) public class Participant { private final String id; private final boolean isLeader; /** * @param id the ID * @param leader true if the leader */ public Participant(String id, boolean leader) { this.id = id; isLeader = leader; } Participant() { this("", false); } /** * Returns the ID set via {@link LeaderSelector#setId(String)} * * @return id */ public String getId() { return id; } /** * Returns true if this participant is the current leader * * @return true/false */ public boolean isLeader() { return isLeader; } @Override public String toString() { return "Participant{" + "id='" + id + '\'' + ", isLeader=" + isLeader + '}'; } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } Participant that = (Participant)o; if ( isLeader != that.isLeader ) { return false; } if ( !id.equals(that.id) ) { return false; } return true; } @Override public int hashCode() { int result = id.hashCode(); result = 31 * result + (isLeader ? 1 : 0); return result; } } locks/000077500000000000000000000000001442004423600340105ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipesInterProcessLock.java000066400000000000000000000040751442004423600401120ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import java.util.concurrent.TimeUnit; /** * NOTE: depending on its implementation, {@link #release()} may throw an exception if the current thread does not own the lock */ public interface InterProcessLock { /** * Acquire the mutex - blocking until it's available. Each call to acquire must be balanced by a call * to {@link #release()} * * @throws Exception ZK errors, connection interruptions */ public void acquire() throws Exception; /** * Acquire the mutex - blocks until it's available or the given time expires. Each call to acquire that returns true must be balanced by a call * to {@link #release()} * * @param time time to wait * @param unit time unit * @return true if the mutex was acquired, false if not * @throws Exception ZK errors, connection interruptions */ public boolean acquire(long time, TimeUnit unit) throws Exception; /** * Perform one release of the mutex. * * @throws Exception ZK errors, interruptions */ public void release() throws Exception; /** * Returns true if the mutex is acquired by a thread in this JVM * * @return true/false */ boolean isAcquiredInThisProcess(); } InterProcessMultiLock.java000066400000000000000000000131151442004423600411200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.ThreadUtils; import java.util.List; import java.util.concurrent.TimeUnit; import static com.google.common.collect.Lists.reverse; /** * A container that manages multiple locks as a single entity. When {@link #acquire()} is called, * all the locks are acquired. If that fails, any paths that were acquired are released. Similarly, when * {@link #release()} is called, all locks are released (failures are ignored). */ public class InterProcessMultiLock implements InterProcessLock { private final List locks; /** * Creates a multi lock of {@link InterProcessMutex}s * * @param client the client * @param paths list of paths to manage in the order that they are to be locked */ public InterProcessMultiLock(CuratorFramework client, List paths) { // paths get checked in each individual InterProcessMutex, so trust them here this(makeLocks(client, paths)); } /** * Creates a multi lock of any type of inter process lock * * @param locks the locks */ public InterProcessMultiLock(List locks) { this.locks = ImmutableList.copyOf(locks); } private static List makeLocks(CuratorFramework client, List paths) { ImmutableList.Builder builder = ImmutableList.builder(); for ( String path : paths ) { InterProcessLock lock = new InterProcessMutex(client, path); builder.add(lock); } return builder.build(); } /** * {@inheritDoc} */ @Override public void acquire() throws Exception { acquire(-1, null); } /** * {@inheritDoc} */ @Override public boolean acquire(long time, TimeUnit unit) throws Exception { Exception exception = null; List acquired = Lists.newArrayList(); boolean success = true; for ( InterProcessLock lock : locks ) { try { if ( unit == null ) { lock.acquire(); acquired.add(lock); } else { if ( lock.acquire(time, unit) ) { acquired.add(lock); } else { success = false; break; } } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); success = false; exception = e; } } if ( !success ) { for ( InterProcessLock lock : reverse(acquired) ) { try { lock.release(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); // ignore } } } if ( exception != null ) { throw exception; } return success; } /** * {@inheritDoc} * *

NOTE: locks are released in the reverse order that they were acquired.

* * @throws Exception ZK errors, interruptions, current thread does not own the lock * */ @Override public synchronized void release() throws Exception { Exception baseException = null; for ( InterProcessLock lock : reverse(locks) ) { try { lock.release(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); if ( baseException == null ) { baseException = e; } else { baseException = new Exception(baseException); } } } if ( baseException != null ) { throw baseException; } } @Override public synchronized boolean isAcquiredInThisProcess() { // it's subjective what the correct meaning is here - I choose to return true // only if all of the locks are acquired for ( InterProcessLock lock : locks ) { if ( !lock.isAcquiredInThisProcess() ) { return false; } } return true; } } InterProcessMutex.java000066400000000000000000000174261442004423600403300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import com.google.common.collect.Maps; import com.google.common.util.concurrent.MoreExecutors; import org.apache.curator.framework.CuratorFramework; import java.io.IOException; import java.util.Collection; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.curator.utils.PathUtils; /** * A re-entrant mutex that works across JVMs. Uses Zookeeper to hold the lock. All processes in all JVMs that * use the same lock path will achieve an inter-process critical section. Further, this mutex is * "fair" - each user will get the mutex in the order requested (from ZK's point of view) */ public class InterProcessMutex implements InterProcessLock, Revocable { private final LockInternals internals; private final String basePath; private final ConcurrentMap threadData = Maps.newConcurrentMap(); private static class LockData { final Thread owningThread; final String lockPath; final AtomicInteger lockCount = new AtomicInteger(1); private LockData(Thread owningThread, String lockPath) { this.owningThread = owningThread; this.lockPath = lockPath; } } private static final String LOCK_NAME = "lock-"; /** * @param client client * @param path the path to lock */ public InterProcessMutex(CuratorFramework client, String path) { this(client, path, new StandardLockInternalsDriver()); } /** * @param client client * @param path the path to lock * @param driver lock driver */ public InterProcessMutex(CuratorFramework client, String path, LockInternalsDriver driver) { this(client, path, LOCK_NAME, 1, driver); } /** * Acquire the mutex - blocking until it's available. Note: the same thread * can call acquire re-entrantly. Each call to acquire must be balanced by a call * to {@link #release()} * * @throws Exception ZK errors, connection interruptions */ @Override public void acquire() throws Exception { if ( !internalLock(-1, null) ) { throw new IOException("Lost connection while trying to acquire lock: " + basePath); } } /** * Acquire the mutex - blocks until it's available or the given time expires. Note: the same thread * can call acquire re-entrantly. Each call to acquire that returns true must be balanced by a call * to {@link #release()} * * @param time time to wait * @param unit time unit * @return true if the mutex was acquired, false if not * @throws Exception ZK errors, connection interruptions */ @Override public boolean acquire(long time, TimeUnit unit) throws Exception { return internalLock(time, unit); } /** * Returns true if the mutex is acquired by a thread in this JVM * * @return true/false */ @Override public boolean isAcquiredInThisProcess() { return (threadData.size() > 0); } /** * Perform one release of the mutex if the calling thread is the same thread that acquired it. If the * thread had made multiple calls to acquire, the mutex will still be held when this method returns. * * @throws Exception ZK errors, interruptions, current thread does not own the lock */ @Override public void release() throws Exception { /* Note on concurrency: a given lockData instance can be only acted on by a single thread so locking isn't necessary */ Thread currentThread = Thread.currentThread(); LockData lockData = threadData.get(currentThread); if ( lockData == null ) { throw new IllegalMonitorStateException("You do not own the lock: " + basePath); } int newLockCount = lockData.lockCount.decrementAndGet(); if ( newLockCount > 0 ) { return; } if ( newLockCount < 0 ) { throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + basePath); } try { internals.releaseLock(lockData.lockPath); } finally { threadData.remove(currentThread); } } /** * Return a sorted list of all current nodes participating in the lock * * @return list of nodes * @throws Exception ZK errors, interruptions, etc. */ public Collection getParticipantNodes() throws Exception { return LockInternals.getParticipantNodes(internals.getClient(), basePath, internals.getLockName(), internals.getDriver()); } @Override public void makeRevocable(RevocationListener listener) { makeRevocable(listener, MoreExecutors.directExecutor()); } @Override public void makeRevocable(final RevocationListener listener, Executor executor) { internals.makeRevocable(new RevocationSpec(executor, new Runnable() { @Override public void run() { listener.revocationRequested(InterProcessMutex.this); } })); } InterProcessMutex(CuratorFramework client, String path, String lockName, int maxLeases, LockInternalsDriver driver) { basePath = PathUtils.validatePath(path); internals = new LockInternals(client, driver, path, lockName, maxLeases); } /** * Returns true if the mutex is acquired by the calling thread * * @return true/false */ public boolean isOwnedByCurrentThread() { LockData lockData = threadData.get(Thread.currentThread()); return (lockData != null) && (lockData.lockCount.get() > 0); } protected byte[] getLockNodeBytes() { return null; } protected String getLockPath() { LockData lockData = threadData.get(Thread.currentThread()); return lockData != null ? lockData.lockPath : null; } private boolean internalLock(long time, TimeUnit unit) throws Exception { /* Note on concurrency: a given lockData instance can be only acted on by a single thread so locking isn't necessary */ Thread currentThread = Thread.currentThread(); LockData lockData = threadData.get(currentThread); if ( lockData != null ) { // re-entering lockData.lockCount.incrementAndGet(); return true; } String lockPath = internals.attemptLock(time, unit, getLockNodeBytes()); if ( lockPath != null ) { LockData newLockData = new LockData(currentThread, lockPath); threadData.put(currentThread, newLockData); return true; } return false; } } InterProcessReadWriteLock.java000066400000000000000000000173551442004423600417260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import org.apache.curator.framework.CuratorFramework; import java.util.Arrays; import java.util.Collection; import java.util.List; /** *

* A re-entrant read/write mutex that works across JVMs. Uses Zookeeper to hold the lock. All processes * in all JVMs that use the same lock path will achieve an inter-process critical section. Further, this mutex is * "fair" - each user will get the mutex in the order requested (from ZK's point of view). *

* *

* A read write lock maintains a pair of associated locks, one for read-only operations and one * for writing. The read lock may be held simultaneously by multiple reader processes, so long as * there are no writers. The write lock is exclusive. *

* *

* Reentrancy
* This lock allows both readers and writers to reacquire read or write locks in the style of a * re-entrant lock. Non-re-entrant readers are not allowed until all write locks held by the * writing thread/process have been released. Additionally, a writer can acquire the read lock, but not * vice-versa. If a reader tries to acquire the write lock it will never succeed.

* * Lock downgrading
* Re-entrancy also allows downgrading from the write lock to a read lock, by acquiring the write * lock, then the read lock and then releasing the write lock. However, upgrading from a read * lock to the write lock is not possible. *

*/ public class InterProcessReadWriteLock { private final ReadLock readMutex; private final WriteLock writeMutex; // must be the same length. LockInternals depends on it private static final String READ_LOCK_NAME = "__READ__"; private static final String WRITE_LOCK_NAME = "__WRIT__"; private static class SortingLockInternalsDriver extends StandardLockInternalsDriver { @Override public final String fixForSorting(String str, String lockName) { str = super.fixForSorting(str, READ_LOCK_NAME); str = super.fixForSorting(str, WRITE_LOCK_NAME); return str; } } private static class InternalInterProcessMutex extends InterProcessMutex { private final String lockName; private final byte[] lockData; InternalInterProcessMutex(CuratorFramework client, String path, String lockName, byte[] lockData, int maxLeases, LockInternalsDriver driver) { super(client, path, lockName, maxLeases, driver); this.lockName = lockName; this.lockData = (lockData == null) ? null : Arrays.copyOf(lockData, lockData.length); } @Override final public Collection getParticipantNodes() throws Exception { return ImmutableList.copyOf(Iterables.filter(super.getParticipantNodes(), new Predicate() { @Override public boolean apply(String node) { return node.contains(lockName); } })); } @Override final protected byte[] getLockNodeBytes() { return lockData; } @Override protected String getLockPath() { return super.getLockPath(); } } public static class WriteLock extends InternalInterProcessMutex { public WriteLock(CuratorFramework client, String basePath, byte[] lockData) { super(client, basePath, WRITE_LOCK_NAME, lockData, 1, new SortingLockInternalsDriver()); } @Override public String getLockPath() { return super.getLockPath(); } } public static class ReadLock extends InternalInterProcessMutex { public ReadLock(CuratorFramework client, String basePath, byte[] lockData, WriteLock writeLock) { super(client, basePath, READ_LOCK_NAME, lockData, Integer.MAX_VALUE, new SortingLockInternalsDriver() { @Override protected String getSortingSequence() { String writePath = writeLock.getLockPath(); if (writePath != null) { return fixForSorting(writePath, WRITE_LOCK_NAME); } return null; } @Override public PredicateResults getsTheLock( CuratorFramework client, List children, String sequenceNodeName, int maxLeases ) throws Exception { if (writeLock.isOwnedByCurrentThread()) { return new PredicateResults(null, true); } int index = 0; int firstWriteIndex = Integer.MAX_VALUE; int ourIndex = -1; for (String node : children) { if (node.contains(WRITE_LOCK_NAME)) { firstWriteIndex = Math.min(index, firstWriteIndex); } else if (node.startsWith(sequenceNodeName)) { ourIndex = index; break; } ++index; } validateOurIndex(sequenceNodeName, ourIndex); boolean getsTheLock = (ourIndex < firstWriteIndex); String pathToWatch = getsTheLock ? null : children.get(firstWriteIndex); return new PredicateResults(pathToWatch, getsTheLock); } }); } @Override public String getLockPath() { return super.getLockPath(); } } /** * @param client the client * @param basePath path to use for locking */ public InterProcessReadWriteLock(CuratorFramework client, String basePath) { this(client, basePath, null); } /** * @param client the client * @param basePath path to use for locking * @param lockData the data to store in the lock nodes */ public InterProcessReadWriteLock(CuratorFramework client, String basePath, byte[] lockData) { this.writeMutex = new WriteLock(client, basePath, lockData); this.readMutex = new ReadLock(client, basePath, lockData, writeMutex); } protected InterProcessReadWriteLock(WriteLock writeLock, ReadLock readLock) { this.writeMutex = writeLock; this.readMutex = readLock; } /** * Returns the lock used for reading. * * @return read lock */ public ReadLock readLock() { return readMutex; } /** * Returns the lock used for writing. * * @return write lock */ public WriteLock writeLock() { return writeMutex; } } InterProcessSemaphore.java000066400000000000000000000252571442004423600411520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.shared.SharedCountListener; import org.apache.curator.framework.recipes.shared.SharedCountReader; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.KeeperException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Collection; import java.util.concurrent.TimeUnit; /** *

* A counting semaphore that works across JVMs. All processes * in all JVMs that use the same lock path will achieve an inter-process limited set of leases. * Further, this semaphore is mostly "fair" - each user will get a lease in the order requested * (from ZK's point of view). *

* *

* There are two modes for determining the max leases for the semaphore. In the first mode the * max leases is a convention maintained by the users of a given path. In the second mode a * {@link SharedCountReader} is used as the method for semaphores of a given path to determine * the max leases. *

* *

* If a {@link SharedCountReader} is not used, no internal checks are done to prevent * Process A acting as if there are 10 leases and Process B acting as if there are 20. Therefore, * make sure that all instances in all processes use the same numberOfLeases value. *

* *

* The various acquire methods return {@link Lease} objects that represent acquired leases. Clients * must take care to close lease objects (ideally in a finally * block) else the lease will be lost. However, if the client session drops (crash, etc.), * any leases held by the client are * automatically closed and made available to other clients. *

* * @deprecated Use {@link InterProcessSemaphoreV2} instead of this class. It uses a better algorithm. */ @Deprecated public class InterProcessSemaphore { private final Logger log = LoggerFactory.getLogger(getClass()); private final LockInternals internals; private static final String LOCK_NAME = "lock-"; /** * @param client the client * @param path path for the semaphore * @param maxLeases the max number of leases to allow for this instance */ public InterProcessSemaphore(CuratorFramework client, String path, int maxLeases) { this(client, path, maxLeases, null); } /** * @param client the client * @param path path for the semaphore * @param count the shared count to use for the max leases */ public InterProcessSemaphore(CuratorFramework client, String path, SharedCountReader count) { this(client, path, 0, count); } private InterProcessSemaphore(CuratorFramework client, String path, int maxLeases, SharedCountReader count) { // path verified in LockInternals internals = new LockInternals(client, new StandardLockInternalsDriver(), path, LOCK_NAME, (count != null) ? count.getCount() : maxLeases); if ( count != null ) { count.addListener ( new SharedCountListener() { @Override public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception { internals.setMaxLeases(newCount); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { // no need to handle this here - clients should set their own connection state listener } } ); } } /** * Convenience method. Closes all leases in the given collection of leases * * @param leases leases to close */ public void returnAll(Collection leases) { for ( Lease l : leases ) { CloseableUtils.closeQuietly(l); } } /** * Convenience method. Closes the lease * * @param lease lease to close */ public void returnLease(Lease lease) { CloseableUtils.closeQuietly(lease); } /** *

Acquire a lease. If no leases are available, this method blocks until either the maximum * number of leases is increased or another client/process closes a lease.

* *

The client must close the lease when it is done with it. You should do this in a * finally block.

* * @return the new lease * @throws Exception ZK errors, interruptions, etc. */ public Lease acquire() throws Exception { String path = internals.attemptLock(-1, null, null); return makeLease(path); } /** *

Acquire qty leases. If there are not enough leases available, this method * blocks until either the maximum number of leases is increased enough or other clients/processes * close enough leases.

* *

The client must close the leases when it is done with them. You should do this in a * finally block. NOTE: You can use {@link #returnAll(Collection)} for this.

* * @param qty number of leases to acquire * @return the new leases * @throws Exception ZK errors, interruptions, etc. */ public Collection acquire(int qty) throws Exception { Preconditions.checkArgument(qty > 0, "qty cannot be 0"); ImmutableList.Builder builder = ImmutableList.builder(); try { while ( qty-- > 0 ) { String path = internals.attemptLock(-1, null, null); builder.add(makeLease(path)); } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); returnAll(builder.build()); throw e; } return builder.build(); } /** *

Acquire a lease. If no leases are available, this method blocks until either the maximum * number of leases is increased or another client/process closes a lease. However, this method * will only block to a maximum of the time parameters given.

* *

The client must close the lease when it is done with it. You should do this in a * finally block.

* * @param time time to wait * @param unit time unit * @return the new lease or null if time ran out * @throws Exception ZK errors, interruptions, etc. */ public Lease acquire(long time, TimeUnit unit) throws Exception { String path = internals.attemptLock(time, unit, null); return (path != null) ? makeLease(path) : null; } /** *

Acquire qty leases. If there are not enough leases available, this method * blocks until either the maximum number of leases is increased enough or other clients/processes * close enough leases. However, this method will only block to a maximum of the time * parameters given. If time expires before all leases are acquired, the subset of acquired * leases are automatically closed.

* *

The client must close the leases when it is done with them. You should do this in a * finally block. NOTE: You can use {@link #returnAll(Collection)} for this.

* * @param qty number of leases to acquire * @param time time to wait * @param unit time unit * @return the new leases or null if time ran out * @throws Exception ZK errors, interruptions, etc. */ public Collection acquire(int qty, long time, TimeUnit unit) throws Exception { long startMs = System.currentTimeMillis(); long waitMs = TimeUnit.MILLISECONDS.convert(time, unit); Preconditions.checkArgument(qty > 0, "qty cannot be 0"); ImmutableList.Builder builder = ImmutableList.builder(); try { while ( qty-- > 0 ) { long elapsedMs = System.currentTimeMillis() - startMs; long thisWaitMs = waitMs - elapsedMs; String path = (thisWaitMs > 0) ? internals.attemptLock(thisWaitMs, TimeUnit.MILLISECONDS, null) : null; if ( path == null ) { returnAll(builder.build()); return null; } builder.add(makeLease(path)); } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); returnAll(builder.build()); throw e; } return builder.build(); } private Lease makeLease(final String path) { return new Lease() { @Override public void close() throws IOException { try { internals.releaseLock(path); } catch ( KeeperException.NoNodeException e ) { log.warn("Lease already released", e); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new IOException(e); } } @Override public byte[] getData() throws Exception { return internals.getClient().getData().forPath(path); } @Override public String getNodeName() { return ZKPaths.getNodeFromPath(path); } }; } } InterProcessSemaphoreMutex.java000066400000000000000000000056261442004423600421730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import java.util.concurrent.TimeUnit; /** * A NON re-entrant mutex that works across JVMs. Uses Zookeeper to hold the lock. All processes in all JVMs that * use the same lock path will achieve an inter-process critical section. */ public class InterProcessSemaphoreMutex implements InterProcessLock { private final InterProcessSemaphoreV2 semaphore; private final WatcherRemoveCuratorFramework watcherRemoveClient; private volatile Lease lease; /** * @param client the client * @param path path for the lock */ public InterProcessSemaphoreMutex(CuratorFramework client, String path) { watcherRemoveClient = client.newWatcherRemoveCuratorFramework(); this.semaphore = new InterProcessSemaphoreV2(watcherRemoveClient, path, 1); } @Override public void acquire() throws Exception { lease = semaphore.acquire(); } @Override public boolean acquire(long time, TimeUnit unit) throws Exception { Lease acquiredLease = semaphore.acquire(time, unit); if ( acquiredLease == null ) { return false; // important - don't overwrite lease field if couldn't be acquired } lease = acquiredLease; return true; } /** * {@inheritDoc} * *

NOTE: Unlike other implementations of {@link org.apache.curator.framework.recipes.locks.InterProcessLock#release()}, * this method will NOT throw an exception if it is called on a different thread than the one which acquired the lock.

* */ @Override public void release() throws Exception { Lease lease = this.lease; Preconditions.checkState(lease != null, "Not acquired"); this.lease = null; lease.close(); watcherRemoveClient.removeWatchers(); } @Override public boolean isAcquiredInThisProcess() { return (lease != null); } } InterProcessSemaphoreV2.java000066400000000000000000000424131442004423600413530ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import org.apache.curator.RetryLoop; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.PathAndBytesable; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.framework.recipes.shared.SharedCountListener; import org.apache.curator.framework.recipes.shared.SharedCountReader; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** *

* A counting semaphore that works across JVMs. All processes * in all JVMs that use the same lock path will achieve an inter-process limited set of leases. * Further, this semaphore is mostly "fair" - each user will get a lease in the order requested * (from ZK's point of view). *

*

* There are two modes for determining the max leases for the semaphore. In the first mode the * max leases is a convention maintained by the users of a given path. In the second mode a * {@link SharedCountReader} is used as the method for semaphores of a given path to determine * the max leases. *

*

* If a {@link SharedCountReader} is not used, no internal checks are done to prevent * Process A acting as if there are 10 leases and Process B acting as if there are 20. Therefore, * make sure that all instances in all processes use the same numberOfLeases value. *

*

* The various acquire methods return {@link Lease} objects that represent acquired leases. Clients * must take care to close lease objects (ideally in a finally * block) else the lease will be lost. However, if the client session drops (crash, etc.), * any leases held by the client are automatically closed and made available to other clients. *

*

* Thanks to Ben Bangert (ben@groovie.org) for the algorithm used. *

*/ public class InterProcessSemaphoreV2 { private final Logger log = LoggerFactory.getLogger(getClass()); private final InterProcessMutex lock; private final WatcherRemoveCuratorFramework client; private final String leasesPath; private final Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { client.postSafeNotify(InterProcessSemaphoreV2.this); } }; private volatile byte[] nodeData; private volatile int maxLeases; private static final String LOCK_PARENT = "locks"; private static final String LEASE_PARENT = "leases"; private static final String LEASE_BASE_NAME = "lease-"; public static final Set LOCK_SCHEMA = Sets.newHashSet( LOCK_PARENT, LEASE_PARENT ); /** * @param client the client * @param path path for the semaphore * @param maxLeases the max number of leases to allow for this instance */ public InterProcessSemaphoreV2(CuratorFramework client, String path, int maxLeases) { this(client, path, maxLeases, null); } /** * @param client the client * @param path path for the semaphore * @param count the shared count to use for the max leases */ public InterProcessSemaphoreV2(CuratorFramework client, String path, SharedCountReader count) { this(client, path, 0, count); } private InterProcessSemaphoreV2(CuratorFramework client, String path, int maxLeases, SharedCountReader count) { this.client = client.newWatcherRemoveCuratorFramework(); path = PathUtils.validatePath(path); lock = new InterProcessMutex(client, ZKPaths.makePath(path, LOCK_PARENT)); this.maxLeases = (count != null) ? count.getCount() : maxLeases; leasesPath = ZKPaths.makePath(path, LEASE_PARENT); if ( count != null ) { count.addListener ( new SharedCountListener() { @Override public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception { InterProcessSemaphoreV2.this.maxLeases = newCount; client.postSafeNotify(InterProcessSemaphoreV2.this); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { // no need to handle this here - clients should set their own connection state listener } } ); } } /** * Set the data to put for the node created by this semaphore. This must be called prior to calling one * of the acquire() methods. * * @param nodeData node data */ public void setNodeData(byte[] nodeData) { this.nodeData = (nodeData != null) ? Arrays.copyOf(nodeData, nodeData.length) : null; } /** * Return a list of all current nodes participating in the semaphore * * @return list of nodes * @throws Exception ZK errors, interruptions, etc. */ public Collection getParticipantNodes() throws Exception { return client.getChildren().forPath(leasesPath); } /** * Convenience method. Closes all leases in the given collection of leases * * @param leases leases to close */ public void returnAll(Collection leases) { for ( Lease l : leases ) { CloseableUtils.closeQuietly(l); } } /** * Convenience method. Closes the lease * * @param lease lease to close */ public void returnLease(Lease lease) { CloseableUtils.closeQuietly(lease); } /** *

Acquire a lease. If no leases are available, this method blocks until either the maximum * number of leases is increased or another client/process closes a lease.

*

The client must close the lease when it is done with it. You should do this in a * finally block.

* * @return the new lease * @throws Exception ZK errors, interruptions, etc. */ public Lease acquire() throws Exception { Collection leases = acquire(1, 0, null); return leases.iterator().next(); } /** *

Acquire qty leases. If there are not enough leases available, this method * blocks until either the maximum number of leases is increased enough or other clients/processes * close enough leases.

*

The client must close the leases when it is done with them. You should do this in a * finally block. NOTE: You can use {@link #returnAll(Collection)} for this.

* * @param qty number of leases to acquire * @return the new leases * @throws Exception ZK errors, interruptions, etc. */ public Collection acquire(int qty) throws Exception { return acquire(qty, 0, null); } /** *

Acquire a lease. If no leases are available, this method blocks until either the maximum * number of leases is increased or another client/process closes a lease. However, this method * will only block to a maximum of the time parameters given.

*

The client must close the lease when it is done with it. You should do this in a * finally block.

* * @param time time to wait * @param unit time unit * @return the new lease or null if time ran out * @throws Exception ZK errors, interruptions, etc. */ public Lease acquire(long time, TimeUnit unit) throws Exception { Collection leases = acquire(1, time, unit); return (leases != null) ? leases.iterator().next() : null; } /** *

Acquire qty leases. If there are not enough leases available, this method * blocks until either the maximum number of leases is increased enough or other clients/processes * close enough leases. However, this method will only block to a maximum of the time * parameters given. If time expires before all leases are acquired, the subset of acquired * leases are automatically closed.

*

The client must close the leases when it is done with them. You should do this in a * finally block. NOTE: You can use {@link #returnAll(Collection)} for this.

* * @param qty number of leases to acquire * @param time time to wait * @param unit time unit * @return the new leases or null if time ran out * @throws Exception ZK errors, interruptions, etc. */ public Collection acquire(int qty, long time, TimeUnit unit) throws Exception { long startMs = System.currentTimeMillis(); boolean hasWait = (unit != null); long waitMs = hasWait ? TimeUnit.MILLISECONDS.convert(time, unit) : 0; Preconditions.checkArgument(qty > 0, "qty cannot be 0"); ImmutableList.Builder builder = ImmutableList.builder(); boolean success = false; try { while ( qty-- > 0 ) { int retryCount = 0; long startMillis = System.currentTimeMillis(); boolean isDone = false; while ( !isDone ) { switch ( internalAcquire1Lease(builder, startMs, hasWait, waitMs) ) { case CONTINUE: { isDone = true; break; } case RETURN_NULL: { return null; } case RETRY_DUE_TO_MISSING_NODE: { // gets thrown by internalAcquire1Lease when it can't find the lock node // this can happen when the session expires, etc. So, if the retry allows, just try it all again if ( !client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper()) ) { throw new KeeperException.NoNodeException("Sequential path not found - possible session loss"); } // try again break; } } } } success = true; } finally { if ( !success ) { returnAll(builder.build()); } } return builder.build(); } private enum InternalAcquireResult { CONTINUE, RETURN_NULL, RETRY_DUE_TO_MISSING_NODE } static volatile CountDownLatch debugAcquireLatch = null; static volatile CountDownLatch debugFailedGetChildrenLatch = null; volatile CountDownLatch debugWaitLatch = null; private InternalAcquireResult internalAcquire1Lease(ImmutableList.Builder builder, long startMs, boolean hasWait, long waitMs) throws Exception { if ( client.getState() != CuratorFrameworkState.STARTED ) { return InternalAcquireResult.RETURN_NULL; } if ( hasWait ) { long thisWaitMs = getThisWaitMs(startMs, waitMs); if ( !lock.acquire(thisWaitMs, TimeUnit.MILLISECONDS) ) { return InternalAcquireResult.RETURN_NULL; } } else { lock.acquire(); } Lease lease = null; boolean success = false; try { PathAndBytesable createBuilder = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL); String path = (nodeData != null) ? createBuilder.forPath(ZKPaths.makePath(leasesPath, LEASE_BASE_NAME), nodeData) : createBuilder.forPath(ZKPaths.makePath(leasesPath, LEASE_BASE_NAME)); String nodeName = ZKPaths.getNodeFromPath(path); lease = makeLease(path); if ( debugAcquireLatch != null ) { debugAcquireLatch.await(); } try { synchronized(this) { for(;;) { List children; try { children = client.getChildren().usingWatcher(watcher).forPath(leasesPath); } catch ( Exception e ) { if ( debugFailedGetChildrenLatch != null ) { debugFailedGetChildrenLatch.countDown(); } throw e; } if ( !children.contains(nodeName) ) { log.error("Sequential path not found: " + path); return InternalAcquireResult.RETRY_DUE_TO_MISSING_NODE; } if ( children.size() <= maxLeases ) { break; } if ( hasWait ) { long thisWaitMs = getThisWaitMs(startMs, waitMs); if ( thisWaitMs <= 0 ) { return InternalAcquireResult.RETURN_NULL; } if ( debugWaitLatch != null ) { debugWaitLatch.countDown(); } wait(thisWaitMs); } else { if ( debugWaitLatch != null ) { debugWaitLatch.countDown(); } wait(); } } success = true; } } finally { if ( !success ) { returnLease(lease); } client.removeWatchers(); } } finally { lock.release(); } builder.add(Preconditions.checkNotNull(lease)); return InternalAcquireResult.CONTINUE; } private long getThisWaitMs(long startMs, long waitMs) { long elapsedMs = System.currentTimeMillis() - startMs; return waitMs - elapsedMs; } private Lease makeLease(final String path) { return new Lease() { @Override public void close() throws IOException { try { client.delete().guaranteed().forPath(path); } catch ( KeeperException.NoNodeException e ) { log.warn("Lease already released", e); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new IOException(e); } } @Override public byte[] getData() throws Exception { return client.getData().forPath(path); } @Override public String getNodeName() { return ZKPaths.getNodeFromPath(path); } }; } } Lease.java000066400000000000000000000033701442004423600357070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import java.io.Closeable; import java.io.IOException; /** * Represents an acquired lease from an {@link InterProcessSemaphore}. It is the client's responsibility * to close this lease when it is no longer needed so that other blocked clients can use it. If the * client crashes (or its session expires, etc.) the lease will automatically be closed. */ public interface Lease extends Closeable { /** * Releases the lease so that other clients/processes can acquire it * * @throws IOException errors */ @Override public void close() throws IOException; /** * Return the data stored in the node for this lease * * @return data * @throws Exception errors */ public byte[] getData() throws Exception; /** * Return the the node for this lease * * @return data * @throws Exception errors */ public String getNodeName(); } LockInternals.java000066400000000000000000000273431442004423600374340ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.curator.RetryLoop; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; public class LockInternals { private final WatcherRemoveCuratorFramework client; private final String path; private final String basePath; private final LockInternalsDriver driver; private final String lockName; private final AtomicReference revocable = new AtomicReference(null); private final CuratorWatcher revocableWatcher = new CuratorWatcher() { @Override public void process(WatchedEvent event) throws Exception { if ( event.getType() == Watcher.Event.EventType.NodeDataChanged ) { checkRevocableWatcher(event.getPath()); } } }; private final Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { client.postSafeNotify(LockInternals.this); } }; private volatile int maxLeases; static final byte[] REVOKE_MESSAGE = "__REVOKE__".getBytes(); /** * Attempt to delete the lock node so that sequence numbers get reset * * @throws Exception errors */ public void clean() throws Exception { try { client.delete().forPath(basePath); } catch ( KeeperException.BadVersionException ignore ) { // ignore - another thread/process got the lock } catch ( KeeperException.NotEmptyException ignore ) { // ignore - other threads/processes are waiting } } LockInternals(CuratorFramework client, LockInternalsDriver driver, String path, String lockName, int maxLeases) { this.driver = driver; this.lockName = lockName; this.maxLeases = maxLeases; this.client = client.newWatcherRemoveCuratorFramework(); this.basePath = PathUtils.validatePath(path); this.path = ZKPaths.makePath(path, lockName); } synchronized void setMaxLeases(int maxLeases) { this.maxLeases = maxLeases; notifyAll(); } void makeRevocable(RevocationSpec entry) { revocable.set(entry); } final void releaseLock(String lockPath) throws Exception { client.removeWatchers(); revocable.set(null); deleteOurPath(lockPath); } CuratorFramework getClient() { return client; } public static Collection getParticipantNodes(CuratorFramework client, final String basePath, String lockName, LockInternalsSorter sorter) throws Exception { List names = getSortedChildren(client, basePath, lockName, sorter); Iterable transformed = Iterables.transform ( names, new Function() { @Override public String apply(String name) { return ZKPaths.makePath(basePath, name); } } ); return ImmutableList.copyOf(transformed); } public static List getSortedChildren(CuratorFramework client, String basePath, final String lockName, final LockInternalsSorter sorter) throws Exception { try { List children = client.getChildren().forPath(basePath); List sortedList = Lists.newArrayList(children); Collections.sort ( sortedList, new Comparator() { @Override public int compare(String lhs, String rhs) { return sorter.fixForSorting(lhs, lockName).compareTo(sorter.fixForSorting(rhs, lockName)); } } ); return sortedList; } catch ( KeeperException.NoNodeException ignore ) { return Collections.emptyList(); } } public static List getSortedChildren(final String lockName, final LockInternalsSorter sorter, List children) { List sortedList = Lists.newArrayList(children); Collections.sort ( sortedList, new Comparator() { @Override public int compare(String lhs, String rhs) { return sorter.fixForSorting(lhs, lockName).compareTo(sorter.fixForSorting(rhs, lockName)); } } ); return sortedList; } List getSortedChildren() throws Exception { return getSortedChildren(client, basePath, lockName, driver); } String getLockName() { return lockName; } LockInternalsDriver getDriver() { return driver; } String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception { final long startMillis = System.currentTimeMillis(); final Long millisToWait = (unit != null) ? unit.toMillis(time) : null; final byte[] localLockNodeBytes = (revocable.get() != null) ? new byte[0] : lockNodeBytes; int retryCount = 0; String ourPath = null; boolean hasTheLock = false; boolean isDone = false; while ( !isDone ) { isDone = true; try { ourPath = driver.createsTheLock(client, path, localLockNodeBytes); hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath); } catch ( KeeperException.NoNodeException e ) { // gets thrown by StandardLockInternalsDriver when it can't find the lock node // this can happen when the session expires, etc. So, if the retry allows, just try it all again if ( client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper()) ) { isDone = false; } else { throw e; } } } if ( hasTheLock ) { return ourPath; } return null; } private void checkRevocableWatcher(String path) throws Exception { RevocationSpec entry = revocable.get(); if ( entry != null ) { try { byte[] bytes = client.getData().usingWatcher(revocableWatcher).forPath(path); if ( Arrays.equals(bytes, REVOKE_MESSAGE) ) { entry.getExecutor().execute(entry.getRunnable()); } } catch ( KeeperException.NoNodeException ignore ) { // ignore } } } private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) throws Exception { boolean haveTheLock = false; boolean doDelete = false; try { if ( revocable.get() != null ) { client.getData().usingWatcher(revocableWatcher).forPath(ourPath); } while ( (client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock ) { List children = getSortedChildren(); String sequenceNodeName = ourPath.substring(basePath.length() + 1); // +1 to include the slash PredicateResults predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases); if ( predicateResults.getsTheLock() ) { haveTheLock = true; } else { String previousSequencePath = basePath + "/" + predicateResults.getPathToWatch(); synchronized(this) { try { // use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak client.getData().usingWatcher(watcher).forPath(previousSequencePath); if ( millisToWait != null ) { millisToWait -= (System.currentTimeMillis() - startMillis); startMillis = System.currentTimeMillis(); if ( millisToWait <= 0 ) { doDelete = true; // timed out - delete our node break; } wait(millisToWait); } else { wait(); } } catch ( KeeperException.NoNodeException e ) { // it has been deleted (i.e. lock released). Try to acquire again } } } } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); doDelete = true; throw e; } finally { if ( doDelete ) { deleteOurPath(ourPath); } } return haveTheLock; } private void deleteOurPath(String ourPath) throws Exception { try { client.delete().guaranteed().forPath(ourPath); } catch ( KeeperException.NoNodeException e ) { // ignore - already deleted (possibly expired session, etc.) } } } LockInternalsDriver.java000066400000000000000000000023641442004423600406040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import org.apache.curator.framework.CuratorFramework; import java.util.List; public interface LockInternalsDriver extends LockInternalsSorter { public PredicateResults getsTheLock(CuratorFramework client, List children, String sequenceNodeName, int maxLeases) throws Exception; public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception; } LockInternalsSorter.java000066400000000000000000000017171442004423600406300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; public interface LockInternalsSorter { public String fixForSorting(String str, String lockName); } Locker.java000066400000000000000000000062221442004423600360740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; /** *

* Utility for safely acquiring a lock and releasing it using Java 7's * try-with-resource feature. *

* *

* Canonical usage: *

 *     InterProcessMutex mutex = new InterProcessMutex(...) // or any InterProcessLock
 *     try ( Locker locker = new Locker(mutex, maxTimeout, unit) )
 *     {
 *         // do work
 *     }
 * 
*

*/ public class Locker implements AutoCloseable { private final InterProcessLock lock; private final AtomicBoolean acquired = new AtomicBoolean(false); /** * @param lock a lock implementation (e.g. {@link InterProcessMutex}, {@link InterProcessSemaphoreV2}, etc.) * @param timeout max timeout to acquire lock * @param unit time unit of timeout * @throws Exception Curator errors or {@link TimeoutException} if the lock cannot be acquired within the timeout */ public Locker(InterProcessLock lock, long timeout, TimeUnit unit) throws Exception { this.lock = lock; acquired.set(acquireLock(lock, timeout, unit)); if ( !acquired.get() ) { throw new TimeoutException("Could not acquire lock within timeout of " + unit.toMillis(timeout) + "ms"); } } /** * @param lock a lock implementation (e.g. {@link InterProcessMutex}, {@link InterProcessSemaphoreV2}, etc.) * @throws Exception errors */ public Locker(InterProcessLock lock) throws Exception { this.lock = lock; acquireLock(lock); acquired.set(true); } @Override /** * Relase the lock if it has been acquired. Can be safely called multiple times. * Only the first call will unlock. */ public void close() throws Exception { if ( acquired.compareAndSet(true, false) ) { releaseLock(); } } protected void releaseLock() throws Exception { lock.release(); } protected void acquireLock(InterProcessLock lock) throws Exception { lock.acquire(); } protected boolean acquireLock(InterProcessLock lock, long timeout, TimeUnit unit) throws Exception { return lock.acquire(timeout, unit); } } PredicateResults.java000066400000000000000000000024121442004423600401340ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; public class PredicateResults { private final boolean getsTheLock; private final String pathToWatch; public PredicateResults(String pathToWatch, boolean getsTheLock) { this.pathToWatch = pathToWatch; this.getsTheLock = getsTheLock; } public String getPathToWatch() { return pathToWatch; } public boolean getsTheLock() { return getsTheLock; } } Revocable.java000066400000000000000000000031151442004423600365550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import java.util.concurrent.Executor; /** * Specifies locks that can be revoked */ public interface Revocable { /** * Make the lock revocable. Your listener will get called when another process/thread * wants you to release the lock. Revocation is cooperative. * * @param listener the listener */ public void makeRevocable(RevocationListener listener); /** * Make the lock revocable. Your listener will get called when another process/thread * wants you to release the lock. Revocation is cooperative. * * @param listener the listener * @param executor executor for the listener */ public void makeRevocable(RevocationListener listener, Executor executor); } RevocationListener.java000066400000000000000000000022311442004423600404700ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; public interface RevocationListener { /** * Called when a revocation request has been received. You should release the lock as soon * as possible. Revocation is cooperative. * * @param forLock the lock that should release */ public void revocationRequested(T forLock); } RevocationSpec.java000066400000000000000000000023761442004423600376070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import java.util.concurrent.Executor; class RevocationSpec { private final Runnable runnable; private final Executor executor; RevocationSpec(Executor executor, Runnable runnable) { this.runnable = runnable; this.executor = executor; } Runnable getRunnable() { return runnable; } Executor getExecutor() { return executor; } } Revoker.java000066400000000000000000000033651442004423600362770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.KeeperException; public class Revoker { /** * Utility to mark a lock for revocation. Assuming that the lock has been registered with * a {@link RevocationListener}, it will get called and the lock should be released. Note, * however, that revocation is cooperative. * * @param client the client * @param path the path of the lock - usually from something like * {@link InterProcessMutex#getParticipantNodes()} * @throws Exception errors */ public static void attemptRevoke(CuratorFramework client, String path) throws Exception { try { client.setData().forPath(path, LockInternals.REVOKE_MESSAGE); } catch ( KeeperException.NoNodeException ignore ) { // ignore } } private Revoker() { } } StandardLockInternalsDriver.java000066400000000000000000000064071442004423600422670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; public class StandardLockInternalsDriver implements LockInternalsDriver { static private final Logger log = LoggerFactory.getLogger(StandardLockInternalsDriver.class); @Override public PredicateResults getsTheLock(CuratorFramework client, List children, String sequenceNodeName, int maxLeases) throws Exception { int ourIndex = children.indexOf(sequenceNodeName); validateOurIndex(sequenceNodeName, ourIndex); boolean getsTheLock = ourIndex < maxLeases; String pathToWatch = getsTheLock ? null : children.get(ourIndex - maxLeases); return new PredicateResults(pathToWatch, getsTheLock); } protected String getSortingSequence() { return null; } @Override public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception { CreateMode createMode = CreateMode.EPHEMERAL_SEQUENTIAL; String sequence = getSortingSequence(); if (sequence != null) { path += sequence; createMode = CreateMode.EPHEMERAL; } String ourPath; if ( lockNodeBytes != null ) { ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(createMode).forPath(path, lockNodeBytes); } else { ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(createMode).forPath(path); } return ourPath; } @Override public String fixForSorting(String str, String lockName) { return standardFixForSorting(str, lockName); } public static String standardFixForSorting(String str, String lockName) { int index = str.lastIndexOf(lockName); if ( index >= 0 ) { index += lockName.length(); return index <= str.length() ? str.substring(index) : ""; } return str; } static void validateOurIndex(String sequenceNodeName, int ourIndex) throws KeeperException { if ( ourIndex < 0 ) { throw new KeeperException.NoNodeException("Sequential path not found: " + sequenceNodeName); } } } nodes/000077500000000000000000000000001442004423600340055ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipesGroupMember.java000066400000000000000000000117531442004423600371030ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.CuratorCache; import org.apache.curator.framework.recipes.cache.CuratorCacheBridge; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import java.io.Closeable; import java.util.Iterator; import java.util.Map; import static org.apache.curator.framework.recipes.cache.CuratorCacheAccessor.parentPathFilter; /** * Group membership management. Adds this instance into a group and * keeps a cache of members in the group */ public class GroupMember implements Closeable { private final PersistentNode pen; private final CuratorCacheBridge cache; private final String membershipPath; private final String thisId; /** * @param client client * @param membershipPath the path to use for membership * @param thisId ID of this group member. MUST be unique for the group */ public GroupMember(CuratorFramework client, String membershipPath, String thisId) { this(client, membershipPath, thisId, CuratorFrameworkFactory.getLocalAddress()); } /** * @param client client * @param membershipPath the path to use for membership * @param thisId ID of this group member. MUST be unique for the group * @param payload the payload to write in our member node */ public GroupMember(CuratorFramework client, String membershipPath, String thisId, byte[] payload) { this.membershipPath = membershipPath; this.thisId = Preconditions.checkNotNull(thisId, "thisId cannot be null"); cache = CuratorCache.bridgeBuilder(client, membershipPath).build(); pen = new PersistentNode(client, CreateMode.EPHEMERAL, false, ZKPaths.makePath(membershipPath, thisId), payload); } /** * Start the group membership. Register thisId as a member and begin * caching all members */ public void start() { pen.start(); try { cache.start(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); Throwables.propagate(e); } } /** * Change the data stored in this instance's node * * @param data new data (cannot be null) */ public void setThisData(byte[] data) { try { pen.setData(data); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); Throwables.propagate(e); } } /** * Have thisId leave the group and stop caching membership */ @Override public void close() { CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(pen); } /** * Return the current view of membership. The keys are the IDs * of the members. The values are each member's payload * * @return membership */ public Map getCurrentMembers() { ImmutableMap.Builder builder = ImmutableMap.builder(); boolean thisIdAdded = false; Iterator iterator = cache.stream().filter(parentPathFilter(membershipPath)).iterator(); while ( iterator.hasNext() ) { ChildData data = iterator.next(); String id = idFromPath(data.getPath()); thisIdAdded = thisIdAdded || id.equals(thisId); builder.put(id, data.getData()); } if ( !thisIdAdded ) { builder.put(thisId, pen.getData()); // this instance is always a member } return builder.build(); } /** * Given a full ZNode path, return the member ID * * @param path full ZNode path * @return id */ public String idFromPath(String path) { return ZKPaths.getNodeFromPath(path); } } PersistentEphemeralNode.java000066400000000000000000000100341442004423600414370ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.CreateMode; /** *

* A persistent ephemeral node is an ephemeral node that attempts to stay present in * ZooKeeper, even through connection and session interruptions. *

*

* Thanks to bbeck (https://github.com/bbeck) for the initial coding and design *

* * @deprecated This has been replaced with the more general {@link PersistentNode} */ @Deprecated public class PersistentEphemeralNode extends PersistentNode { /** * The mode for node creation * * @deprecated This has been replaced with the more general {@link PersistentNode} */ @Deprecated public enum Mode { /** * Same as {@link CreateMode#EPHEMERAL} */ EPHEMERAL() { @Override protected CreateMode getCreateMode(boolean pathIsSet) { return CreateMode.EPHEMERAL; } @Override protected boolean isProtected() { return false; } }, /** * Same as {@link CreateMode#EPHEMERAL_SEQUENTIAL} */ EPHEMERAL_SEQUENTIAL() { @Override protected CreateMode getCreateMode(boolean pathIsSet) { return pathIsSet ? CreateMode.EPHEMERAL : CreateMode.EPHEMERAL_SEQUENTIAL; } @Override protected boolean isProtected() { return false; } }, /** * Same as {@link CreateMode#EPHEMERAL} with protection */ PROTECTED_EPHEMERAL() { @Override protected CreateMode getCreateMode(boolean pathIsSet) { return CreateMode.EPHEMERAL; } @Override protected boolean isProtected() { return true; } }, /** * Same as {@link CreateMode#EPHEMERAL_SEQUENTIAL} with protection */ PROTECTED_EPHEMERAL_SEQUENTIAL() { @Override protected CreateMode getCreateMode(boolean pathIsSet) { return pathIsSet ? CreateMode.EPHEMERAL : CreateMode.EPHEMERAL_SEQUENTIAL; } @Override protected boolean isProtected() { return true; } }; protected abstract CreateMode getCreateMode(boolean pathIsSet); protected abstract boolean isProtected(); } /** * @param client client instance * @param mode creation/protection mode * @param basePath the base path for the node * @param initData data for the node */ @SuppressWarnings("deprecation") public PersistentEphemeralNode(CuratorFramework client, Mode mode, String basePath, byte[] initData) { super(client, mode.getCreateMode(false), mode.isProtected(), basePath, initData); } } PersistentNode.java000066400000000000000000000467601442004423600376330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CreateBuilder; import org.apache.curator.framework.api.CreateBuilderMain; import org.apache.curator.framework.api.CreateModable; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; /** *

* A persistent node is a node that attempts to stay present in * ZooKeeper, even through connection and session interruptions. *

*

* Thanks to bbeck (https://github.com/bbeck) for the initial coding and design *

*/ public class PersistentNode implements Closeable { private final AtomicReference initialCreateLatch = new AtomicReference(new CountDownLatch(1)); private final Logger log = LoggerFactory.getLogger(getClass()); private final WatcherRemoveCuratorFramework client; private final AtomicReference nodePath = new AtomicReference(null); private final String basePath; private final CreateMode mode; private final long ttl; private final AtomicReference data = new AtomicReference(); private final AtomicReference state = new AtomicReference(State.LATENT); private volatile boolean authFailure; private volatile boolean parentCreationFailure; private final BackgroundCallback backgroundCallback; private final boolean useProtection; private final boolean useParentCreation; private final AtomicReference>> createMethod = new AtomicReference>>(null); private final StandardListenerManager listeners = StandardListenerManager.standard(); private final CuratorWatcher watcher = new CuratorWatcher() { @Override public void process(WatchedEvent event) throws Exception { if ( isActive() ) { if ( event.getType() == EventType.NodeDeleted ) { createNode(); } else if ( event.getType() == EventType.NodeDataChanged ) { watchNode(); } } } }; private final BackgroundCallback checkExistsCallback = new BackgroundCallback() { @Override public void processResult(CuratorFramework dummy, CuratorEvent event) throws Exception { if ( isActive() ) { if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() ) { createNode(); } else { boolean isEphemeral = event.getStat().getEphemeralOwner() != 0; if ( isEphemeral != mode.isEphemeral() ) { log.warn("Existing node ephemeral state doesn't match requested state. Maybe the node was created outside of PersistentNode? " + basePath); } } } else { client.removeWatchers(); } } }; private final BackgroundCallback setDataCallback = new BackgroundCallback() { @Override public void processResult(CuratorFramework dummy, CuratorEvent event) throws Exception { //If the result is ok then initialisation is complete (if we're still initialising) //Don't retry on other errors as the only recoverable cases will be connection loss //and the node not existing, both of which are already handled by other watches. if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { //Update is ok, mark initialisation as complete if required. initialisationComplete(); } else if ( event.getResultCode() == KeeperException.Code.NOAUTH.intValue() ) { log.warn("Client does not have authorisation to write node at path {}", event.getPath()); authFailure = true; } } }; private final ConnectionStateListener connectionStateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework dummy, ConnectionState newState) { if ( (newState == ConnectionState.RECONNECTED) && isActive() ) { createNode(); } } }; @VisibleForTesting volatile CountDownLatch debugCreateNodeLatch = null; private enum State { LATENT, STARTED, CLOSED } /** * @param givenClient client instance * @param mode creation mode * @param useProtection if true, call {@link CreateBuilder#withProtection()} * @param basePath the base path for the node * @param initData data for the node */ public PersistentNode(CuratorFramework givenClient, final CreateMode mode, boolean useProtection, final String basePath, byte[] initData) { this(givenClient, mode, useProtection, basePath, initData, -1, true); } /** * @param givenClient client instance * @param mode creation mode * @param useProtection if true, call {@link CreateBuilder#withProtection()} * @param basePath the base path for the node * @param initData data for the node * @param useParentCreation if true, call {@link CreateBuilder#creatingParentContainersIfNeeded()} */ public PersistentNode(CuratorFramework givenClient, final CreateMode mode, boolean useProtection, final String basePath, byte[] initData, boolean useParentCreation) { this(givenClient, mode, useProtection, basePath, initData, -1, useParentCreation); } /** * @param givenClient client instance * @param mode creation mode * @param useProtection if true, call {@link CreateBuilder#withProtection()} * @param basePath the base path for the node * @param initData data for the node * @param ttl for ttl modes, the ttl to use * @param useParentCreation if true, call {@link CreateBuilder#creatingParentContainersIfNeeded()} */ public PersistentNode(CuratorFramework givenClient, final CreateMode mode, boolean useProtection, final String basePath, byte[] initData, long ttl, boolean useParentCreation) { this.useProtection = useProtection; this.useParentCreation = useParentCreation; this.client = Preconditions.checkNotNull(givenClient, "client cannot be null").newWatcherRemoveCuratorFramework(); this.basePath = PathUtils.validatePath(basePath); this.mode = Preconditions.checkNotNull(mode, "mode cannot be null"); this.ttl = ttl; final byte[] data = Preconditions.checkNotNull(initData, "data cannot be null"); backgroundCallback = new BackgroundCallback() { @Override public void processResult(CuratorFramework dummy, CuratorEvent event) throws Exception { if ( isActive() ) { processBackgroundCallback(event); } else { processBackgroundCallbackClosedState(event); } } }; this.data.set(Arrays.copyOf(data, data.length)); } private void processBackgroundCallbackClosedState(CuratorEvent event) { String path = null; if ( event.getResultCode() == KeeperException.Code.NODEEXISTS.intValue() ) { path = event.getPath(); } else if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { path = event.getName(); } if ( path != null ) { try { client.delete().guaranteed().inBackground().forPath(path); } catch ( Exception e ) { log.error("Could not delete node after close", e); } } } private void processBackgroundCallback(CuratorEvent event) throws Exception { String path = null; boolean nodeExists = false; if ( event.getResultCode() == KeeperException.Code.NODEEXISTS.intValue() ) { path = event.getPath(); nodeExists = true; } else if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { path = event.getName(); } else if ( event.getResultCode() == KeeperException.Code.NOAUTH.intValue() ) { log.warn("Client does not have authorisation to create node at path {}", event.getPath()); authFailure = true; return; } else if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() ) { log.warn("Client cannot create parent hierarchy for path {} with useParentCreation set to {}", event.getPath(), useParentCreation); parentCreationFailure = true; return; } if ( path != null ) { authFailure = false; nodePath.set(path); watchNode(); if ( nodeExists ) { client.setData().inBackground(setDataCallback).forPath(getActualPath(), getData()); } else { initialisationComplete(); notifyListeners(); } } else { createNode(); } } private void initialisationComplete() { CountDownLatch localLatch = initialCreateLatch.getAndSet(null); if ( localLatch != null ) { localLatch.countDown(); } } /** * You must call start() to initiate the persistent node. An attempt to create the node * in the background will be started */ public void start() { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Already started"); client.getConnectionStateListenable().addListener(connectionStateListener); createNode(); } /** * Block until the either initial node creation initiated by {@link #start()} succeeds or * the timeout elapses. * * @param timeout the maximum time to wait * @param unit time unit * @return if the node was created before timeout * @throws InterruptedException if the thread is interrupted */ public boolean waitForInitialCreate(long timeout, TimeUnit unit) throws InterruptedException { Preconditions.checkState(state.get() == State.STARTED, "Not started"); CountDownLatch localLatch = initialCreateLatch.get(); return (localLatch == null) || localLatch.await(timeout, unit); } @VisibleForTesting final AtomicLong debugWaitMsForBackgroundBeforeClose = new AtomicLong(0); @Override public void close() throws IOException { if ( debugWaitMsForBackgroundBeforeClose.get() > 0 ) { try { Thread.sleep(debugWaitMsForBackgroundBeforeClose.get()); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } } if ( !state.compareAndSet(State.STARTED, State.CLOSED) ) { return; } client.getConnectionStateListenable().removeListener(connectionStateListener); try { deleteNode(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new IOException(e); } client.removeWatchers(); } /** * Returns the listenable * * @return listenable */ public Listenable getListenable() { return listeners; } /** * Returns the currently set path or null if the node does not exist * * @return node path or null */ public String getActualPath() { return nodePath.get(); } /** * Set data that node should set in ZK also writes the data to the node. NOTE: it * is an error to call this method after {@link #start()} but before the initial create * has completed. Use {@link #waitForInitialCreate(long, TimeUnit)} to ensure initial * creation. * * @param data new data value * @throws Exception errors */ public void setData(byte[] data) throws Exception { data = Preconditions.checkNotNull(data, "data cannot be null"); Preconditions.checkState(nodePath.get() != null, "initial create has not been processed. Call waitForInitialCreate() to ensure."); Preconditions.checkState(!parentCreationFailure, "Failed to create parent nodes."); this.data.set(Arrays.copyOf(data, data.length)); if ( isActive() ) { client.setData().inBackground(setDataCallback).forPath(getActualPath(), getData()); } } /** * Return the current value of our data * * @return our data */ public byte[] getData() { return this.data.get(); } protected void deleteNode() throws Exception { String localNodePath = nodePath.getAndSet(null); if ( localNodePath != null ) { try { client.delete().guaranteed().forPath(localNodePath); } catch ( KeeperException.NoNodeException ignore ) { // ignore } } } private void createNode() { if ( !isActive() ) { return; } if ( debugCreateNodeLatch != null ) { try { debugCreateNodeLatch.await(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); return; } } try { String existingPath = nodePath.get(), createPath; if ( existingPath != null && !useProtection ) { createPath = existingPath; } else if ( existingPath != null && mode.isSequential() ) { createPath = basePath + ZKPaths.extractSequentialSuffix(existingPath); } else { createPath = basePath; } CreateModable> localCreateMethod = createMethod.get(); if ( localCreateMethod == null ) { CreateBuilderMain createBuilder = mode.isTTL() ? client.create().withTtl(ttl) : client.create(); CreateModable> tempCreateMethod; if (useParentCreation) { tempCreateMethod = useProtection ? createBuilder.creatingParentContainersIfNeeded().withProtection() : createBuilder.creatingParentContainersIfNeeded(); } else { tempCreateMethod = useProtection ? createBuilder.withProtection() : createBuilder; } createMethod.compareAndSet(null, tempCreateMethod); localCreateMethod = createMethod.get(); } localCreateMethod.withMode(getCreateMode(existingPath != null)).inBackground(backgroundCallback).forPath(createPath, data.get()); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new RuntimeException("Creating node. BasePath: " + basePath, e); // should never happen unless there's a programming error - so throw RuntimeException } } private CreateMode getCreateMode(boolean pathIsSet) { if ( pathIsSet ) { switch ( mode ) { default: { break; } case EPHEMERAL_SEQUENTIAL: { return CreateMode.EPHEMERAL; // protection case - node already set } case PERSISTENT_SEQUENTIAL: { return CreateMode.PERSISTENT; // protection case - node already set } case PERSISTENT_SEQUENTIAL_WITH_TTL: { return CreateMode.PERSISTENT_WITH_TTL; // protection case - node already set } } } return mode; } private void watchNode() throws Exception { if ( !isActive() ) { return; } String localNodePath = nodePath.get(); if ( localNodePath != null ) { client.checkExists().usingWatcher(watcher).inBackground(checkExistsCallback).forPath(localNodePath); } } private void notifyListeners() { final String path = getActualPath(); listeners.forEach(listener -> { try { listener.nodeCreated(path); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("From PersistentNode listener", e); } }); } private boolean isActive() { return (state.get() == State.STARTED); } @VisibleForTesting boolean isAuthFailure() { return authFailure; } @VisibleForTesting boolean isParentCreationFailure() { return parentCreationFailure; } } PersistentNodeListener.java000066400000000000000000000022131442004423600413220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; /** * Listener for changes to a PersistentNode */ public interface PersistentNodeListener { /** * Called on a persistentNode event when node is created * * @param path Path of the znode * @throws Exception errors */ void nodeCreated(String path) throws Exception; } PersistentTtlNode.java000066400000000000000000000231021442004423600403000ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.util.Objects; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** *

* Manages a {@link PersistentNode} that uses {@link CreateMode#CONTAINER}. Asynchronously * it creates or updates a child on the persistent node that is marked with a provided TTL. *

* *

* The effect of this is to have a node that can be watched, etc. The child node serves as * a method of having the parent node deleted if the TTL expires. i.e. if the process * that is running the PersistentTtlNode crashes and the TTL elapses, first the child node * will be deleted due to the TTL expiration and then the parent node will be deleted as it's * a container node with no children. *

* *

* PersistentTtlNode is useful when you need to create a TTL node but don't want to keep * it alive manually by periodically setting data - PersistentTtlNode does that for you. Further * the keep-alive is done in a way that does not generate watch triggers on the parent node. *

*/ public class PersistentTtlNode implements Closeable { public static final String DEFAULT_CHILD_NODE_NAME = "touch"; public static final int DEFAULT_TOUCH_SCHEDULE_FACTOR = 2; public static final boolean DEFAULT_USE_PARENT_CREATION = true; private final Logger log = LoggerFactory.getLogger(getClass()); private final PersistentNode node; private final CuratorFramework client; private final long ttlMs; private final int touchScheduleFactor; private final ScheduledExecutorService executorService; private final AtomicReference> futureRef = new AtomicReference<>(); private final String childPath; /** * @param client the client * @param path path for the parent ZNode * @param ttlMs max ttl for the node in milliseconds * @param initData data for the node */ public PersistentTtlNode(CuratorFramework client, String path, long ttlMs, byte[] initData) { this(client, Executors.newSingleThreadScheduledExecutor(ThreadUtils.newThreadFactory("PersistentTtlNode")), path, ttlMs, initData, DEFAULT_CHILD_NODE_NAME, DEFAULT_TOUCH_SCHEDULE_FACTOR, DEFAULT_USE_PARENT_CREATION); } /** * @param client the client * @param path path for the parent ZNode * @param ttlMs max ttl for the node in milliseconds * @param initData data for the node * @param useParentCreation if true, parent ZNode can be created without ancestors */ public PersistentTtlNode(CuratorFramework client, String path, long ttlMs, byte[] initData, boolean useParentCreation) { this(client, Executors.newSingleThreadScheduledExecutor(ThreadUtils.newThreadFactory("PersistentTtlNode")), path, ttlMs, initData, DEFAULT_CHILD_NODE_NAME, DEFAULT_TOUCH_SCHEDULE_FACTOR, useParentCreation); } /** * @param client the client * @param executorService ExecutorService to use for background thread. This service should be single threaded, otherwise you may see inconsistent results. * @param path path for the parent ZNode * @param ttlMs max ttl for the node in milliseconds * @param initData data for the node * @param childNodeName name to use for the child node of the node created at path * @param touchScheduleFactor how ofter to set/create the child node as a factor of the ttlMs. i.e. * the child is touched every (ttlMs / touchScheduleFactor) */ public PersistentTtlNode(CuratorFramework client, ScheduledExecutorService executorService, String path, long ttlMs, byte[] initData, String childNodeName, int touchScheduleFactor) { this(client, executorService, path, ttlMs, initData, childNodeName, touchScheduleFactor, DEFAULT_USE_PARENT_CREATION); } /** * @param client the client * @param executorService ExecutorService to use for background thread. This service should be single threaded, otherwise you may see inconsistent results. * @param path path for the parent ZNode * @param ttlMs max ttl for the node in milliseconds * @param initData data for the node * @param childNodeName name to use for the child node of the node created at path * @param touchScheduleFactor how ofter to set/create the child node as a factor of the ttlMs. i.e. * the child is touched every (ttlMs / touchScheduleFactor) * @param useParentCreation if true, parent ZNode can be created without ancestors */ public PersistentTtlNode(CuratorFramework client, ScheduledExecutorService executorService, String path, long ttlMs, byte[] initData, String childNodeName, int touchScheduleFactor, boolean useParentCreation) { this.client = Objects.requireNonNull(client, "client cannot be null"); this.ttlMs = ttlMs; this.touchScheduleFactor = touchScheduleFactor; node = new PersistentNode(client, CreateMode.CONTAINER, false, path, initData, useParentCreation) { @Override protected void deleteNode() { // NOP } }; this.executorService = Objects.requireNonNull(executorService, "executorService cannot be null"); childPath = ZKPaths.makePath(Objects.requireNonNull(path, "path cannot be null"), childNodeName); } /** * You must call start() to initiate the persistent ttl node */ public void start() { node.start(); Runnable touchTask = new Runnable() { @Override public void run() { try { try { client.setData().forPath(childPath); } catch ( KeeperException.NoNodeException e ) { client.create().orSetData().withTtl(ttlMs).withMode(CreateMode.PERSISTENT_WITH_TTL).forPath(childPath); } } catch ( KeeperException.NoNodeException ignore ) { // ignore } catch ( Exception e ) { if ( !ThreadUtils.checkInterrupted(e) ) { log.debug("Could not touch child node", e); } } } }; Future future = executorService.scheduleAtFixedRate(touchTask, ttlMs / touchScheduleFactor, ttlMs / touchScheduleFactor, TimeUnit.MILLISECONDS); futureRef.set(future); } /** * Block until the either initial node creation initiated by {@link #start()} succeeds or * the timeout elapses. * * @param timeout the maximum time to wait * @param unit time unit * @return if the node was created before timeout * @throws InterruptedException if the thread is interrupted */ public boolean waitForInitialCreate(long timeout, TimeUnit unit) throws InterruptedException { return node.waitForInitialCreate(timeout, unit); } /** * Set data that node should set in ZK also writes the data to the node. NOTE: it * is an error to call this method after {@link #start()} but before the initial create * has completed. Use {@link #waitForInitialCreate(long, TimeUnit)} to ensure initial * creation. * * @param data new data value * @throws Exception errors */ public void setData(byte[] data) throws Exception { node.setData(data); } /** * Return the current value of our data * * @return our data */ public byte[] getData() { return node.getData(); } /** * Call when you are done with the PersistentTtlNode. Note: the ZNode is not immediately * deleted. However, if no other PersistentTtlNode with the same path is running the node will get deleted * based on the ttl. */ @Override public void close() { Future future = futureRef.getAndSet(null); if ( future != null ) { future.cancel(true); } try { node.close(); } catch ( IOException e ) { throw new RuntimeException(e); } } } queue/000077500000000000000000000000001442004423600340215ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipesBlockingQueueConsumer.java000066400000000000000000000125661442004423600411470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.collect.ImmutableList; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import java.util.Collection; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** * Utility - a queue consumer that provides behavior similar to a {@link BlockingQueue} */ public class BlockingQueueConsumer implements QueueConsumer { private final ConnectionStateListener connectionStateListener; private final BlockingQueue items; /** * Creates with capacity of {@link Integer#MAX_VALUE} * @param connectionStateListener listener for connection state changes */ public BlockingQueueConsumer(ConnectionStateListener connectionStateListener) { this(connectionStateListener, new LinkedBlockingQueue()); } /** * @param capacity max capacity (i.e. puts block if full) * @param connectionStateListener listener for connection state changes */ public BlockingQueueConsumer(ConnectionStateListener connectionStateListener, int capacity) { this(connectionStateListener, new ArrayBlockingQueue(capacity)); } /** * Wrap the given blocking queue * * @param queue queue to use * @param connectionStateListener listener for connection state changes */ public BlockingQueueConsumer(ConnectionStateListener connectionStateListener, BlockingQueue queue) { this.connectionStateListener = connectionStateListener; this.items = queue; } @Override public void consumeMessage(T message) throws Exception { items.add(message); } /** * Return any currently queued items without removing them from the queue * * @return items (can be empty) */ public List getItems() { return ImmutableList.copyOf(items); } /** * Returns the number of currently queue items * * @return currently queue item count or 0 */ public int size() { return items.size(); } /** * Take the next item from the queue, blocking until there is an item available * * @return the item * @throws InterruptedException thread interruption */ public T take() throws InterruptedException { return items.take(); } /** * Take the next item from the queue, waiting up to the specified time for * an available item. If the time elapses, null is returned. * * @param time amount of time to block * @param unit time unit * @return next item or null * @throws InterruptedException thread interruption */ public T take(int time, TimeUnit unit) throws InterruptedException { return items.poll(time, unit); } /** * Removes all available elements from this queue and adds them * to the given collection. This operation may be more * efficient than repeatedly polling this queue. A failure * encountered while attempting to add elements to * collection c may result in elements being in neither, * either or both collections when the associated exception is * thrown. Attempts to drain a queue to itself result in * IllegalArgumentException. Further, the behavior of * this operation is undefined if the specified collection is * modified while the operation is in progress. * * @param c the collection to transfer elements into * @return the number of elements transferred * @throws UnsupportedOperationException if addition of elements * is not supported by the specified collection * @throws ClassCastException if the class of an element of this queue * prevents it from being added to the specified collection * @throws NullPointerException if the specified collection is null * @throws IllegalArgumentException if the specified collection is this * queue, or some property of an element of this queue prevents * it from being added to the specified collection */ public int drainTo(Collection c) { return items.drainTo(c); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { connectionStateListener.stateChanged(client, newState); } } ChildrenCache.java000066400000000000000000000126261442004423600373470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.PathUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import java.io.Closeable; import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; class ChildrenCache implements Closeable { private final WatcherRemoveCuratorFramework client; private final String path; private final AtomicReference children = new AtomicReference(new Data(Lists.newArrayList(), 0)); private final AtomicBoolean isClosed = new AtomicBoolean(false); private final CuratorWatcher watcher = new CuratorWatcher() { @Override public void process(WatchedEvent event) throws Exception { if ( !isClosed.get() ) { sync(); } } }; private final BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { setNewChildren(event.getChildren()); } } }; private final ConnectionStateListener connectionStateListener = (__, newState) -> { if ((newState == ConnectionState.CONNECTED) || (newState == ConnectionState.RECONNECTED)) { try { sync(); } catch ( Exception e ) { throw new RuntimeException(e); } } }; static class Data { final List children; final long version; private Data(List children, long version) { this.children = ImmutableList.copyOf(children); this.version = version; } } ChildrenCache(CuratorFramework client, String path) { this.client = client.newWatcherRemoveCuratorFramework(); this.path = PathUtils.validatePath(path); } void start() throws Exception { client.getConnectionStateListenable().addListener(connectionStateListener); sync(); } @Override public void close() throws IOException { client.removeWatchers(); client.getConnectionStateListenable().removeListener(connectionStateListener); isClosed.set(true); notifyFromCallback(); } Data getData() { return children.get(); } Data blockingNextGetData(long startVersion) throws InterruptedException { return blockingNextGetData(startVersion, 0, null); } synchronized Data blockingNextGetData(long startVersion, long maxWait, TimeUnit unit) throws InterruptedException { long startMs = System.currentTimeMillis(); boolean hasMaxWait = (unit != null); long maxWaitMs = hasMaxWait ? unit.toMillis(maxWait) : -1; while ( startVersion == children.get().version ) { if ( hasMaxWait ) { long elapsedMs = System.currentTimeMillis() - startMs; long thisWaitMs = maxWaitMs - elapsedMs; if ( thisWaitMs <= 0 ) { break; } wait(thisWaitMs); } else { wait(); } } return children.get(); } private synchronized void notifyFromCallback() { notifyAll(); } private synchronized void sync() throws Exception { client.getChildren().usingWatcher(watcher).inBackground(callback).forPath(path); } private synchronized void setNewChildren(List newChildren) { if ( newChildren != null ) { Data currentData = children.get(); children.set(new Data(newChildren, currentData.version + 1)); notifyFromCallback(); } } } DistributedDelayQueue.java000066400000000000000000000205231442004423600411340ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.listen.Listenable; import java.io.Closeable; import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** *

* A variation of the DistributedPriorityQueue that uses time as the priority. When items * are added to the queue, a delay value is given. The item will not be sent to a consumer * until the time elapses. *

*/ public class DistributedDelayQueue implements Closeable, QueueBase { private final DistributedQueue queue; private static final char SEPARATOR = '|'; DistributedDelayQueue ( CuratorFramework client, QueueConsumer consumer, QueueSerializer serializer, String queuePath, ThreadFactory threadFactory, Executor executor, int minItemsBeforeRefresh, String lockPath, int maxItems, boolean putInBackground, int finalFlushMs ) { Preconditions.checkArgument(minItemsBeforeRefresh >= 0, "minItemsBeforeRefresh cannot be negative"); queue = new DistributedQueue ( client, consumer, serializer, queuePath, threadFactory, executor, minItemsBeforeRefresh, true, lockPath, maxItems, putInBackground, finalFlushMs ) { @Override protected long getDelay(String itemNode) { return getDelay(itemNode, System.currentTimeMillis()); } private long getDelay(String itemNode, long sortTime) { long epoch = getEpoch(itemNode); return epoch - sortTime; } @Override protected void sortChildren(List children) { final long sortTime = System.currentTimeMillis(); Collections.sort ( children, new Comparator() { @Override public int compare(String o1, String o2) { long diff = getDelay(o1, sortTime) - getDelay(o2, sortTime); return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0); } } ); } }; } /** * Start the queue. No other methods work until this is called * * @throws Exception startup errors */ @Override public void start() throws Exception { queue.start(); } @Override public void close() throws IOException { queue.close(); } /** * Add an item into the queue. Adding is done in the background - thus, this method will * return quickly.

* NOTE: if an upper bound was set via {@link QueueBuilder#maxItems}, this method will * block until there is available space in the queue. * * @param item item to add * @param delayUntilEpoch future epoch (milliseconds) when this item will be available to consumers * @throws Exception connection issues */ public void put(T item, long delayUntilEpoch) throws Exception { put(item, delayUntilEpoch, 0, null); } /** * Same as {@link #put(Object, long)} but allows a maximum wait time if an upper bound was set * via {@link QueueBuilder#maxItems}. * * @param item item to add * @param delayUntilEpoch future epoch (milliseconds) when this item will be available to consumers * @param maxWait maximum wait * @param unit wait unit * @return true if items was added, false if timed out * @throws Exception */ public boolean put(T item, long delayUntilEpoch, int maxWait, TimeUnit unit) throws Exception { Preconditions.checkArgument(delayUntilEpoch > 0, "delayUntilEpoch cannot be negative"); queue.checkState(); return queue.internalPut(item, null, queue.makeItemPath() + epochToString(delayUntilEpoch), maxWait, unit); } /** * Add a set of items with the same priority into the queue. Adding is done in the background - thus, this method will * return quickly.

* NOTE: if an upper bound was set via {@link QueueBuilder#maxItems}, this method will * block until there is available space in the queue. * * @param items items to add * @param delayUntilEpoch future epoch (milliseconds) when this item will be available to consumers * @throws Exception connection issues */ public void putMulti(MultiItem items, long delayUntilEpoch) throws Exception { putMulti(items, delayUntilEpoch, 0, null); } /** * Same as {@link #putMulti(MultiItem, long)} but allows a maximum wait time if an upper bound was set * via {@link QueueBuilder#maxItems}. * * @param items items to add * @param delayUntilEpoch future epoch (milliseconds) when this item will be available to consumers * @param maxWait maximum wait * @param unit wait unit * @return true if items was added, false if timed out * @throws Exception */ public boolean putMulti(MultiItem items, long delayUntilEpoch, int maxWait, TimeUnit unit) throws Exception { Preconditions.checkArgument(delayUntilEpoch > 0, "delayUntilEpoch cannot be negative"); queue.checkState(); return queue.internalPut(null, items, queue.makeItemPath() + epochToString(delayUntilEpoch), maxWait, unit); } @Override public void setErrorMode(ErrorMode newErrorMode) { queue.setErrorMode(newErrorMode); } @Override public boolean flushPuts(long waitTime, TimeUnit timeUnit) throws InterruptedException { return queue.flushPuts(waitTime, timeUnit); } /** * Return the manager for put listeners * * @return put listener container */ @Override public Listenable> getPutListenerContainer() { return queue.getPutListenerContainer(); } /** * Return the most recent message count from the queue. This is useful for debugging/information * purposes only. * * @return count (can be 0) */ @Override public int getLastMessageCount() { return queue.getLastMessageCount(); } @VisibleForTesting static String epochToString(long epoch) { return SEPARATOR + String.format("%08X", epoch) + SEPARATOR; } private static long getEpoch(String itemNode) { int index2 = itemNode.lastIndexOf(SEPARATOR); int index1 = (index2 > 0) ? itemNode.lastIndexOf(SEPARATOR, index2 - 1) : -1; if ( (index1 > 0) && (index2 > (index1 + 1)) ) { try { String epochStr = itemNode.substring(index1 + 1, index2); return Long.parseLong(epochStr, 16); } catch ( NumberFormatException ignore ) { // ignore } } return 0; } } DistributedIdQueue.java000066400000000000000000000161001442004423600404260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.listen.Listenable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** * A version of {@link DistributedQueue} that allows IDs to be associated with queue items. Items * can then be removed from the queue if needed */ public class DistributedIdQueue implements QueueBase { private final Logger log = LoggerFactory.getLogger(getClass()); private final DistributedQueue queue; private static final char SEPARATOR = '|'; private static class Parts { final String id; final String cleaned; private Parts(String id, String cleaned) { this.id = id; this.cleaned = cleaned; } } DistributedIdQueue ( CuratorFramework client, QueueConsumer consumer, QueueSerializer serializer, String queuePath, ThreadFactory threadFactory, Executor executor, int minItemsBeforeRefresh, boolean refreshOnWatch, String lockPath, int maxItems, boolean putInBackground, int finalFlushMs ) { queue = new DistributedQueue(client, consumer, serializer, queuePath, threadFactory, executor, minItemsBeforeRefresh, refreshOnWatch, lockPath, maxItems, putInBackground, finalFlushMs) { @Override protected void sortChildren(List children) { internalSortChildren(children); } @Override protected String makeRequeueItemPath(String itemPath) { return makeIdPath(parseId(itemPath).id); } }; if ( queue.makeItemPath().contains(Character.toString(SEPARATOR)) ) { throw new IllegalStateException("DistributedQueue can't use " + SEPARATOR); } } @Override public void start() throws Exception { queue.start(); } @Override public void close() throws IOException { queue.close(); } @Override public Listenable> getPutListenerContainer() { return queue.getPutListenerContainer(); } @Override public void setErrorMode(ErrorMode newErrorMode) { queue.setErrorMode(newErrorMode); } @Override public boolean flushPuts(long waitTime, TimeUnit timeUnit) throws InterruptedException { return queue.flushPuts(waitTime, timeUnit); } @Override public int getLastMessageCount() { return queue.getLastMessageCount(); } /** * Put an item into the queue with the given Id

* NOTE: if an upper bound was set via {@link QueueBuilder#maxItems}, this method will * block until there is available space in the queue. * * @param item item * @param itemId item Id * @throws Exception errors */ public void put(T item, String itemId) throws Exception { put(item, itemId, 0, null); } /** * Same as {@link #put(Object, String)} but allows a maximum wait time if an upper bound was set * via {@link QueueBuilder#maxItems}. * * @param item item * @param itemId item Id * @param maxWait maximum wait * @param unit wait unit * @return true if items was added, false if timed out * @throws Exception */ public boolean put(T item, String itemId, int maxWait, TimeUnit unit) throws Exception { Preconditions.checkArgument(isValidId(itemId), "Invalid id: " + itemId); queue.checkState(); return queue.internalPut(item, null, makeIdPath(itemId), maxWait, unit); } /** * Remove any items with the given Id * * @param id item Id to remove * @return number of items removed * @throws Exception errors */ public int remove(String id) throws Exception { id = Preconditions.checkNotNull(id, "id cannot be null"); queue.checkState(); int count = 0; for ( String name : queue.getChildren() ) { if ( parseId(name).id.equals(id) ) { if ( queue.tryRemove(name) ) { ++count; } } } return count; } @VisibleForTesting boolean debugIsQueued(String id) throws Exception { for ( String name : queue.getChildren() ) { if ( parseId(name).id.equals(id) ) { return true; } } return false; } private String makeIdPath(String itemId) { return queue.makeItemPath() + SEPARATOR + fixId(itemId) + SEPARATOR; } private void internalSortChildren(List children) { Collections.sort ( children, new Comparator() { @Override public int compare(String o1, String o2) { return parseId(o1).cleaned.compareTo(parseId(o2).cleaned); } } ); } private boolean isValidId(String id) { return (id != null) && (id.length() > 0); } private static String fixId(String id) { String fixed = id.replace('/', '_'); return fixed.replace(SEPARATOR, '_'); } private Parts parseId(String name) { int firstIndex = name.indexOf(SEPARATOR); int secondIndex = name.indexOf(SEPARATOR, firstIndex + 1); if ( (firstIndex < 0) || (secondIndex < 0) ) { log.error("Bad node in queue: " + name); return new Parts(name, name); } return new Parts ( name.substring(firstIndex + 1, secondIndex), name.substring(0, firstIndex) + name.substring(secondIndex + 1) ); } } DistributedPriorityQueue.java000066400000000000000000000154411442004423600417220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.listen.Listenable; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** *

An implementation of the Distributed Priority Queue ZK recipe.

*

Internally, this uses a {@link DistributedQueue}. The only difference is that you specify a * priority when putting into the queue.

*

IMPORTANT NOTE: The priority queue will perform far worse than a standard queue. Every time an * item is added to/removed from the queue, every watcher must re-get all the nodes

*/ public class DistributedPriorityQueue implements Closeable, QueueBase { private final DistributedQueue queue; DistributedPriorityQueue ( CuratorFramework client, QueueConsumer consumer, QueueSerializer serializer, String queuePath, ThreadFactory threadFactory, Executor executor, int minItemsBeforeRefresh, String lockPath, int maxItems, boolean putInBackground, int finalFlushMs ) { Preconditions.checkArgument(minItemsBeforeRefresh >= 0, "minItemsBeforeRefresh cannot be negative"); queue = new DistributedQueue ( client, consumer, serializer, queuePath, threadFactory, executor, minItemsBeforeRefresh, true, lockPath, maxItems, putInBackground, finalFlushMs ); } /** * Start the queue. No other methods work until this is called * * @throws Exception startup errors */ @Override public void start() throws Exception { queue.start(); } @Override public void close() throws IOException { queue.close(); } /** * Add an item into the queue. Adding is done in the background - thus, this method will * return quickly.

* NOTE: if an upper bound was set via {@link QueueBuilder#maxItems}, this method will * block until there is available space in the queue. * * @param item item to add * @param priority item's priority - lower numbers come out of the queue first * @throws Exception connection issues */ public void put(T item, int priority) throws Exception { put(item, priority, 0, null); } /** * Same as {@link #put(Object, int)} but allows a maximum wait time if an upper bound was set * via {@link QueueBuilder#maxItems}. * * @param item item to add * @param priority item's priority - lower numbers come out of the queue first * @param maxWait maximum wait * @param unit wait unit * @return true if items was added, false if timed out * @throws Exception */ public boolean put(T item, int priority, int maxWait, TimeUnit unit) throws Exception { queue.checkState(); String priorityHex = priorityToString(priority); return queue.internalPut(item, null, queue.makeItemPath() + priorityHex, maxWait, unit); } /** * Add a set of items with the same priority into the queue. Adding is done in the background - thus, this method will * return quickly.

* NOTE: if an upper bound was set via {@link QueueBuilder#maxItems}, this method will * block until there is available space in the queue. * * @param items items to add * @param priority item priority - lower numbers come out of the queue first * @throws Exception connection issues */ public void putMulti(MultiItem items, int priority) throws Exception { putMulti(items, priority, 0, null); } /** * Same as {@link #putMulti(MultiItem, int)} but allows a maximum wait time if an upper bound was set * via {@link QueueBuilder#maxItems}. * * @param items items to add * @param priority item priority - lower numbers come out of the queue first * @param maxWait maximum wait * @param unit wait unit * @return true if items was added, false if timed out * @throws Exception */ public boolean putMulti(MultiItem items, int priority, int maxWait, TimeUnit unit) throws Exception { queue.checkState(); String priorityHex = priorityToString(priority); return queue.internalPut(null, items, queue.makeItemPath() + priorityHex, maxWait, unit); } @Override public void setErrorMode(ErrorMode newErrorMode) { queue.setErrorMode(newErrorMode); } @Override public boolean flushPuts(long waitTime, TimeUnit timeUnit) throws InterruptedException { return queue.flushPuts(waitTime, timeUnit); } /** * Return the manager for put listeners * * @return put listener container */ @Override public Listenable> getPutListenerContainer() { return queue.getPutListenerContainer(); } /** * Return the most recent message count from the queue. This is useful for debugging/information * purposes only. * * @return count (can be 0) */ @Override public int getLastMessageCount() { return queue.getLastMessageCount(); } @VisibleForTesting ChildrenCache getCache() { return queue.getCache(); } @VisibleForTesting static String priorityToString(int priority) { // the padded hex val of the number prefixed with a 0 for negative numbers // and a 1 for positive (so that it sorts correctly) long l = (long)priority & 0xFFFFFFFFL; return String.format("%s%08X", (priority >= 0) ? "1" : "0", l); } } DistributedQueue.java000066400000000000000000000613731442004423600401650ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.recipes.leader.LeaderSelector; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; /** *

An implementation of the Distributed Queue ZK recipe. Items put into the queue * are guaranteed to be ordered (by means of ZK's PERSISTENT_SEQUENTIAL node).

* *

* Guarantees:

*
    *
  • If a single consumer takes items out of the queue, they will be ordered FIFO. i.e. if ordering is important, * use a {@link LeaderSelector} to nominate a single consumer.
  • *
  • Unless a {@link QueueBuilder#lockPath(String)} is used, there is only guaranteed processing of each message to the point of receipt by a given instance. *
  • If an instance receives an item from the queue but dies while processing it, the item will be lost. If you need message recoverability, use * a {@link QueueBuilder#lockPath(String)}
  • *
*/ public class DistributedQueue implements QueueBase { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFramework client; private final QueueSerializer serializer; private final String queuePath; private final Executor executor; private final ExecutorService service; private final AtomicReference state = new AtomicReference(State.LATENT); private final QueueConsumer consumer; private final int minItemsBeforeRefresh; private final boolean refreshOnWatch; private final boolean isProducerOnly; private final String lockPath; private final AtomicReference errorMode = new AtomicReference(ErrorMode.REQUEUE); private final StandardListenerManager> putListenerContainer = StandardListenerManager.standard(); private final AtomicInteger lastChildCount = new AtomicInteger(0); private final int maxItems; private final int finalFlushMs; private final boolean putInBackground; private final ChildrenCache childrenCache; private final AtomicInteger putCount = new AtomicInteger(0); private enum State { LATENT, STARTED, STOPPED } @VisibleForTesting protected enum ProcessType { NORMAL, REMOVE } private static final String QUEUE_ITEM_NAME = "queue-"; DistributedQueue ( CuratorFramework client, QueueConsumer consumer, QueueSerializer serializer, String queuePath, ThreadFactory threadFactory, Executor executor, int minItemsBeforeRefresh, boolean refreshOnWatch, String lockPath, int maxItems, boolean putInBackground, int finalFlushMs ) { Preconditions.checkNotNull(client, "client cannot be null"); Preconditions.checkNotNull(serializer, "serializer cannot be null"); Preconditions.checkNotNull(threadFactory, "threadFactory cannot be null"); Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkArgument(maxItems > 0, "maxItems must be a positive number"); isProducerOnly = (consumer == null); this.lockPath = (lockPath == null) ? null : PathUtils.validatePath(lockPath); this.putInBackground = putInBackground; this.consumer = consumer; this.minItemsBeforeRefresh = minItemsBeforeRefresh; this.refreshOnWatch = refreshOnWatch; this.client = client; this.serializer = serializer; this.queuePath = PathUtils.validatePath(queuePath); this.executor = executor; this.maxItems = maxItems; this.finalFlushMs = finalFlushMs; service = Executors.newFixedThreadPool(2, threadFactory); childrenCache = new ChildrenCache(client, queuePath); if ( (maxItems != QueueBuilder.NOT_SET) && putInBackground ) { log.warn("Bounded queues should set putInBackground(false) in the builder. Putting in the background will result in spotty maxItem consistency."); } } /** * Start the queue. No other methods work until this is called * * @throws Exception startup errors */ @Override public void start() throws Exception { if ( !state.compareAndSet(State.LATENT, State.STARTED) ) { throw new IllegalStateException(); } try { client.create().creatingParentContainersIfNeeded().forPath(queuePath); } catch ( KeeperException.NodeExistsException ignore ) { // this is OK } if ( lockPath != null ) { try { client.create().creatingParentContainersIfNeeded().forPath(lockPath); } catch ( KeeperException.NodeExistsException ignore ) { // this is OK } } if ( !isProducerOnly || (maxItems != QueueBuilder.NOT_SET) ) { childrenCache.start(); } if ( !isProducerOnly ) { service.submit ( new Callable() { @Override public Object call() { runLoop(); return null; } } ); } } @Override public void close() throws IOException { if ( state.compareAndSet(State.STARTED, State.STOPPED) ) { if ( finalFlushMs > 0 ) { try { flushPuts(finalFlushMs, TimeUnit.MILLISECONDS); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } } CloseableUtils.closeQuietly(childrenCache); putListenerContainer.clear(); service.shutdownNow(); } } /** * Return the manager for put listeners * * @return put listener container */ @Override public Listenable> getPutListenerContainer() { return putListenerContainer; } /** * Used when the queue is created with a {@link QueueBuilder#lockPath(String)}. Determines * the behavior when the queue consumer throws an exception * * @param newErrorMode the new error mode (the default is {@link ErrorMode#REQUEUE} */ @Override public void setErrorMode(ErrorMode newErrorMode) { Preconditions.checkNotNull(lockPath, "lockPath cannot be null"); if ( newErrorMode == ErrorMode.REQUEUE ) { log.warn("ErrorMode.REQUEUE requires ZooKeeper version 3.4.x+ - make sure you are not using a prior version"); } errorMode.set(newErrorMode); } /** * Wait until any pending puts are committed * * @param waitTime max wait time * @param timeUnit time unit * @return true if the flush was successful, false if it timed out first * @throws InterruptedException if thread was interrupted */ @Override public boolean flushPuts(long waitTime, TimeUnit timeUnit) throws InterruptedException { long msWaitRemaining = TimeUnit.MILLISECONDS.convert(waitTime, timeUnit); synchronized(putCount) { while ( putCount.get() > 0 ) { if ( msWaitRemaining <= 0 ) { return false; } long startMs = System.currentTimeMillis(); putCount.wait(msWaitRemaining); long elapsedMs = System.currentTimeMillis() - startMs; msWaitRemaining -= elapsedMs; } } return true; } /** * Add an item into the queue. Adding is done in the background - thus, this method will * return quickly.

* NOTE: if an upper bound was set via {@link QueueBuilder#maxItems}, this method will * block until there is available space in the queue. * * @param item item to add * @throws Exception connection issues */ public void put(T item) throws Exception { put(item, 0, null); } /** * Same as {@link #put(Object)} but allows a maximum wait time if an upper bound was set * via {@link QueueBuilder#maxItems}. * * @param item item to add * @param maxWait maximum wait * @param unit wait unit * @return true if items was added, false if timed out * @throws Exception */ public boolean put(T item, int maxWait, TimeUnit unit) throws Exception { checkState(); String path = makeItemPath(); return internalPut(item, null, path, maxWait, unit); } /** * Add a set of items into the queue. Adding is done in the background - thus, this method will * return quickly.

* NOTE: if an upper bound was set via {@link QueueBuilder#maxItems}, this method will * block until there is available space in the queue. * * @param items items to add * @throws Exception connection issues */ public void putMulti(MultiItem items) throws Exception { putMulti(items, 0, null); } /** * Same as {@link #putMulti(MultiItem)} but allows a maximum wait time if an upper bound was set * via {@link QueueBuilder#maxItems}. * * @param items items to add * @param maxWait maximum wait * @param unit wait unit * @return true if items was added, false if timed out * @throws Exception */ public boolean putMulti(MultiItem items, int maxWait, TimeUnit unit) throws Exception { checkState(); String path = makeItemPath(); return internalPut(null, items, path, maxWait, unit); } /** * Return the most recent message count from the queue. This is useful for debugging/information * purposes only. * * @return count (can be 0) */ @Override public int getLastMessageCount() { return lastChildCount.get(); } boolean internalPut(final T item, MultiItem multiItem, String path, int maxWait, TimeUnit unit) throws Exception { if ( !blockIfMaxed(maxWait, unit) ) { return false; } final MultiItem givenMultiItem = multiItem; if ( item != null ) { final AtomicReference ref = new AtomicReference(item); multiItem = new MultiItem() { @Override public T nextItem() throws Exception { return ref.getAndSet(null); } }; } putCount.incrementAndGet(); byte[] bytes = ItemSerializer.serialize(multiItem, serializer); if ( putInBackground ) { doPutInBackground(item, path, givenMultiItem, bytes); } else { doPutInForeground(item, path, givenMultiItem, bytes); } return true; } private void doPutInForeground(final T item, String path, final MultiItem givenMultiItem, byte[] bytes) throws Exception { client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(path, bytes); synchronized(putCount) { putCount.decrementAndGet(); putCount.notifyAll(); } putListenerContainer.forEach(listener -> { if ( item != null ) { listener.putCompleted(item); } else { listener.putMultiCompleted(givenMultiItem); } }); } private void doPutInBackground(final T item, String path, final MultiItem givenMultiItem, byte[] bytes) throws Exception { BackgroundCallback callback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if ( event.getResultCode() != KeeperException.Code.OK.intValue() ) { return; } if ( event.getType() == CuratorEventType.CREATE ) { synchronized(putCount) { putCount.decrementAndGet(); putCount.notifyAll(); } } putListenerContainer.forEach(listener -> { if ( item != null ) { listener.putCompleted(item); } else { listener.putMultiCompleted(givenMultiItem); } }); } }; internalCreateNode(path, bytes, callback); } @VisibleForTesting void internalCreateNode(String path, byte[] bytes, BackgroundCallback callback) throws Exception { client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).inBackground(callback).forPath(path, bytes); } void checkState() throws Exception { if ( state.get() != State.STARTED ) { throw new IllegalStateException(); } } String makeItemPath() { return ZKPaths.makePath(queuePath, QUEUE_ITEM_NAME); } @VisibleForTesting ChildrenCache getCache() { return childrenCache; } protected void sortChildren(List children) { Collections.sort(children); } protected List getChildren() throws Exception { return client.getChildren().forPath(queuePath); } protected long getDelay(String itemNode) { return 0; } protected boolean tryRemove(String itemNode) throws Exception { boolean isUsingLockSafety = (lockPath != null); if ( isUsingLockSafety ) { return processWithLockSafety(itemNode, ProcessType.REMOVE); } return processNormally(itemNode, ProcessType.REMOVE); } private boolean blockIfMaxed(int maxWait, TimeUnit unit) throws Exception { ChildrenCache.Data data = childrenCache.getData(); while ( data.children.size() >= maxItems ) { long previousVersion = data.version; data = childrenCache.blockingNextGetData(data.version, maxWait, unit); if ( data.version == previousVersion ) { return false; } } return true; } private void runLoop() { long currentVersion = -1; long maxWaitMs = -1; try { while ( state.get() == State.STARTED ) { try { ChildrenCache.Data data = (maxWaitMs > 0) ? childrenCache.blockingNextGetData(currentVersion, maxWaitMs, TimeUnit.MILLISECONDS) : childrenCache.blockingNextGetData(currentVersion); currentVersion = data.version; List children = Lists.newArrayList(data.children); sortChildren(children); // makes sure items are processed in the correct order if ( children.size() > 0 ) { maxWaitMs = getDelay(children.get(0)); if ( maxWaitMs > 0 ) { continue; } } else { continue; } processChildren(children, currentVersion); } catch ( InterruptedException e ) { // swallow the interrupt as it's only possible from either a background // operation and, thus, doesn't apply to this loop or the instance // is being closed in which case the while test will get it } } } catch ( Exception e ) { log.error("Exception caught in background handler", e); } } private void processChildren(List children, long currentVersion) throws Exception { final Semaphore processedLatch = new Semaphore(0); final boolean isUsingLockSafety = (lockPath != null); int min = minItemsBeforeRefresh; for ( final String itemNode : children ) { if ( Thread.currentThread().isInterrupted() ) { processedLatch.release(children.size()); break; } if ( !itemNode.startsWith(QUEUE_ITEM_NAME) ) { log.warn("Foreign node in queue path: " + itemNode); processedLatch.release(); continue; } if ( min-- <= 0 ) { if ( refreshOnWatch && (currentVersion != childrenCache.getData().version) ) { processedLatch.release(children.size()); break; } } if ( getDelay(itemNode) > 0 ) { processedLatch.release(); continue; } executor.execute ( new Runnable() { @Override public void run() { try { if ( isUsingLockSafety ) { processWithLockSafety(itemNode, ProcessType.NORMAL); } else { processNormally(itemNode, ProcessType.NORMAL); } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Error processing message at " + itemNode, e); } finally { processedLatch.release(); } } } ); } processedLatch.acquire(children.size()); } private enum ProcessMessageBytesCode { NORMAL, REQUEUE } private ProcessMessageBytesCode processMessageBytes(String itemNode, byte[] bytes) throws Exception { ProcessMessageBytesCode resultCode = ProcessMessageBytesCode.NORMAL; MultiItem items; try { items = ItemSerializer.deserialize(bytes, serializer); } catch ( Throwable e ) { ThreadUtils.checkInterrupted(e); log.error("Corrupted queue item: " + itemNode, e); return resultCode; } for(;;) { T item = items.nextItem(); if ( item == null ) { break; } try { consumer.consumeMessage(item); } catch ( Throwable e ) { ThreadUtils.checkInterrupted(e); log.error("Exception processing queue item: " + itemNode, e); if ( errorMode.get() == ErrorMode.REQUEUE ) { resultCode = ProcessMessageBytesCode.REQUEUE; break; } } } return resultCode; } private boolean processNormally(String itemNode, ProcessType type) throws Exception { try { String itemPath = ZKPaths.makePath(queuePath, itemNode); Stat stat = new Stat(); byte[] bytes = null; if ( type == ProcessType.NORMAL ) { bytes = client.getData().storingStatIn(stat).forPath(itemPath); } if ( client.getState() == CuratorFrameworkState.STARTED ) { client.delete().withVersion(stat.getVersion()).forPath(itemPath); } if ( type == ProcessType.NORMAL ) { processMessageBytes(itemNode, bytes); } return true; } catch ( KeeperException.NodeExistsException ignore ) { // another process got it } catch ( KeeperException.NoNodeException ignore ) { // another process got it } catch ( KeeperException.BadVersionException ignore ) { // another process got it } return false; } @VisibleForTesting protected boolean processWithLockSafety(String itemNode, ProcessType type) throws Exception { String lockNodePath = ZKPaths.makePath(lockPath, itemNode); boolean lockCreated = false; try { client.create().withMode(CreateMode.EPHEMERAL).forPath(lockNodePath); lockCreated = true; String itemPath = ZKPaths.makePath(queuePath, itemNode); boolean requeue = false; byte[] bytes = null; if ( type == ProcessType.NORMAL ) { bytes = client.getData().forPath(itemPath); requeue = (processMessageBytes(itemNode, bytes) == ProcessMessageBytesCode.REQUEUE); } if ( requeue ) { client.inTransaction() .delete().forPath(itemPath) .and() .create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(makeRequeueItemPath(itemPath), bytes) .and() .commit(); } else { client.delete().forPath(itemPath); } return true; } catch ( KeeperException.NodeExistsException ignore ) { // another process got it } catch ( KeeperException.NoNodeException ignore ) { // another process got it } catch ( KeeperException.BadVersionException ignore ) { // another process got it } finally { if ( lockCreated ) { client.delete().guaranteed().forPath(lockNodePath); } } return false; } protected String makeRequeueItemPath(String itemPath) { return makeItemPath(); } } ErrorMode.java000066400000000000000000000023451442004423600365660ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; /** * Used when the queue is created with a {@link QueueBuilder#lockPath(String)}. Determines * the behavior when the queue consumer throws an exception */ public enum ErrorMode { /** * If the consumer throws an exception, requeue the message. This is the default. */ REQUEUE, /** * If the consumer throws an exception, delete the message */ DELETE } ItemSerializer.java000066400000000000000000000072011442004423600376140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.collect.Lists; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Iterator; import java.util.List; class ItemSerializer { private static final int VERSION = 0x00010001; private static final byte ITEM_OPCODE = 0x01; private static final byte EOF_OPCODE = 0x02; private static final int INITIAL_BUFFER_SIZE = 0x1000; static MultiItem deserialize(byte[] bytes, QueueSerializer serializer) throws Exception { DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes)); int version = in.readInt(); if ( version != VERSION ) { throw new IOException(String.format("Incorrect version. Expected %d - Found: %d", VERSION, version)); } List items = Lists.newArrayList(); for(;;) { byte opcode = in.readByte(); if ( opcode == EOF_OPCODE ) { break; } if ( opcode != ITEM_OPCODE ) { throw new IOException(String.format("Incorrect opcode. Expected %d - Found: %d", ITEM_OPCODE, opcode)); } int size = in.readInt(); if ( size < 0 ) { throw new IOException(String.format("Bad size: %d", size)); } byte[] itemBytes = new byte[size]; if ( size > 0 ) { in.readFully(itemBytes); } items.add(serializer.deserialize(itemBytes)); } final Iterator iterator = items.iterator(); return new MultiItem() { @Override public T nextItem() { return iterator.hasNext() ? iterator.next() : null; } }; } static byte[] serialize(MultiItem items, QueueSerializer serializer) throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE); DataOutputStream out = new DataOutputStream(bytes); out.writeInt(VERSION); for(;;) { T item = items.nextItem(); if ( item == null ) { break; } byte[] itemBytes = serializer.serialize(item); out.writeByte(ITEM_OPCODE); out.writeInt(itemBytes.length); if ( itemBytes.length > 0 ) { out.write(itemBytes); } } out.writeByte(EOF_OPCODE); out.close(); return bytes.toByteArray(); } private ItemSerializer() { } } MultiItem.java000066400000000000000000000024551442004423600366030ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; /** * Abstraction for multiple items. * @see DistributedQueue#putMulti(MultiItem) * @see DistributedPriorityQueue#putMulti(MultiItem, int) */ public interface MultiItem { /** * Called repeatedly to get the items to add to the queue. This method * should return null when there are no more items to add. * * @return item or null * @throws Exception any errors */ public T nextItem() throws Exception; } QueueAllocator.java000066400000000000000000000020361442004423600376120ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import org.apache.curator.framework.CuratorFramework; public interface QueueAllocator> { public T allocateQueue(CuratorFramework client, String queuePath); } QueueBase.java000066400000000000000000000043371442004423600365520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import org.apache.curator.framework.listen.Listenable; import java.io.Closeable; import java.util.concurrent.TimeUnit; public interface QueueBase extends Closeable { /** * Start the queue. No other methods work until this is called * * @throws Exception startup errors */ void start() throws Exception; /** * Return the manager for put listeners * * @return put listener container */ Listenable> getPutListenerContainer(); /** * Used when the queue is created with a {@link QueueBuilder#lockPath(String)}. Determines * the behavior when the queue consumer throws an exception * * @param newErrorMode the new error mode (the default is {@link ErrorMode#REQUEUE} */ void setErrorMode(ErrorMode newErrorMode); /** * Wait until any pending puts are committed * * @param waitTime max wait time * @param timeUnit time unit * @return true if the flush was successful, false if it timed out first * @throws InterruptedException if thread was interrupted */ boolean flushPuts(long waitTime, TimeUnit timeUnit) throws InterruptedException; /** * Return the most recent message count from the queue. This is useful for debugging/information * purposes only. * * @return count (can be 0) */ int getLastMessageCount(); } QueueBuilder.java000066400000000000000000000221051442004423600372570ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.ThreadUtils; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.apache.curator.utils.PathUtils; /** * The builder for both {@link DistributedQueue} and {@link DistributedPriorityQueue} */ public class QueueBuilder { private final CuratorFramework client; private final QueueConsumer consumer; private final QueueSerializer serializer; private final String queuePath; private ThreadFactory factory; private Executor executor; private String lockPath; private int maxItems = NOT_SET; private boolean putInBackground = true; private int finalFlushMs = 5000; static final ThreadFactory defaultThreadFactory = ThreadUtils.newThreadFactory("QueueBuilder"); static final int NOT_SET = Integer.MAX_VALUE; /** * Allocate a new builder * * * @param client the curator client * @param consumer functor to consume messages - NOTE: pass null to make this a producer-only queue * @param serializer serializer to use for items * @param queuePath path to store queue * @return builder */ public static QueueBuilder builder(CuratorFramework client, QueueConsumer consumer, QueueSerializer serializer, String queuePath) { return new QueueBuilder(client, consumer, serializer, queuePath); } /** * Build a {@link DistributedQueue} from the current builder values * * @return distributed queue */ public DistributedQueue buildQueue() { return new DistributedQueue ( client, consumer, serializer, queuePath, factory, executor, Integer.MAX_VALUE, false, lockPath, maxItems, putInBackground, finalFlushMs ); } /** * Build a {@link DistributedIdQueue} from the current builder values * * @return distributed id queue */ public DistributedIdQueue buildIdQueue() { return new DistributedIdQueue ( client, consumer, serializer, queuePath, factory, executor, Integer.MAX_VALUE, false, lockPath, maxItems, putInBackground, finalFlushMs ); } /** *

Build a {@link DistributedPriorityQueue} from the current builder values.

* *

When the priority * queue detects an item addition/removal, it will stop processing its current list of items and * refresh the list. minItemsBeforeRefresh modifies this. It determines the minimum * number of items from the active list that will get processed before a refresh.

* *

Due to a quirk in the way ZooKeeper notifies changes, the queue will get an item addition/remove * notification after every item is processed. This can lead to poor performance. Set * minItemsBeforeRefresh to the value your application can tolerate being out of sync.

* *

For example: if the queue sees 10 items to process, it will end up making 10 calls to ZooKeeper * to check status. You can control this by setting minItemsBeforeRefresh to 10 (or more) * and the queue will only refresh with ZooKeeper after 10 items are processed

* * @param minItemsBeforeRefresh minimum items to process before refreshing the item list * @return distributed priority queue */ public DistributedPriorityQueue buildPriorityQueue(int minItemsBeforeRefresh) { return new DistributedPriorityQueue ( client, consumer, serializer, queuePath, factory, executor, minItemsBeforeRefresh, lockPath, maxItems, putInBackground, finalFlushMs ); } /** *

Build a {@link DistributedDelayQueue} from the current builder values.

* * @return distributed delay queue */ public DistributedDelayQueue buildDelayQueue() { return new DistributedDelayQueue ( client, consumer, serializer, queuePath, factory, executor, Integer.MAX_VALUE, lockPath, maxItems, putInBackground, finalFlushMs ); } /** * Change the thread factory used. The default is {@link Executors#defaultThreadFactory()} * * @param factory new thread factory to use * @return this */ public QueueBuilder threadFactory(ThreadFactory factory) { Preconditions.checkNotNull(factory, "factory cannot be null"); this.factory = factory; return this; } /** * Change the executor used. The default is {@link MoreExecutors#directExectutor()} * * @param executor new executor to use * @return this */ public QueueBuilder executor(Executor executor) { Preconditions.checkNotNull(executor, "executor cannot be null"); this.executor = executor; return this; } /** *

Without a lock set, queue items are removed before being sent to the queue consumer. This can result in message * loss if the consumer fails to complete the message or the process dies.

* *

Use a lock to make the message recoverable. A lock is held while * the message is being processed - this prevents other processes from taking the message. The message will not be removed * from the queue until the consumer functor returns. Thus, if there is a failure or the process dies, * the message will get sent to another process. There is a small performance penalty for this behavior however. * * @param path path for the lock * @return this */ public QueueBuilder lockPath(String path) { lockPath = PathUtils.validatePath(path); return this; } /** * By default, the various queues are unbounded. This method allows setting a max number of items * to have in the queue. With this value set, the various put methods will block when the * number of items in the queue approaches maxItems. NOTE: maxItems cannot * be exactly achieved. The only guarantee is that approximately maxItems will cause * puts to block. * * @param maxItems the upper bound for the queue * @return this */ public QueueBuilder maxItems(int maxItems) { this.maxItems = maxItems; putInBackground = false; return this; } /** * By default, messages are added in the background. However, this can flood the background thread. * * @param putInBackground true to put in the background (default). false to put in the foreground. * @return this */ public QueueBuilder putInBackground(boolean putInBackground) { this.putInBackground = putInBackground; return this; } /** * Sets an amount of time to call {@link DistributedQueue#flushPuts(long, TimeUnit)} when the * queue is closed. The default is 5 seconds. Pass 0 to turn flushing on close off. * * @param time time * @param unit the unit * @return this */ public QueueBuilder finalFlushTime(int time, TimeUnit unit) { finalFlushMs = (int)unit.toMillis(time); return this; } private QueueBuilder(CuratorFramework client, QueueConsumer consumer, QueueSerializer serializer, String queuePath) { this.client = client; this.consumer = consumer; this.serializer = serializer; this.queuePath = PathUtils.validatePath(queuePath); factory = defaultThreadFactory; executor = MoreExecutors.directExecutor(); } } QueueConsumer.java000066400000000000000000000023241442004423600374650ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import org.apache.curator.framework.state.ConnectionStateListener; /** * Message Consumer */ public interface QueueConsumer extends ConnectionStateListener { /** * Process a message from the queue * * @param message message to process * @throws Exception any errors */ public void consumeMessage(T message) throws Exception; } QueuePutListener.java000066400000000000000000000024731442004423600401550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; /** * Queue puts are done in the background. Use this listener to * be notified when the put completes */ public interface QueuePutListener { /** * Notification that a single item put has completed * * @param item the item */ public void putCompleted(T item); /** * Notification that a multi item put has completed * * @param items the items */ public void putMultiCompleted(MultiItem items); } QueueSafety.java000066400000000000000000000036241442004423600371310ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import java.util.concurrent.BlockingQueue; import org.apache.curator.utils.PathUtils; /** * Parameter block for specifying queue safety with either {@link DistributedQueue} or * {@link DistributedPriorityQueue} */ public class QueueSafety { private final String lockPath; private final QueueConsumer consumer; private final BlockingQueue queue; /** * @param lockPath ZKPath to use for locking purposes * @param consumer the message consumer */ public QueueSafety(String lockPath, QueueConsumer consumer) { this.lockPath = PathUtils.validatePath(lockPath); this.consumer = consumer; this.queue = null; } QueueSafety(String lockPath, BlockingQueue queue) { this.lockPath = PathUtils.validatePath(lockPath); this.consumer = null; this.queue = queue; } String getLockPath() { return lockPath; } QueueConsumer getConsumer() { return consumer; } BlockingQueue getQueue() { return queue; } } QueueSerializer.java000066400000000000000000000024241442004423600400040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; /** * Helper to serialize/deserialize queue items */ public interface QueueSerializer { /** * Turn a queue item into bytes * * @param item the item * @return byte representation */ public byte[] serialize(T item); /** * Deserialize bytes into a queue item * * @param bytes byte representation * @return item */ public T deserialize(byte[] bytes); } QueueSharder.java000066400000000000000000000233361442004423600372700ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.leader.LeaderLatch; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicReference; /** *

* A utility for shard a distributed queue. *

* *

* Due to limitations in ZooKeeper's transport layer, * a single queue will break if it has more than 10K-ish items in it. This class * provides a facade over multiple distributed queues. It monitors the queues and if * any one of them goes over a threshold, a new queue is added. Puts are distributed * amongst the queues. *

* *

* NOTE: item ordering is maintained within each managed queue but cannot be maintained across * queues. i.e. items might get consumed out of order if they are in different managed * queues. *

*/ public class QueueSharder> implements Closeable { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFramework client; private final QueueAllocator queueAllocator; private final String queuePath; private final QueueSharderPolicies policies; private final ConcurrentMap queues = Maps.newConcurrentMap(); private final Set preferredQueues = Sets.newSetFromMap(Maps.newConcurrentMap()); private final AtomicReference state = new AtomicReference(State.LATENT); private final LeaderLatch leaderLatch; private final ExecutorService service; private static final String QUEUE_PREFIX = "queue-"; private enum State { LATENT, STARTED, CLOSED } /** * @param client client * @param queueAllocator allocator for new queues * @param queuePath path for the queues * @param leaderPath path for the leader that monitors queue sizes (must be different than queuePath) * @param policies sharding policies */ public QueueSharder(CuratorFramework client, QueueAllocator queueAllocator, String queuePath, String leaderPath, QueueSharderPolicies policies) { this.client = client; this.queueAllocator = queueAllocator; this.queuePath = queuePath; this.policies = policies; leaderLatch = new LeaderLatch(client, leaderPath); service = Executors.newSingleThreadExecutor(policies.getThreadFactory()); } /** * The sharder must be started * * @throws Exception errors */ public void start() throws Exception { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once"); client.createContainers(queuePath); getInitialQueues(); leaderLatch.start(); service.submit ( new Callable() { @Override public Void call() throws Exception { while ( state.get() == State.STARTED ) { try { Thread.sleep(policies.getThresholdCheckMs()); checkThreshold(); } catch ( InterruptedException e ) { // swallow the interrupt as it's only possible from either a background // operation and, thus, doesn't apply to this loop or the instance // is being closed in which case the while test will get it } } return null; } } ); } @Override public void close() { if ( state.compareAndSet(State.STARTED, State.CLOSED) ) { service.shutdownNow(); CloseableUtils.closeQuietly(leaderLatch); for ( T queue : queues.values() ) { try { queue.close(); } catch ( IOException e ) { log.error("Closing a queue", e); } } } } /** * Return one of the managed queues - the selection method cannot be relied on. It should * be considered a random managed queue. * * @return a queue */ public T getQueue() { Preconditions.checkState(state.get() == State.STARTED, "Not started"); List localPreferredQueues = Lists.newArrayList(preferredQueues); if ( localPreferredQueues.size() > 0 ) { String key = localPreferredQueues.get( ThreadLocalRandom.current().nextInt(localPreferredQueues.size())); return queues.get(key); } List keys = Lists.newArrayList(queues.keySet()); String key = keys.get(ThreadLocalRandom.current().nextInt(keys.size())); return queues.get(key); } /** * Return the current number of mananged queues * * @return qty */ public int getShardQty() { return queues.size(); } /** * Return the current set of shard paths * * @return paths */ public Collection getQueuePaths() { return ImmutableSet.copyOf(queues.keySet()); } private void getInitialQueues() throws Exception { List children = client.getChildren().forPath(queuePath); for ( String child : children ) { String queuePath = ZKPaths.makePath(this.queuePath, child); addNewQueueIfNeeded(queuePath); } if ( children.size() == 0 ) { addNewQueueIfNeeded(null); } } private void addNewQueueIfNeeded(String newQueuePath) throws Exception { if ( newQueuePath == null ) { newQueuePath = ZKPaths.makePath(queuePath, QUEUE_PREFIX + UUID.randomUUID().toString()); } if ( !queues.containsKey(newQueuePath) ) { T queue = queueAllocator.allocateQueue(client, newQueuePath); if ( queues.putIfAbsent(newQueuePath, queue) == null ) { queue.start(); preferredQueues.add(newQueuePath); } } } private void checkThreshold() { try { boolean addAQueueIfLeader = false; int size = 0; List children = client.getChildren().forPath(queuePath); for ( String child : children ) { String queuePath = ZKPaths.makePath(this.queuePath, child); addNewQueueIfNeeded(queuePath); Stat stat = client.checkExists().forPath(queuePath); if ( stat.getNumChildren() >= policies.getNewQueueThreshold() ) { size = stat.getNumChildren(); addAQueueIfLeader = true; preferredQueues.remove(queuePath); } else if ( stat.getNumChildren() <= (policies.getNewQueueThreshold() / 2) ) { preferredQueues.add(queuePath); } } if ( addAQueueIfLeader && leaderLatch.hasLeadership() ) { if ( queues.size() < policies.getMaxQueues() ) { log.info(String.format("Adding a queue due to exceeded threshold. Queue Size: %d - Threshold: %d", size, policies.getNewQueueThreshold())); addNewQueueIfNeeded(null); } else { log.warn(String.format("Max number of queues (%d) reached. Consider increasing the max.", policies.getMaxQueues())); } } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Checking queue counts against threshold", e); } } } QueueSharderPolicies.java000066400000000000000000000110231442004423600407460ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import com.google.common.base.Preconditions; import java.util.concurrent.ThreadFactory; /** * Various policies/options for sharding. Usage: * QueueSharderPolicies.builder().foo().bar().build(); */ public class QueueSharderPolicies { private int newQueueThreshold; private int thresholdCheckMs; private int maxQueues; private ThreadFactory threadFactory; private static final int DEFAULT_QUEUE_THRESHOLD = 10000; private static final int DEFAULT_THRESHOLD_CHECK_MS = 30000; private static final int DEFAULT_MAX_QUEUES = 10; public static class Builder { private QueueSharderPolicies policies = new QueueSharderPolicies(); /** * Change the queue threshold. This is the number of items that causes * a new queue to be added. The default is {@link QueueSharderPolicies#DEFAULT_QUEUE_THRESHOLD} * * @param newQueueThreshold new value * @return this */ public Builder newQueueThreshold(int newQueueThreshold) { Preconditions.checkArgument(newQueueThreshold > 0, "newQueueThreshold must be a positive number"); policies.newQueueThreshold = newQueueThreshold; return this; } /** * Change the threshold check. This is how often the queue sizes are checked. The default * is {@link QueueSharderPolicies#DEFAULT_THRESHOLD_CHECK_MS} * * @param thresholdCheckMs period in milliseconds * @return this */ public Builder thresholdCheckMs(int thresholdCheckMs) { Preconditions.checkArgument(thresholdCheckMs > 0, "thresholdCheckMs must be a positive number"); policies.thresholdCheckMs = thresholdCheckMs; return this; } /** * Change the maximum number of queues to create. The default value is {@link QueueSharderPolicies#DEFAULT_MAX_QUEUES} * * @param maxQueues the new max * @return this */ public Builder maxQueues(int maxQueues) { Preconditions.checkArgument(maxQueues > 0, "thresholdCheckMs must be a positive number"); policies.maxQueues = maxQueues; return this; } /** * Change the thread factory that's used to create the sharder's thread * * @param threadFactory new factory * @return this */ public Builder threadFactory(ThreadFactory threadFactory) { policies.threadFactory = Preconditions.checkNotNull(threadFactory, "threadFactory cannot be null"); return this; } public QueueSharderPolicies build() { try { return policies; } finally { policies = new QueueSharderPolicies(); } } private Builder() { } } /** * Allocate a new builder * * @return builder */ public static Builder builder() { return new Builder(); } int getNewQueueThreshold() { return newQueueThreshold; } int getThresholdCheckMs() { return thresholdCheckMs; } int getMaxQueues() { return maxQueues; } ThreadFactory getThreadFactory() { return threadFactory; } private QueueSharderPolicies() { this.newQueueThreshold = DEFAULT_QUEUE_THRESHOLD; this.thresholdCheckMs = DEFAULT_THRESHOLD_CHECK_MS; this.maxQueues = DEFAULT_MAX_QUEUES; this.threadFactory = QueueBuilder.defaultThreadFactory; } } SimpleDistributedQueue.java000066400000000000000000000202631442004423600413300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.EnsureContainers; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** *

* Drop in replacement for: org.apache.zookeeper.recipes.queue.DistributedQueue that is part of * the ZooKeeper distribution *

* *

* This class is data compatible with the ZK version. i.e. it uses the same naming scheme so * it can read from an existing queue *

*/ public class SimpleDistributedQueue { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFramework client; private final String path; private final EnsureContainers ensureContainers; private final String PREFIX = "qn-"; /** * @param client the client * @param path path to store queue nodes */ public SimpleDistributedQueue(CuratorFramework client, String path) { this.client = client; this.path = PathUtils.validatePath(path); ensureContainers = new EnsureContainers(client, path); } /** * Return the head of the queue without modifying the queue. * * @return the data at the head of the queue. * @throws Exception errors * @throws NoSuchElementException if the queue is empty */ public byte[] element() throws Exception { byte[] bytes = internalElement(false, null); if ( bytes == null ) { throw new NoSuchElementException(); } return bytes; } /** * Attempts to remove the head of the queue and return it. * * @return The former head of the queue * @throws Exception errors * @throws NoSuchElementException if the queue is empty */ public byte[] remove() throws Exception { byte[] bytes = internalElement(true, null); if ( bytes == null ) { throw new NoSuchElementException(); } return bytes; } /** * Removes the head of the queue and returns it, blocks until it succeeds. * * @return The former head of the queue * @throws Exception errors */ public byte[] take() throws Exception { return internalPoll(0, null); } /** * Inserts data into queue. * * @param data the data * @return true if data was successfully added * @throws Exception errors */ public boolean offer(byte[] data) throws Exception { String thisPath = ZKPaths.makePath(path, PREFIX); client.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(thisPath, data); return true; } /** * Returns the data at the first element of the queue, or null if the queue is empty. * * @return data at the first element of the queue, or null. * @throws Exception errors */ public byte[] peek() throws Exception { try { return element(); } catch ( NoSuchElementException e ) { return null; } } /** * Retrieves and removes the head of this queue, waiting up to the * specified wait time if necessary for an element to become available. * * @param timeout how long to wait before giving up, in units of * unit * @param unit a TimeUnit determining how to interpret the * timeout parameter * @return the head of this queue, or null if the * specified waiting time elapses before an element is available * @throws Exception errors */ public byte[] poll(long timeout, TimeUnit unit) throws Exception { return internalPoll(timeout, unit); } /** * Attempts to remove the head of the queue and return it. Returns null if the queue is empty. * * @return Head of the queue or null. * @throws Exception errors */ public byte[] poll() throws Exception { try { return remove(); } catch ( NoSuchElementException e ) { return null; } } protected void ensurePath() throws Exception { ensureContainers.ensure(); } private byte[] internalPoll(long timeout, TimeUnit unit) throws Exception { ensurePath(); long startMs = System.currentTimeMillis(); boolean hasTimeout = (unit != null); long maxWaitMs = hasTimeout ? TimeUnit.MILLISECONDS.convert(timeout, unit) : Long.MAX_VALUE; for(;;) { final CountDownLatch latch = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { latch.countDown(); } }; byte[] bytes; try { bytes = internalElement(true, watcher); } catch ( NoSuchElementException dummy ) { log.debug("Parent containers appear to have lapsed - recreate and retry"); ensureContainers.reset(); continue; } if ( bytes != null ) { return bytes; } if ( hasTimeout ) { long elapsedMs = System.currentTimeMillis() - startMs; long thisWaitMs = maxWaitMs - elapsedMs; if ( thisWaitMs <= 0 ) { return null; } latch.await(thisWaitMs, TimeUnit.MILLISECONDS); } else { latch.await(); } } } private byte[] internalElement(boolean removeIt, Watcher watcher) throws Exception { ensurePath(); List nodes; try { nodes = (watcher != null) ? client.getChildren().usingWatcher(watcher).forPath(path) : client.getChildren().forPath(path); } catch ( KeeperException.NoNodeException dummy ) { throw new NoSuchElementException(); } Collections.sort(nodes); for ( String node : nodes ) { if ( !node.startsWith(PREFIX) ) { log.warn("Foreign node in queue path: " + node); continue; } String thisPath = ZKPaths.makePath(path, node); try { byte[] bytes = client.getData().forPath(thisPath); if ( removeIt ) { client.delete().forPath(thisPath); } return bytes; } catch ( KeeperException.NoNodeException ignore ) { //Another client removed the node first, try next } } return null; } } shared/000077500000000000000000000000001442004423600341435ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipesSharedCount.java000066400000000000000000000151361442004423600372330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.shared; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps; import com.google.common.util.concurrent.MoreExecutors; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.state.ConnectionState; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Map; import java.util.concurrent.Executor; /** * Manages a shared integer. All clients watching the same path will have the up-to-date * value of the shared integer (considering ZK's normal consistency guarantees). */ public class SharedCount implements Closeable, SharedCountReader, Listenable { private final Map listeners = Maps.newConcurrentMap(); private final SharedValue sharedValue; /** * @param client the client * @param path the shared path - i.e. where the shared count is stored * @param seedValue the initial value for the count if/f the path has not yet been created */ public SharedCount(CuratorFramework client, String path, int seedValue) { sharedValue = new SharedValue(client, path, toBytes(seedValue)); } protected SharedCount(CuratorFramework client, String path, SharedValue sv) { sharedValue = sv; } @Override public int getCount() { return fromBytes(sharedValue.getValue()); } @Override public VersionedValue getVersionedValue() { VersionedValue localValue = sharedValue.getVersionedValue(); return new VersionedValue(localValue.getVersion(), fromBytes(localValue.getValue())); } /** * Change the shared count value irrespective of its previous state * * @param newCount new value * @throws Exception ZK errors, interruptions, etc. */ public void setCount(int newCount) throws Exception { sharedValue.setValue(toBytes(newCount)); } /** * Changes the shared count only if its value has not changed since this client last * read it. If the count has changed, the value is not set and this client's view of the * value is updated. i.e. if the count is not successful you can get the updated value * by calling {@link #getCount()}. * * @deprecated use {@link #trySetCount(VersionedValue, int)} for stronger atomicity * guarantees. Even if this object's internal state is up-to-date, the caller has no way to * ensure that they've read the most recently seen count. * * @param newCount the new value to attempt * @return true if the change attempt was successful, false if not. If the change * was not successful, {@link #getCount()} will return the updated value * @throws Exception ZK errors, interruptions, etc. */ @Deprecated public boolean trySetCount(int newCount) throws Exception { return sharedValue.trySetValue(toBytes(newCount)); } /** * Changes the shared count only if its value has not changed since the version specified by * newCount. If the count has changed, the value is not set and this client's view of the * value is updated. i.e. if the count is not successful you can get the updated value * by calling {@link #getCount()}. * * @param newCount the new value to attempt * @return true if the change attempt was successful, false if not. If the change * was not successful, {@link #getCount()} will return the updated value * @throws Exception ZK errors, interruptions, etc. */ public boolean trySetCount(VersionedValue previous, int newCount) throws Exception { VersionedValue previousCopy = new VersionedValue(previous.getVersion(), toBytes(previous.getValue())); return sharedValue.trySetValue(previousCopy, toBytes(newCount)); } @Override public void addListener(SharedCountListener listener) { addListener(listener, MoreExecutors.directExecutor()); } @Override public void addListener(final SharedCountListener listener, Executor executor) { SharedValueListener valueListener = new SharedValueListener() { @Override public void valueHasChanged(SharedValueReader sharedValue, byte[] newValue) throws Exception { listener.countHasChanged(SharedCount.this, fromBytes(newValue)); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { listener.stateChanged(client, newState); } }; sharedValue.getListenable().addListener(valueListener, executor); listeners.put(listener, valueListener); } @Override public void removeListener(SharedCountListener listener) { SharedValueListener valueListener = listeners.remove(listener); if(valueListener != null) { sharedValue.getListenable().removeListener(valueListener); } } /** * The shared count must be started before it can be used. Call {@link #close()} when you are * finished with the shared count * * @throws Exception ZK errors, interruptions, etc. */ public void start() throws Exception { sharedValue.start(); } @Override public void close() throws IOException { sharedValue.close(); } @VisibleForTesting static byte[] toBytes(int value) { byte[] bytes = new byte[4]; ByteBuffer.wrap(bytes).putInt(value); return bytes; } private static int fromBytes(byte[] bytes) { return ByteBuffer.wrap(bytes).getInt(); } } SharedCountListener.java000066400000000000000000000025051442004423600407350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.shared; import org.apache.curator.framework.state.ConnectionStateListener; /** * Listener for changes to a shared count */ public interface SharedCountListener extends ConnectionStateListener { /** * Called when the shared value has changed * * @param sharedCount the shared count instance * @param newCount the new count * @throws Exception errors */ public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception; } SharedCountReader.java000066400000000000000000000025201442004423600403470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.shared; import org.apache.curator.framework.listen.Listenable; /** * Abstracts a shared integer and allows listening for changes to its value */ public interface SharedCountReader extends Listenable { /** * Return the current value of the count * * @return count */ public int getCount(); /** * Return the current count and version * * @return count and version */ public VersionedValue getVersionedValue(); } SharedValue.java000066400000000000000000000267711442004423600372260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.shared; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.WatcherRemoveCuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.PathUtils; import org.apache.curator.utils.ThreadUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.atomic.AtomicReference; /** * Manages a shared value. All clients watching the same path will have the up-to-date * value (considering ZK's normal consistency guarantees). */ public class SharedValue implements Closeable, SharedValueReader { private static final int UNINITIALIZED_VERSION = -1; private final Logger log = LoggerFactory.getLogger(getClass()); private final StandardListenerManager listeners = StandardListenerManager.standard(); private final WatcherRemoveCuratorFramework client; private final String path; private final byte[] seedValue; private final AtomicReference state = new AtomicReference(State.LATENT); private final AtomicReference> currentValue; private final CuratorWatcher watcher; private class SharedValueCuratorWatcher implements CuratorWatcher { @Override public void process(WatchedEvent event) throws Exception { if ( state.get() == State.STARTED && event.getType() != Watcher.Event.EventType.None ) { // don't block event thread in possible retry readValueAndNotifyListenersInBackground(); } } }; private final ConnectionStateListener connectionStateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { notifyListenerOfStateChanged(newState); if ( newState.isConnected() ) { try { readValueAndNotifyListenersInBackground(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Could not read value after reconnect", e); } } } }; private enum State { LATENT, STARTED, CLOSED } /** * @param client the client * @param path the shared path - i.e. where the shared value is stored * @param seedValue the initial value for the value if/f the path has not yet been created */ public SharedValue(CuratorFramework client, String path, byte[] seedValue) { this.client = client.newWatcherRemoveCuratorFramework(); this.path = PathUtils.validatePath(path); this.seedValue = Arrays.copyOf(seedValue, seedValue.length); this.watcher = new SharedValueCuratorWatcher(); currentValue = new AtomicReference>(new VersionedValue(UNINITIALIZED_VERSION, Arrays.copyOf(seedValue, seedValue.length))); } @VisibleForTesting protected SharedValue(WatcherRemoveCuratorFramework client, String path, byte[] seedValue, CuratorWatcher watcher) { this.client = client; this.path = PathUtils.validatePath(path); this.seedValue = Arrays.copyOf(seedValue, seedValue.length); // inject watcher for testing this.watcher = watcher; currentValue = new AtomicReference>(new VersionedValue(UNINITIALIZED_VERSION, Arrays.copyOf(seedValue, seedValue.length))); } @Override public byte[] getValue() { VersionedValue localCopy = currentValue.get(); return Arrays.copyOf(localCopy.getValue(), localCopy.getValue().length); } @Override public VersionedValue getVersionedValue() { VersionedValue localCopy = currentValue.get(); return new VersionedValue(localCopy.getVersion(), Arrays.copyOf(localCopy.getValue(), localCopy.getValue().length)); } /** * Change the shared value value irrespective of its previous state * * @param newValue new value * @throws Exception ZK errors, interruptions, etc. */ public void setValue(byte[] newValue) throws Exception { Preconditions.checkState(state.get() == State.STARTED, "not started"); Stat result = client.setData().forPath(path, newValue); updateValue(result.getVersion(), Arrays.copyOf(newValue, newValue.length)); } /** * Changes the shared value only if its value has not changed since this client last * read it. If the value has changed, the value is not set and this client's view of the * value is updated. i.e. if the value is not successful you can get the updated value * by calling {@link #getValue()}. * * @deprecated use {@link #trySetValue(VersionedValue, byte[])} for stronger atomicity * guarantees. Even if this object's internal state is up-to-date, the caller has no way to * ensure that they've read the most recently seen value. * * @param newValue the new value to attempt * @return true if the change attempt was successful, false if not. If the change * was not successful, {@link #getValue()} will return the updated value * @throws Exception ZK errors, interruptions, etc. */ @Deprecated public boolean trySetValue(byte[] newValue) throws Exception { return trySetValue(currentValue.get(), newValue); } /** * Changes the shared value only if its value has not changed since the version specified by * newValue. If the value has changed, the value is not set and this client's view of the * value is updated. i.e. if the value is not successful you can get the updated value * by calling {@link #getValue()}. * * @param newValue the new value to attempt * @return true if the change attempt was successful, false if not. If the change * was not successful, {@link #getValue()} will return the updated value * @throws Exception ZK errors, interruptions, etc. */ public boolean trySetValue(VersionedValue previous, byte[] newValue) throws Exception { Preconditions.checkState(state.get() == State.STARTED, "not started"); VersionedValue current = currentValue.get(); if ( previous.getVersion() != current.getVersion() || !Arrays.equals(previous.getValue(), current.getValue()) ) { return false; } try { Stat result = client.setData().withVersion(previous.getVersion()).forPath(path, newValue); updateValue(result.getVersion(), Arrays.copyOf(newValue, newValue.length)); return true; } catch ( KeeperException.BadVersionException ignore ) { // ignore } readValue(); return false; } private void updateValue(int version, byte[] bytes) { while (true) { VersionedValue current = currentValue.get(); if (current.getVersion() >= version) { // A newer version was concurrently set. return; } if ( currentValue.compareAndSet(current, new VersionedValue(version, bytes)) ) { // Successfully set. return; } // Lost a race, retry. } } /** * Returns the listenable * * @return listenable */ public Listenable getListenable() { return listeners; } /** * The shared value must be started before it can be used. Call {@link #close()} when you are * finished with the shared value * * @throws Exception ZK errors, interruptions, etc. */ public void start() throws Exception { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once"); client.getConnectionStateListenable().addListener(connectionStateListener); try { client.create().creatingParentContainersIfNeeded().forPath(path, seedValue); } catch ( KeeperException.NodeExistsException ignore ) { // ignore } readValue(); } @Override public void close() throws IOException { state.set(State.CLOSED); client.removeWatchers(); client.getConnectionStateListenable().removeListener(connectionStateListener); listeners.clear(); } private void readValue() throws Exception { Stat localStat = new Stat(); byte[] bytes = client.getData().storingStatIn(localStat).usingWatcher(watcher).forPath(path); updateValue(localStat.getVersion(), bytes); } private final BackgroundCallback upadateAndNotifyListenerCallback = new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { if (event.getResultCode() == KeeperException.Code.OK.intValue()) { updateValue(event.getStat().getVersion(), event.getData()); notifyListeners(); } } }; private void readValueAndNotifyListenersInBackground() throws Exception { client.getData().usingWatcher(watcher).inBackground(upadateAndNotifyListenerCallback).forPath(path); } private void notifyListeners() { final byte[] localValue = getValue(); listeners.forEach(listener -> { try { listener.valueHasChanged(SharedValue.this, localValue); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("From SharedValue listener", e); } }); } private void notifyListenerOfStateChanged(final ConnectionState newState) { listeners.forEach(listener -> listener.stateChanged(client, newState)); } } SharedValueListener.java000066400000000000000000000025001442004423600407140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.shared; import org.apache.curator.framework.state.ConnectionStateListener; /** * Listener for changes to a shared value */ public interface SharedValueListener extends ConnectionStateListener { /** * Called when the shared value has changed * * @param sharedValue the shared value instance * @param newValue the new value * @throws Exception errors */ public void valueHasChanged(SharedValueReader sharedValue, byte[] newValue) throws Exception; } SharedValueReader.java000066400000000000000000000026551442004423600403440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.shared; import org.apache.curator.framework.listen.Listenable; /** * Abstracts a shared value and allows listening for changes to the value */ public interface SharedValueReader { /** * Return the current value of the count * * @return count */ public byte[] getValue(); /** * Return the current version and value * * @return version/value */ public VersionedValue getVersionedValue(); /** * Returns the listenable * * @return listenable */ public Listenable getListenable(); } VersionedValue.java000066400000000000000000000026271442004423600377500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/shared/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.shared; import com.google.common.base.Preconditions; /** * POJO for a version and a value */ public class VersionedValue { private final int version; private final T value; /** * @param version the version * @param value the value (cannot be null) */ VersionedValue(int version, T value) { this.version = version; this.value = Preconditions.checkNotNull(value, "value cannot be null"); } public int getVersion() { return version; } public T getValue() { return value; } } watch/000077500000000000000000000000001442004423600340035ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipesPersistentWatcher.java000066400000000000000000000136431442004423600403330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/main/java/org/apache/curator/framework/recipes/watch/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.watch; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.ThreadUtils; import org.apache.zookeeper.AddWatchMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; /** * A managed persistent watcher. The watch will be managed such that it stays set through * connection lapses, etc. */ public class PersistentWatcher implements Closeable { private final Logger log = LoggerFactory.getLogger(getClass()); private final AtomicReference state = new AtomicReference<>(State.LATENT); private final StandardListenerManager listeners = StandardListenerManager.standard(); private final StandardListenerManager resetListeners = StandardListenerManager.standard(); private final ConnectionStateListener connectionStateListener = (client, newState) -> { if ( newState.isConnected() ) { reset(); } }; private final Watcher watcher = event -> listeners.forEach(w -> w.process(event)); private final CuratorFramework client; private final String basePath; private final boolean recursive; private enum State { LATENT, STARTED, CLOSED } /** * @param client client * @param basePath path to set the watch on * @param recursive ZooKeeper persistent watches can optionally be recursive */ public PersistentWatcher(CuratorFramework client, String basePath, boolean recursive) { this.client = Objects.requireNonNull(client, "client cannot be null"); this.basePath = Objects.requireNonNull(basePath, "basePath cannot be null"); this.recursive = recursive; } /** * Start watching */ public void start() { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Already started"); client.getConnectionStateListenable().addListener(connectionStateListener); reset(); } /** * Remove the watcher */ @Override public void close() { if ( state.compareAndSet(State.STARTED, State.CLOSED) ) { listeners.clear(); client.getConnectionStateListenable().removeListener(connectionStateListener); try { client.watchers().remove(watcher).guaranteed().inBackground().forPath(basePath); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.debug(String.format("Could not remove watcher for path: %s", basePath), e); } } } /** * Container for setting listeners * * @return listener container */ public Listenable getListenable() { return listeners; } /** * Listeners are called when the persistent watcher has been successfully registered * or re-registered after a connection disruption * * @return listener container */ public Listenable getResetListenable() { return resetListeners; } private void reset() { if ( state.get() != State.STARTED ) { return; } try { BackgroundCallback callback = (__, event) -> { if ( event.getResultCode() == KeeperException.Code.OK.intValue() ) { resetListeners.forEach(Runnable::run); } else { reset(); } }; client.watchers().add().withMode(recursive ? AddWatchMode.PERSISTENT_RECURSIVE : AddWatchMode.PERSISTENT).inBackground(callback).usingWatcher(watcher).forPath(basePath); } catch ( Exception e ) { log.error("Could not reset persistent watch at path: " + basePath, e); } } } curator-apache-curator-5.5.0/curator-recipes/src/site/000077500000000000000000000000001442004423600227355ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/000077500000000000000000000000001442004423600250565ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/barrier.confluence000066400000000000000000000014661442004423600305560ustar00rootroot00000000000000h1. Barrier h2. Description An implementation of the Distributed Barrier ZK recipe. Distributed systems use barriers to block processing of a set of nodes until a condition is met at which time all the nodes are allowed to proceed. h2. Participating Classes * DistributedBarrier h2. Usage h3. Creating a DistributedBarrier {code} public DistributedBarrier(CuratorFramework client, String barrierPath) Parameters: client - client barrierPath - path to use as the barrier {code} h2. General Usage To wait on the barrier: {code} public void waitOnBarrier() {code} There are utilities for setting/removing the barrier: {code} setBarrier(); removeBarrier(); {code} h2. Error Handling DistributedBarrier instances watch for connection loss and will throw an exception from {{waitOnBarrier()}}. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/curator-cache.confluence000066400000000000000000000024571442004423600316510ustar00rootroot00000000000000h1. Curator Cache *Note: * CuratorCache requires ZooKeeper 3.6\+. h2. Description A utility that attempts to keep the data from a node locally cached. Optionally the entire tree of children below the node can also be cached. Will respond to update/create/delete events, pull down the data, etc. You can register listeners that will get notified when changes occur. h2. Participating Classes * CuratorCache * CuratorCacheListener * ChildData h2. Usage h3. Creating a CuratorCache {code} CuratorCache.build(CuratorFramework client, String path, Options... options) Parameters: client - the client path - path to watch options - empty or one or more options {code} Note: there is a builder factory available for additional options when building the cache instance. See {{CuratorCacheBuilder}} for details. h2. General Usage The cache must be started by calling {{start()}}. Call {{close()}} when you are through with the cache. At any time, call the various accessor methods to get the current state of the cache. You can also register to be notified when a change occurs by calling {{listenable()}} and then registering a listener for events. See the examples for an example usage. h2. Error Handling CuratorCache instances internally monitor connection losses, etc. automatically rebuilding the cache on reconnection. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/distributed-atomic-long.confluence000066400000000000000000000041501442004423600336520ustar00rootroot00000000000000h1. Distributed Atomic Long h2. Description A counter that attempts atomic increments. It first tries using optimistic locking. If that fails, an optional InterProcessMutex is taken. For both optimistic and mutex, a retry policy is used to retry the increment h2. Participating Classes * DistributedAtomicLong * AtomicValue * PromotedToLock h2. Usage h3. Creating a DistributedAtomicLong Optimistic mode only: {code} public DistributedAtomicLong(CuratorFramework client, String counterPath, RetryPolicy retryPolicy) Creates the counter in optimistic mode only - i.e. the promotion to a mutex is not done Parameters: client - the client counterPath - path to hold the counter value retryPolicy - the retry policy to use {code} Mutex promotion mode: {code} public DistributedAtomicLong(CuratorFramework client, String counterPath, RetryPolicy retryPolicy, PromotedToLock promotedToLock) Creates the counter in mutex promotion mode. The optimistic lock will be tried first using the given retry policy. If the increment does not succeed, a InterProcessMutex will be tried with its own retry policy Parameters: client - the client counterPath - path to hold the counter value retryPolicy - the retry policy to use promotedToLock - the arguments for the mutex promotion {code} h2. General Usage # Perform an operation on the counter: ** {{get()}} ** {{increment()}} ** {{decrement()}} ** {{add()}} ** {{subtract()}} # Examine the result AtomicValue: ** You *must* first check {{succeeded()}} which returns true if the operation succeeded. If false is returned, the operation failed and the atomic was not updated. ** If the operation succeeded, you can get the value prior to the operation via {{preValue()}} and the value after the operation {{postValue()}} h2. Error Handling All the atomic instances access the ZooKeeper server for each method call. Therefore, the standard retry mechanism will be applied and any errors executing the operations will result in an Exception being thrown. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/distributed-delay-queue.confluence000066400000000000000000000054771442004423600336760ustar00rootroot00000000000000h1. Distributed Delay Queue h2. *IMPORTANT* \- We recommend that you do NOT use ZooKeeper for Queues. Please see [[Tech Note 4|https://cwiki.apache.org/confluence/display/CURATOR/TN4]] for details. h2. Description An implementation of a Distributed Delay Queue. A Delay Queue is similar to a Priority Queue. When items are added to the queue, a delay value is given. The item will not be sent to a consumer until the time elapses. h2. Participating Classes * QueueBuilder * QueueConsumer * QueueSerializer * DistributedDelayQueue h2. Usage h3. Creating a DistributedDelayQueue {code} public static QueueBuilder builder(CuratorFramework client, QueueConsumer consumer, QueueSerializer serializer, java.lang.String queuePath) Parameters: client - the curator client consumer - message consumer serializer - serializer to use for items queuePath - path to store queue {code} {code} QueueBuilder builder = QueueBuilder.builder(client, consumer, serializer, path); ... more builder method calls as needed ... DistributedDelayQueue queue = builder.buildDelayQueue(); {code} h2. General Usage The queue must be started via the {{start()}} method. Call {{close()}} when you are done with the queue. To add messages to the queue: {code}{code} queue.put(aMessage, delayUntilEpoch); {code} The consumer ({{QueueConsumer.consumeMessage()}}) will get called as messages arrive. {{delayUntilEpoch}} is a future epoch (milliseconds) when this item will be available to consumers. h2. Lock Safety In the general usage case, the message is removed from the queue prior to the consumer being called. A more atomic mode is provided that removes the item from the queue only after the consumer successfully returns. To enable this mode, call the {{lockPath()}} method of the Queue Builder. This uses a lock to make the message recoverable. A lock is held while the message is being processed \- this prevents other processes from taking the message. The message will not be removed from the queue until the consumer functor returns. Thus, if there is a failure or the process dies, the message will get sent to another process. There is a small performance penalty for this behavior however. h2. Data Format Same as [[Distributed Queue|distributed-queue.html]]. h2. Error Handling The {{QueueConsumer}} class extends {{ConnectionStateListener}}. When the queue is started, it adds the listener to the Curator instance. Users of the {{DistributedPriorityQueue}} must pay attention to any connection state changes. If the SUSPENDED state is reported, the instance must assume that, until it receives a RECONNECTED state, the queue is no longer being updated. If the LOST state is reported, the queue is permanently down. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/distributed-id-queue.confluence000066400000000000000000000025411442004423600331610ustar00rootroot00000000000000h1. Distributed ID Queue h2. *IMPORTANT* \- We recommend that you do NOT use ZooKeeper for Queues. Please see [[Tech Note 4|https://cwiki.apache.org/confluence/display/CURATOR/TN4]] for details. h2. Description This is an alternate version of [[Distributed Queue|distributed-queue.html]] that supports assigning IDs to the items added to the queue. Items put into the queue are guaranteed to be ordered (by means of ZK's PERSISTENT_SEQUENTIAL node). If a single consumer takes items out of the queue, they will be ordered FIFO. If ordering is important, use a LeaderSelector to nominate a single consumer. h2. Participating Classes * QueueBuilder * QueueConsumer * QueueSerializer * DistributedQueue h2. Usage h3. Creating a DistributedIdQueue See [[Distributed Queue|distributed-queue.html]] for details of using the builder, the only difference is to use the {{buildIdQueue()}} method. h2. General Usage The queue must be started via the {{start()}} method. Call {{close()}} when you are done with the queue. To add messages to the queue: {code} queue.put(aMessage, messageId); {code} To remove messages from the queue: {code} int numberRemoved = queue.remove(messageId); {code} Your consumer ({{QueueConsumer.consumeMessage()}}) will get called as messages arrive. The lock safety and error handling are the same as for [[Distributed Queue|distributed-queue.html]].distributed-priority-queue.confluence000066400000000000000000000071661442004423600343770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/site/confluenceh1. Distributed Priority Queue h2. *IMPORTANT* \- We recommend that you do NOT use ZooKeeper for Queues. Please see [[Tech Note 4|https://cwiki.apache.org/confluence/display/CURATOR/TN4]] for details. h2. Description An implementation of the Distributed Priority Queue ZK recipe. h2. Participating Classes * QueueBuilder * QueueConsumer * QueueSerializer * DistributedPriorityQueue h2. Usage h3. Creating a DistributedPriorityQueue {code} public static QueueBuilder builder(CuratorFramework client, QueueConsumer consumer, QueueSerializer serializer, java.lang.String queuePath) Parameters: client - the curator client consumer - message consumer serializer - serializer to use for items queuePath - path to store queue {code} {code} QueueBuilder builder = QueueBuilder.builder(client, consumer, serializer, path); ... more builder method calls as needed ... DistributedPriorityQueue queue = builder.buildPriorityQueue(minItemsBeforeRefresh); {code} {code} public DistributedPriorityQueue buildPriorityQueue(int minItemsBeforeRefresh) Build a DistributedPriorityQueue from the current builder values. When the priority queue detects an item addition/removal, it will stop processing its current list of items and refresh the list. minItemsBeforeRefresh modifies this. It determines the minimum number of items from the active list that will get processed before a refresh. Due to a quirk in the way ZooKeeper notifies changes, the queue will get an item addition/remove notification after every item is processed. This can lead to poor performance. Set minItemsBeforeRefresh to the value your application can tolerate being out of sync. For example: if the queue sees 10 items to process, it will end up making 10 calls to ZooKeeper to check status. You can control this by setting minItemsBeforeRefresh to 10 (or more) and the queue will only refresh with ZooKeeper after 10 items are processed Parameters: minItemsBeforeRefresh - minimum items to process before refreshing the item list {code} h2. General Usage The queue must be started via the {{start()}} method. Call {{close()}} when you are done with the queue. To add messages to the queue: {code} queue.put(aMessage, priority); {code} The consumer ({{QueueConsumer.consumeMessage()}}) will get called as messages arrive. h2. Lock Safety In the general usage case, the message is removed from the queue prior to the consumer being called. A more atomic mode is provided that removes the item from the queue only after the consumer successfully returns. To enable this mode, call the {{lockPath()}} method of the Queue Builder. This uses a lock to make the message recoverable. A lock is held while the message is being processed \- this prevents other processes from taking the message. The message will not be removed from the queue until the consumer functor returns. Thus, if there is a failure or the process dies, the message will get sent to another process. There is a small performance penalty for this behavior however. h2. Data Format Same as [[Distributed Queue|distributed-queue.html]]. h2. Error Handling The {{QueueConsumer}} class extends {{ConnectionStateListener}}. When the queue is started, it adds the listener to the Curator instance. Users of the {{DistributedPriorityQueue}} must pay attention to any connection state changes. If the SUSPENDED state is reported, the instance must assume that, until it receives a RECONNECTED state, the queue is no longer being updated. If the LOST state is reported, the queue is permanently down. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/distributed-queue.confluence000066400000000000000000000060041442004423600325650ustar00rootroot00000000000000h1. Distributed Queue h2. *IMPORTANT* \- We recommend that you do NOT use ZooKeeper for Queues. Please see [[Tech Note 4|https://cwiki.apache.org/confluence/display/CURATOR/TN4]] for details. h2. Description An implementation of the Distributed Queue ZK recipe. Items put into the queue are guaranteed to be ordered (by means of ZK's PERSISTENT_SEQUENTIAL node). If a single consumer takes items out of the queue, they will be ordered FIFO. If ordering is important, use a LeaderSelector to nominate a single consumer. h2. Participating Classes * QueueBuilder * QueueConsumer * QueueSerializer * DistributedQueue h2. Usage h3. Creating a DistributedQueue {code} public static QueueBuilder builder(CuratorFramework client, QueueConsumer consumer, QueueSerializer serializer, java.lang.String queuePath) Parameters: client - the curator client consumer - functor to receive messages serializer - serializer to use for items queuePath - path to store queue {code} {code} QueueBuilder builder = QueueBuilder.builder(client, consumer, serializer, path); ... more builder method calls as needed ... DistributedQueue locks) Creates a multi lock of any type of inter process lock Parameters: locks - the locks {code} {code} public InterProcessMultiLock(CuratorFramework client, List paths) Creates a multi lock of InterProcessMutexes Parameters: client - client paths - list of paths to manage in the order that they are to be locked {code} h2. General Usage The usage is the same as for [[Shared Lock|shared-lock.html]]. However, When acquire() is called, all the locks are acquired. If that fails, any paths that were acquired are released. Similarly, when release() is called, all locks are released (failures are ignored). h2. Error Handling It is strongly recommended that you add a {{ConnectionStateListener}} and watch for SUSPENDED and LOST state changes. If a SUSPENDED state is reported you cannot be certain that you still hold the lock unless you subsequently receive a RECONNECTED state. If a LOST state is reported it is certain that you no longer hold the lock. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/node-cache.confluence000066400000000000000000000017551442004423600311170ustar00rootroot00000000000000h1. Node Cache h2. Description A Node Cache is used to watch a ZNode. Whenever the data is modified or the ZNode is deleted, the Node Cache will change its state to contain the current data (or null if ZNode was deleted). h2. Participating Classes * NodeCache * NodeCacheListener * ChildData h2. Usage h3. Creating a NodeChildrenCache {code} public NodeCache(CuratorFramework client, String path) Parameters: client - the client path - path to cache {code} h2. General Usage The cache must be started by calling {{start()}}. Call {{close()}} when you are through with the cache. At any time, call {{getCurrentData()}} to get the current state of the cache. You can also register to be notified when a change occurs by calling {{getListenable()}} and then: {code} public void addListener(NodeCacheListener listener) Add a change listener Parameters: listener - the listener {code} h2. Error Handling NodeCache instances internally monitor a {{ConnectionStateListener}}. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/path-cache.confluence000066400000000000000000000037451442004423600311270ustar00rootroot00000000000000h1. Path Cache h2. Description A Path Cache is used to watch a ZNode. Whenever a child is added, updated or removed, the Path Cache will change its state to contain the current set of children, the children's data and the children's state. h2. Participating Classes * PathChildrenCache * PathChildrenCacheEvent * PathChildrenCacheListener * ChildData h2. Usage h3. Creating a PathChildrenCache {code} public PathChildrenCache(CuratorFramework client, String path, boolean cacheData) Parameters: client - the client path - path to watch cacheData - if true, node contents are cached in addition to the stat {code} h2. General Usage The cache must be started by calling {{start()}}. Call {{close()}} when you are through with the cache. There are two versions of {{start()}}. The no\-arg version gives default behavior. The other version takes an enumeration that allows you to control how the initial cache is warmed: {code} public enum StartMode { /** * cache will _not_ be primed. i.e. it will start empty and you will receive * events for all nodes added, etc. */ NORMAL, /** * rebuild() will be called before this method returns in * order to get an initial view of the node. */ BUILD_INITIAL_CACHE, /** * After cache is primed with initial values (in the background) a * PathChildrenCacheEvent.Type.INITIALIZED event will be posted */ POST_INITIALIZED_EVENT } {code} At any time, call {{getCurrentData()}} to get the current state of the cache. You can also register to be notified when a change occurs by calling {{getListenable()}} and then: {code} public void addListener(PathChildrenCacheListener listener) Add a change listener Parameters: listener - the listener {code} h2. Error Handling PathChildrenCache instances internally monitor a {{ConnectionStateListener}}. If the connection state changes, the cache is reset (the {{PathChildrenCacheListener}} will receive a RESET). curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/persistent-node.confluence000066400000000000000000000022051442004423600322430ustar00rootroot00000000000000h1. Persistent Node h2. Description A persistent node is a node that attempts to stay present in ZooKeeper, even through connection and session interruptions. h2. Participating Classes * PersistentNode h2. Usage h3. Creating a PersistentNode {code} public PersistentNode(CuratorFramework client, CreateMode mode, boolean useProtection, String basePath, byte[] data, boolean useParentCreation) Parameters: client - client instance mode - creation mode useProtection - if true, call CreateBuilder.withProtection() basePath - the base path for the node data - data for the node useParentCreation - if true, call CreateBuilder.creatingParentContainersIfNeeded() {code} h3. General Usage PersistentNodes must be started: {code} node.start(); {code} When you are through with the PersistentNode instance, you should call close: {code} node.close(); {code} NOTE: this will delete the node h2. Error Handling PersistentNode instances internally handle all error states recreating the node as necessary. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/persistent-ttl-node.confluence000066400000000000000000000025141442004423600330470ustar00rootroot00000000000000h1. Persistent TTL Node h2. Description PersistentTtlNode is useful when you need to create a TTL node but don't want to keep it alive manually by periodically setting data \- PersistentTtlNode does that for you. Further the keep\-alive is done in a way that does not generate watch triggers on the parent node. It also provides similar guarantees that a [[Persistent Node|persistent-node.html]] does: the node attempts to stay present in ZooKeeper, even through connection and session interruptions. h2. Participating Classes * PersistentNode * PersistentTtlNode h2. Usage h3. Creating a PersistentTtlNode {code} public PersistentTtlNode(CuratorFramework client, String path, long ttlMs, byte[] initData) Parameters: client - client instance path path for the parent ZNode ttlMs max ttl for the node in milliseconds initData - initData for the node {code} h3. General Usage PersistentTtlNode must be started: {code} node.start(); {code} When you are through with the PersistentTtlNode instance, you should call close: {code} node.close(); {code} NOTE: this will NOT delete the node immediately. The node will get deleted based on the ttl. h2. Error Handling PersistentTtlNode instances internally handle all error states recreating the node as necessary. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/persistent-watcher.confluence000066400000000000000000000023721442004423600327600ustar00rootroot00000000000000h1. Persistent Recursive Watcher *Note: * PersistentWatcher requires ZooKeeper 3.6\+. h2. Description A managed persistent persistent watcher. The watch will be managed such that it stays set through connection lapses, etc. h2. Participating Classes * PersistentWatcher h2. Usage h3. Creating a PersistentWatcher {code} public PersistentWatcher(CuratorFramework client, String basePath, boolean recursive) Parameters: client - the client basePath - path to set the watch on recursive - ZooKeeper persistent watches can optionally be recursive {code} h2. General Usage The instance must be started by calling {{start()}}. Call {{close()}} when you want to remove the watch. PersistentWatcher presents two listener types: * {{Listenable getListenable()}} \- Use this to add watchers. These will behave in the same manner that watchers added via {{ZooKeeper.addWatch()}} behave. * {{Listenable getResetListenable()}} \- The Runnables added with this get called once the Persistent Watcher has been successfully set (or reset after a connection partition). h2. Error Handling PersistentWatcher instances internally monitor connection losses, etc. automatically resetting on reconnection. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/shared-counter.confluence000066400000000000000000000040421442004423600320440ustar00rootroot00000000000000h1. Shared Counter h2. Description Manages a shared integer. All clients watching the same path will have the up\-to\-date value of the shared integer (considering ZK's normal consistency guarantees). h2. Participating Classes * SharedCount * SharedCountReader * SharedCountListener h2. Usage h3. Creating a SharedCounter {code} public SharedCount(CuratorFramework client, String path, int seedValue) Parameters: client - the client path - the shared path - i.e. where the shared count is stored seedValue - the initial value for the count if/f the path has not yet been created {code} h3. General Usage SharedCounts must be started: {code} count.start(); {code} When you are through with the instance, you should call close: {code} count.close(); {code} {code} int getCount() Return the current value of the count {code} {code} void addListener(SharedCountListener listener) Add a listener for changes to the count {code} {code} public void setCount(int newCount) Change the shared count value irrespective of its previous state {code} {code} public boolean trySetCount(int newCount) Changes the shared count only if its value has not changed since this client last read it. If the count has changed, the value is not set and this client's view of the value is updated. i.e. if the count is not successful you can get the updated value by calling getCount(). Parameters: newCount - the new value to attempt Returns: true if the change attempt was successful, false if not. If the change was not successful, getCount() will return the updated value {code} h2. Error Handling The {{SharedCountListener}} class extends {{ConnectionStateListener}}. When the SharedCount is started, it adds the listener to the Curator instance. Users of the {{SharedCount}} must pay attention to any connection state changes. If the SUSPENDED state is reported, the instance must assume that, until it receives a RECONNECTED state, the count is no longer accurate and isn't being updated. If the LOST state is reported, the count is permanently down. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/shared-lock.confluence000066400000000000000000000027631442004423600313250ustar00rootroot00000000000000h1. Shared Lock h2. Description Fully distributed locks that are globally synchronous, meaning at any snapshot in time no two clients think they hold the same lock. Note: unlike InterProcessMutex this lock is *not* reentrant. h2. Participating Classes * InterProcessSemaphoreMutex h2. Usage h3. Create an InterProcessSemaphoreMutex {code} public InterProcessSemaphoreMutex(CuratorFramework client, String path) Parameters: client - client path - the path to lock {code} h3. General Usage To acquire the lock, use one of the acquire methods: {code} public void acquire() Acquire the mutex - blocking until it's available. Must be balanced by a call to release(). {code} {code} public boolean acquire(long time, TimeUnit unit) Acquire the mutex - blocks until it's available or the given time expires. Must be balanced by a call to release().   Parameters: time - time to wait unit - time unit Returns: true if the mutex was acquired, false if not {code} To release the mutex, call: {code} public void release() Perform one release of the mutex if the calling thread is the same thread that acquired it. {code} h2. Error Handling It is strongly recommended that you add a {{ConnectionStateListener}} and watch for SUSPENDED and LOST state changes. If a SUSPENDED state is reported you cannot be certain that you still hold the lock unless you subsequently receive a RECONNECTED state. If a LOST state is reported it is certain that you no longer hold the lock. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/shared-reentrant-lock.confluence000066400000000000000000000053741442004423600333260ustar00rootroot00000000000000h1. Shared Reentrant Lock h2. Description Fully distributed locks that are globally synchronous, meaning at any snapshot in time no two clients think they hold the same lock. h2. Participating Classes * InterProcessMutex h2. Usage h3. Create an InterProcessMutex {code} public InterProcessMutex(CuratorFramework client, String path) Parameters: client - client path - the path to lock {code} h3. General Usage To acquire the lock, use one of the acquire methods: {code} public void acquire() Acquire the mutex - blocking until it's available. Note: the same thread can call acquire re-entrantly. Each call to acquire must be balanced by a call to release() {code} {code} public boolean acquire(long time, TimeUnit unit) Acquire the mutex - blocks until it's available or the given time expires. Note: the same thread can call acquire re-entrantly. Each call to acquire that returns true must be balanced by a call to release() Parameters: time - time to wait unit - time unit Returns: true if the mutex was acquired, false if not {code} To release the mutex, call: {code} public void release() Perform one release of the mutex if the calling thread is the same thread that acquired it. If the thread had made multiple calls to acquire, the mutex will still be held when this method returns. {code} *NOTE:* A InterProcessMutex instance is reusable. i.e. don't create a new instance every time. Re\-use a single instance. h3. Revoking InterProcessMutex supports a cooperative revocation mechanism as described on the ZooKeeper recipes wiki. To make a mutex revocable, call: {code} public void makeRevocable(RevocationListener listener) Make the lock revocable. Your listener will get called when another process/thread wants you to release the lock. Revocation is cooperative. Parameters: listener - the listener {code} To ask for a lock to revoke/release, use the static method in the {{Revoker}} class: {code} public static void attemptRevoke(CuratorFramework client, String path) throws Exception Utility to mark a lock for revocation. Assuming that the lock has been registered with a RevocationListener, it will get called and the lock should be released. Note, however, that revocation is cooperative. Parameters: client - the client path - the path of the lock - usually from something like InterProcessMutex.getParticipantNodes() {code} h2. Error Handling It is strongly recommended that you add a {{ConnectionStateListener}} and watch for SUSPENDED and LOST state changes. If a SUSPENDED state is reported you cannot be certain that you still hold the lock unless you subsequently receive a RECONNECTED state. If a LOST state is reported it is certain that you no longer hold the lock. shared-reentrant-read-write-lock.confluence000066400000000000000000000041061442004423600353000ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/site/confluenceh1. Shared Reentrant Read Write Lock h2. Description A re\-entrant read/write mutex that works across JVMs. Uses Zookeeper to hold the lock. All processes in all JVMs that use the same lock path will achieve an inter\-process critical section. Further, this mutex is "fair" - each user will get the mutex in the order requested (from ZK's point of view). A read write lock maintains a pair of associated locks, one for read\-only operations and one for writing. The read lock may be held simultaneously by multiple reader processes, so long as there are no writers. The write lock is exclusive. _Reentrancy_ This lock allows both readers and writers to reacquire read or write locks in the style of a re\-entrant lock. Non\-re\-entrant readers are not allowed until all write locks held by the writing thread/process have been released. Additionally, a writer can acquire the read lock, but not vice\-versa. If a reader tries to acquire the write lock it will never succeed. _Lock Downgrading_ Re\-entrancy also allows downgrading from the write lock to a read lock, by acquiring the write lock, then the read lock and then releasing the write lock. However, upgrading from a read lock to the write lock is not possible. h2. Participating Classes * InterProcessReadWriteLock * InterProcessLock h2. Usage h3. Create an InterProcessReadWriteLock {code} public InterProcessReadWriteLock(CuratorFramework client, String basePath) Parameters: client - the client basePath - path to use for locking {code} h3. General Usage Access either the read lock or the write lock and then use the methods as described for [[Shared lock|shared-lock.html]]. {code} public InterProcessLock readLock() public InterProcessLock writeLock() {code} h2. Error Handling It is strongly recommended that you add a {{ConnectionStateListener}} and watch for SUSPENDED and LOST state changes. If a SUSPENDED state is reported you cannot be certain that you still hold the lock unless you subsequently receive a RECONNECTED state. If a LOST state is reported it is certain that you no longer hold the lock. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/shared-semaphore.confluence000066400000000000000000000107021442004423600323500ustar00rootroot00000000000000h1. Shared Semaphore h2. Description A counting semaphore that works across JVMs. All processes in all JVMs that use the same lock path will achieve an inter\-process limited set of leases. Further, this semaphore is mostly "fair" \- each user will get a lease in the order requested (from ZK's point of view). There are two modes for determining the max leases for the semaphore. In the first mode the max leases is a convention maintained by the users of a given path. In the second mode a SharedCountReader is used as the method for semaphores of a given path to determine the max leases. If a SharedCountReader is not used, no internal checks are done to prevent Process A acting as if there are 10 leases and Process B acting as if there are 20. Therefore, make sure that all instances in all processes use the same numberOfLeases value. The various acquire methods return Lease objects that represent acquired leases. Clients must take care to close lease objects (ideally in a {{finally}} block) else the lease will be lost. However, if the client session drops (crash, etc.), any leases held by the client are automatically closed and made available to other clients. h2. Participating Classes * InterProcessSemaphoreV2 * Lease * SharedCountReader h2. Usage h3. Creating an InterProcessSemaphoreV2 {code} public InterProcessSemaphoreV2(CuratorFramework client, String path, int numberOfLeases) Parameters: client - client path - the path to lock numberOfLeases - the number of leases allowed by this semaphore {code} {code} public InterProcessSemaphoreV2(CuratorFramework client, String path, SharedCountReader count) Parameters: client - the client path - path for the semaphore count - the shared count to use for the max leases {code} h2. General Usage To acquire one lease/usage, use one of the acquire methods: {code} public Lease acquire() Acquire a lease. If no leases are available, this method blocks until either the maximum number of leases is increased or another client/process closes a lease. The client must close the lease when it is done with it. You should do this in a finally block. Returns: the new lease {code} {code} public Collection acquire(int qty) Acquire qty leases. If there are not enough leases available, this method blocks until either the maximum number of leases is increased enough or other clients/processes close enough leases. The client must close the leases when it is done with them. You should do this in a finally block. NOTE: You can use returnAll(Collection) for this. Parameters: qty - number of leases to acquire Returns: the new leases {code} {code} public Lease acquire(long time, TimeUnit unit) Acquire a lease. If no leases are available, this method blocks until either the maximum number of leases is increased or another client/process closes a lease. However, this method will only block to a maximum of the time parameters given. The client must close the lease when it is done with it. You should do this in a finally block. Parameters: time - time to wait unit - time unit Returns: the new lease or null if time ran out {code} {code} public Collection acquire(int qty, long time, TimeUnit unit) Acquire qty leases. If there are not enough leases available, this method blocks until either the maximum number of leases is increased enough or other clients/processes close enough leases. However, this method will only block to a maximum of the time parameters given. If time expires before all leases are acquired, the subset of acquired leases are automatically closed. The client must close the leases when it is done with them. You should do this in a finally block. NOTE: You can use returnAll(Collection) for this. Parameters: qty - number of leases to acquire time - time to wait unit - time unit {code} {{Lease}} instances can either be closed directly or you can use these convenience methods: {code} public void returnAll(Collection leases) public void returnLease(Lease lease) {code} h2. Error Handling It is strongly recommended that you add a {{ConnectionStateListener}} and watch for SUSPENDED and LOST state changes. If a SUSPENDED state is reported you cannot be certain that you still hold the lock unless you subsequently receive a RECONNECTED state. If a LOST state is reported it is certain that you no longer hold the lock. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/simple-distributed-queue.confluence000066400000000000000000000022111442004423600340500ustar00rootroot00000000000000h1. Simple Distributed Queue h2. *IMPORTANT* \- We recommend that you do NOT use ZooKeeper for Queues. Please see [[Tech Note 4|https://cwiki.apache.org/confluence/display/CURATOR/TN4]] for details. h2. Description A drop\-in replacement for the DistributedQueue that comes with the ZK distribution. h2. Participating Classes * SimpleDistributedQueue h2. Usage *Creating a SimpleDistributedQueue* {code} public SimpleDistributedQueue(CuratorFramework client, String path) Parameters: client - the client path - path to store queue nodes {code} *Add to the queue* {code} public boolean offer(byte[] data) throws Exception Inserts data into queue. Parameters: data - the data Returns: true if data was successfully added {code} *Take from the queue* {code} public byte[] take() throws Exception Removes the head of the queue and returns it, blocks until it succeeds. Returns: The former head of the queue {code} NOTE: see the Javadoc for additional methods h2. Error Handling It is strongly recommended that you add a {{ConnectionStateListener}} and watch for SUSPENDED and LOST state changes. curator-apache-curator-5.5.0/curator-recipes/src/site/confluence/tree-cache.confluence000066400000000000000000000025521442004423600311250ustar00rootroot00000000000000h1. Tree Cache h2. Description A utility that attempts to keep all data from all children of a ZK path locally cached. This class will watch the ZK path, respond to update/create/delete events, pull down the data, etc. You can register a listener that will get notified when changes occur. h2. Participating Classes * TreeCache * TreeCacheListener * TreeCacheEvent * ChildData h2. Usage h3. Creating a TreeCache {code} public TreeCache(CuratorFramework client, String path, boolean cacheData) Parameters: client - the client path - path to watch cacheData - if true, node contents are cached in addition to the stat {code} h2. General Usage The cache must be started by calling {{start()}}. Call {{close()}} when you are through with the cache. At any time, call {{getCurrentChildren()}} to get the current state of the cache. Alternatively, call {{getCurrentData()}} to get the data for a given path that's being monitored. You can also register to be notified when a change occurs by calling {{getListenable()}} and then: {code} public void addListener(TreeCacheListener listener) Add a change listener Parameters: listener - the listener {code} h2. Error Handling TreeCache instances internally monitor a {{ConnectionStateListener}}. If the connection state changes, the cache will receive messages detailing the change. curator-apache-curator-5.5.0/curator-recipes/src/site/site.xml000066400000000000000000000030261442004423600244240ustar00rootroot00000000000000 ]]> curator-apache-curator-5.5.0/curator-recipes/src/test/000077500000000000000000000000001442004423600227505ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/000077500000000000000000000000001442004423600236715ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/000077500000000000000000000000001442004423600244605ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/000077500000000000000000000000001442004423600257015ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/000077500000000000000000000000001442004423600273605ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/connection/000077500000000000000000000000001442004423600315175ustar00rootroot00000000000000TestThreadLocalRetryLoop.java000066400000000000000000000131251442004423600372070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/connection/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.connection; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.RetryLoop; import org.apache.curator.RetryPolicy; import org.apache.curator.RetrySleeper; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.ThreadUtils; import org.apache.zookeeper.KeeperException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class TestThreadLocalRetryLoop extends CuratorTestBase { private static final int retryCount = 4; private static final String backgroundThreadNameBase = "ignore-curator-background-thread"; @Test @DisplayName("Check for fix for CURATOR-559") public void testRecursingRetry() throws Exception { AtomicInteger count = new AtomicInteger(); try (CuratorFramework client = newClient(count)) { prep(client, count); doOperation(client); assertEquals(count.get(), retryCount + 1); // Curator's retry policy has been off by 1 since inception - we might consider fixing it someday } } @Test @DisplayName("Check for fix for CURATOR-559 with multiple threads") public void testThreadedRecursingRetry() throws Exception { final int threadQty = 4; ExecutorService executorService = Executors.newFixedThreadPool(threadQty); AtomicInteger count = new AtomicInteger(); try (CuratorFramework client = newClient(count)) { prep(client, count); for ( int i = 0; i < threadQty; ++i ) { executorService.submit(() -> doOperation(client)); } executorService.shutdown(); assertTrue(executorService.awaitTermination(timing.milliseconds(), TimeUnit.MILLISECONDS)); assertEquals(count.get(), threadQty * (retryCount + 1)); // Curator's retry policy has been off by 1 since inception - we might consider fixing it someday } } @Test public void testBadReleaseWithNoGet() { assertThrows(NullPointerException.class, ()-> { ThreadLocalRetryLoop retryLoopStack = new ThreadLocalRetryLoop(); retryLoopStack.release(); }); } private CuratorFramework newClient(AtomicInteger count) { RetryPolicy retryPolicy = makeRetryPolicy(count); return CuratorFrameworkFactory.builder().connectString(server.getConnectString()).connectionTimeoutMs(100).sessionTimeoutMs(100).retryPolicy(retryPolicy).threadFactory(ThreadUtils.newThreadFactory(backgroundThreadNameBase)).build(); } private void prep(CuratorFramework client, AtomicInteger count) throws Exception { client.start(); client.create().forPath("/test"); CountDownLatch lostLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener((__, newState) -> { if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } }); server.stop(); assertTrue(timing.awaitLatch(lostLatch)); count.set(0); // in case the server shutdown incremented the count } private Void doOperation(CuratorFramework client) throws Exception { try { RetryLoop.callWithRetry(client.getZookeeperClient(), () -> { client.checkExists().forPath("/hey"); return null; }); fail("Should have thrown an exception"); } catch ( KeeperException dummy ) { // correct } return null; } private RetryPolicy makeRetryPolicy(AtomicInteger count) { return new RetryNTimes(retryCount, 1) { @Override public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper) { if ( !Thread.currentThread().getName().contains(backgroundThreadNameBase) ) // if it does, it's Curator's background thread - don't count these { count.incrementAndGet(); } return super.allowRetry(retryCount, elapsedTimeMs, sleeper); } }; } } curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/000077500000000000000000000000001442004423600313555ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/client/000077500000000000000000000000001442004423600326335ustar00rootroot00000000000000TestBackgroundStates.java000066400000000000000000000135371442004423600375330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/client/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.client; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.nodes.PersistentEphemeralNode; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.TestingServer; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; // NOTE: these tests are in Framework as they use the PersistentEphemeralNode recipe public class TestBackgroundStates extends BaseClassForTests { @Test public void testListenersReconnectedIsOK() throws Exception { server.close(); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); PersistentEphemeralNode node = null; try { client.start(); node = new PersistentEphemeralNode(client, PersistentEphemeralNode.Mode.EPHEMERAL, "/abc/node", "hello".getBytes()); node.start(); final CountDownLatch connectedLatch = new CountDownLatch(1); final CountDownLatch reconnectedLatch = new CountDownLatch(1); final AtomicReference lastState = new AtomicReference(); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { lastState.set(newState); if ( newState == ConnectionState.CONNECTED ) { connectedLatch.countDown(); } if ( newState == ConnectionState.RECONNECTED ) { reconnectedLatch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); timing.sleepABit(); server = new TestingServer(server.getPort()); assertTrue(timing.awaitLatch(connectedLatch)); timing.sleepABit(); assertTrue(node.waitForInitialCreate(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); server.restart(); timing.sleepABit(); assertTrue(timing.awaitLatch(reconnectedLatch)); timing.sleepABit(); assertEquals(lastState.get(), ConnectionState.RECONNECTED); } finally { CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(node); } } @Test public void testConnectionStateListener() throws Exception { server.close(); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(timing.milliseconds())); try { client.start(); final BlockingQueue stateVector = Queues.newLinkedBlockingQueue(1); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { stateVector.offer(newState); } }; Timing waitingTiming = timing.forWaiting(); client.getConnectionStateListenable().addListener(listener); server = new TestingServer(server.getPort()); assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED); server.stop(); assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED); assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST); server.restart(); assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.RECONNECTED); server.close(); assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED); assertEquals(stateVector.poll(waitingTiming.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST); } finally { CloseableUtils.closeQuietly(client); } } }TestResetConnectionWithBackgroundFailure.java000066400000000000000000000107111442004423600435250ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/client/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.client; import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.leader.LeaderSelector; import org.apache.curator.framework.recipes.leader.LeaderSelectorListener; import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; public class TestResetConnectionWithBackgroundFailure extends BaseClassForTests { private final Logger log = LoggerFactory.getLogger(getClass()); @Test public void testConnectionStateListener() throws Exception { server.stop(); LeaderSelector selector = null; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); timing.sleepABit(); LeaderSelectorListener listenerLeader = new LeaderSelectorListenerAdapter() { @Override public void takeLeadership(CuratorFramework client) throws Exception { Thread.currentThread().join(); } }; selector = new LeaderSelector(client, "/leader", listenerLeader); selector.autoRequeue(); selector.start(); final BlockingQueue listenerSequence = Queues.newLinkedBlockingQueue(); ConnectionStateListener listener1 = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { listenerSequence.add(newState); } }; Timing forWaiting = timing.forWaiting(); client.getConnectionStateListenable().addListener(listener1); log.debug("Starting ZK server"); server.restart(); assertEquals(listenerSequence.poll(forWaiting.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED); log.debug("Stopping ZK server"); server.stop(); assertEquals(listenerSequence.poll(forWaiting.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED); assertEquals(listenerSequence.poll(forWaiting.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST); log.debug("Starting ZK server"); server.restart(); assertEquals(listenerSequence.poll(forWaiting.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.RECONNECTED); log.debug("Stopping ZK server"); server.close(); assertEquals(listenerSequence.poll(forWaiting.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED); assertEquals(listenerSequence.poll(forWaiting.milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } }curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/000077500000000000000000000000001442004423600330075ustar00rootroot00000000000000atomic/000077500000000000000000000000001442004423600342045ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipesTestCachedAtomicCounter.java000066400000000000000000000117531442004423600415620ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.junit.jupiter.api.Test; import java.util.concurrent.atomic.AtomicReference; public class TestCachedAtomicCounter extends BaseClassForTests { @Test public void testWithError() throws Exception { final int FACTOR = 100; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { AtomicValue value = new MutableAtomicValue(0L, (long)FACTOR, true); final AtomicReference> fakeValueRef = new AtomicReference>(value); DistributedAtomicLong dal = new DistributedAtomicLong(client, "/", null, null) { @Override public AtomicValue trySet(Long newValue) throws Exception { return fakeValueRef.get(); } @Override public AtomicValue get() throws Exception { return fakeValueRef.get(); } @Override public AtomicValue increment() throws Exception { return fakeValueRef.get(); } @Override public AtomicValue decrement() throws Exception { return fakeValueRef.get(); } @Override public AtomicValue add(Long delta) throws Exception { return fakeValueRef.get(); } @Override public AtomicValue subtract(Long delta) throws Exception { return fakeValueRef.get(); } @Override public void forceSet(Long newValue) throws Exception { } @Override public AtomicValue compareAndSet(Long expectedValue, Long newValue) throws Exception { return fakeValueRef.get(); } }; CachedAtomicLong cachedLong = new CachedAtomicLong(dal, FACTOR); for ( int i = 0; i < FACTOR; ++i ) { value = cachedLong.next(); assertTrue(value.succeeded()); assertEquals(value.preValue().longValue(), i); assertEquals(value.postValue().longValue(), i + 1); if ( i == 0 ) { MutableAtomicValue badValue = new MutableAtomicValue(0L, 0L); badValue.succeeded = false; fakeValueRef.set(badValue); } } value = cachedLong.next(); assertFalse(value.succeeded()); } finally { client.close(); } } @Test public void testBasic() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { DistributedAtomicLong dal = new DistributedAtomicLong(client, "/counter", new RetryOneTime(1)); CachedAtomicLong cachedLong = new CachedAtomicLong(dal, 100); for ( long i = 0; i < 200; ++i ) { AtomicValue value = cachedLong.next(); assertTrue(value.succeeded()); assertEquals(value.preValue().longValue(), i); assertEquals(value.postValue().longValue(), i + 1); } } finally { client.close(); } } } TestDistributedAtomicLong.java000066400000000000000000000302571442004423600421550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/atomic/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.atomic; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Lists; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.RetryOneTime; import org.apache.commons.math.stat.descriptive.SummaryStatistics; import org.apache.commons.math.stat.descriptive.SynchronizedSummaryStatistics; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; public class TestDistributedAtomicLong extends BaseClassForTests { @Test public void testCorruptedValue() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/counter", "foo".getBytes()); DistributedAtomicLong dal = new DistributedAtomicLong(client, "/counter", new RetryOneTime(1)); try { dal.get().postValue(); } catch ( BufferUnderflowException e ) { fail("", e); } catch ( BufferOverflowException e ) { fail("", e); } catch ( RuntimeException e ) { // correct } } finally { client.close(); } } @Test public void testCompareAndSetWithFreshInstance() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); DistributedAtomicLong dal = new DistributedAtomicLong(client, "/counter", new RetryOneTime(1)); AtomicValue result = dal.compareAndSet(0L, 1L); assertFalse(result.succeeded()); assertTrue(dal.initialize(0L)); result = dal.compareAndSet(0L, 1L); assertTrue(result.succeeded()); assertFalse(dal.initialize(0L)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCompareAndSet() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final AtomicBoolean doIncrement = new AtomicBoolean(false); DistributedAtomicLong dal = new DistributedAtomicLong(client, "/counter", new RetryOneTime(1)) { @Override public byte[] valueToBytes(Long newValue) { if ( doIncrement.get() ) { DistributedAtomicLong inc = new DistributedAtomicLong(client, "/counter", new RetryOneTime(1)); try { // this will force a bad version exception inc.increment(); } catch ( Exception e ) { throw new Error(e); } } return super.valueToBytes(newValue); } }; dal.forceSet(1L); assertTrue(dal.compareAndSet(1L, 5L).succeeded()); assertFalse(dal.compareAndSet(1L, 5L).succeeded()); doIncrement.set(true); assertFalse(dal.compareAndSet(5L, 10L).succeeded()); } finally { client.close(); } } @Test public void testForceSet() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final DistributedAtomicLong dal = new DistributedAtomicLong(client, "/counter", new RetryOneTime(1)); ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.submit ( new Callable() { @Override public Object call() throws Exception { for ( int i = 0; i < 1000; ++i ) { dal.increment(); Thread.sleep(10); } return null; } } ); executorService.submit ( new Callable() { @Override public Object call() throws Exception { for ( int i = 0; i < 1000; ++i ) { dal.forceSet(0L); Thread.sleep(10); } return null; } } ); assertTrue(dal.get().preValue() < 10); } finally { client.close(); } } @Test public void testSimulation() throws Exception { final int threadQty = 20; final int executionQty = 50; final AtomicInteger optimisticTries = new AtomicInteger(); final AtomicInteger promotedLockTries = new AtomicInteger(); final AtomicInteger failures = new AtomicInteger(); final AtomicInteger errors = new AtomicInteger(); final SummaryStatistics timingStats = new SynchronizedSummaryStatistics(); List> procs = Lists.newArrayList(); ExecutorService executorService = Executors.newFixedThreadPool(threadQty); for ( int i = 0; i < threadQty; ++i ) { Callable proc = new Callable() { @Override public Void call() throws Exception { doSimulation(executionQty, timingStats, optimisticTries, promotedLockTries, failures, errors); return null; } }; procs.add(executorService.submit(proc)); } for ( Future f : procs ) { f.get(); } System.out.println("OptimisticTries: " + optimisticTries.get()); System.out.println("PromotedLockTries: " + promotedLockTries.get()); System.out.println("Failures: " + failures.get()); System.out.println("Errors: " + errors.get()); System.out.println(); System.out.println("Avg time: " + timingStats.getMean()); System.out.println("Max time: " + timingStats.getMax()); System.out.println("Min time: " + timingStats.getMin()); System.out.println("Qty: " + timingStats.getN()); assertEquals(errors.get(), 0); assertTrue(optimisticTries.get() > 0); assertTrue(promotedLockTries.get() > 0); } @Test public void testBasic() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { DistributedAtomicLong dal = new DistributedAtomicLong(client, "/foo/bar/counter", new RetryOneTime(1)); AtomicValue value = dal.increment(); assertTrue(value.succeeded()); assertEquals(value.getStats().getOptimisticTries(), 1); assertEquals(value.getStats().getPromotedLockTries(), 0); assertEquals(value.preValue().longValue(), 0L); assertEquals(value.postValue().longValue(), 1L); value = dal.decrement(); assertTrue(value.succeeded()); assertEquals(value.getStats().getOptimisticTries(), 1); assertEquals(value.getStats().getPromotedLockTries(), 0); assertEquals(value.preValue().longValue(), 1L); assertEquals(value.postValue().longValue(), 0L); value = dal.add(10L); assertTrue(value.succeeded()); assertEquals(value.getStats().getOptimisticTries(), 1); assertEquals(value.getStats().getPromotedLockTries(), 0); assertEquals(value.preValue().longValue(), 0L); assertEquals(value.postValue().longValue(), 10L); value = dal.subtract(5L); assertTrue(value.succeeded()); assertEquals(value.getStats().getOptimisticTries(), 1); assertEquals(value.getStats().getPromotedLockTries(), 0); assertEquals(value.preValue().longValue(), 10L); assertEquals(value.postValue().longValue(), 5L); } finally { client.close(); } } private void doSimulation(int executionQty, SummaryStatistics timingStats, AtomicInteger optimisticTries, AtomicInteger promotedLockTries, AtomicInteger failures, AtomicInteger errors) throws Exception { Random random = new Random(); long previousValue = -1; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { RetryPolicy retryPolicy = new ExponentialBackoffRetry(3, 3); PromotedToLock.Builder builder = PromotedToLock.builder().lockPath("/lock").retryPolicy(retryPolicy); DistributedAtomicLong dal = new DistributedAtomicLong(client, "/counter", retryPolicy, builder.build()); for ( int i = 0; i < executionQty; ++i ) { Thread.sleep(random.nextInt(10)); long start = System.currentTimeMillis(); AtomicValue value = dal.increment(); long elapsed = System.currentTimeMillis() - start; timingStats.addValue(elapsed); if ( value.succeeded() ) { if ( value.postValue() <= previousValue ) { errors.incrementAndGet(); } previousValue = value.postValue(); } else { failures.incrementAndGet(); } optimisticTries.addAndGet(value.getStats().getOptimisticTries()); promotedLockTries.addAndGet(value.getStats().getPromotedLockTries()); } } finally { client.close(); } } } barriers/000077500000000000000000000000001442004423600345415ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipesTestDistributedBarrier.java000066400000000000000000000167601442004423600420470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/barriers/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.barriers; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Lists; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.zookeeper.KeeperException; import org.junit.jupiter.api.Test; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class TestDistributedBarrier extends BaseClassForTests { @Test public void testServerCrash() throws Exception { final int TIMEOUT = 1000; final CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).connectionTimeoutMs(TIMEOUT).retryPolicy(new RetryOneTime(1)).build(); try { client.start(); final DistributedBarrier barrier = new DistributedBarrier(client, "/barrier"); barrier.setBarrier(); final ExecutorService service = Executors.newSingleThreadExecutor(); Future future = service.submit ( new Callable() { @Override public Object call() throws Exception { Thread.sleep(TIMEOUT / 2); server.stop(); return null; } } ); barrier.waitOnBarrier(TIMEOUT * 2, TimeUnit.SECONDS); future.get(); fail(); } catch ( KeeperException.ConnectionLossException expected ) { // expected } finally { client.close(); } } @Test public void testMultiClient() throws Exception { CuratorFramework client1 = null; CuratorFramework client2 = null; try { { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); DistributedBarrier barrier = new DistributedBarrier(client, "/barrier"); barrier.setBarrier(); } finally { CloseableUtils.closeQuietly(client); } } client1 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client2 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); List> futures = Lists.newArrayList(); ExecutorService service = Executors.newCachedThreadPool(); for ( final CuratorFramework c : new CuratorFramework[]{client1, client2} ) { Future future = service.submit ( new Callable() { @Override public Object call() throws Exception { c.start(); DistributedBarrier barrier = new DistributedBarrier(c, "/barrier"); barrier.waitOnBarrier(10, TimeUnit.MILLISECONDS); return null; } } ); futures.add(future); } Thread.sleep(1000); { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); DistributedBarrier barrier = new DistributedBarrier(client, "/barrier"); barrier.removeBarrier(); } finally { CloseableUtils.closeQuietly(client); } } for ( Future f : futures ) { f.get(); } } finally { CloseableUtils.closeQuietly(client1); CloseableUtils.closeQuietly(client2); } } @Test public void testNoBarrier() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); final DistributedBarrier barrier = new DistributedBarrier(client, "/barrier"); assertTrue(barrier.waitOnBarrier(10, TimeUnit.SECONDS)); // just for grins, test the infinite wait ExecutorService service = Executors.newSingleThreadExecutor(); Future future = service.submit ( new Callable() { @Override public Object call() throws Exception { barrier.waitOnBarrier(); return ""; } } ); assertTrue(future.get(10, TimeUnit.SECONDS) != null); } finally { client.close(); } } @Test public void testBasic() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); final DistributedBarrier barrier = new DistributedBarrier(client, "/barrier"); barrier.setBarrier(); ExecutorService service = Executors.newSingleThreadExecutor(); service.submit ( new Callable() { @Override public Object call() throws Exception { Thread.sleep(1000); barrier.removeBarrier(); return null; } } ); assertTrue(barrier.waitOnBarrier(10, TimeUnit.SECONDS)); } finally { client.close(); } } } TestDistributedDoubleBarrier.java000066400000000000000000000227561442004423600432040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/barriers/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.barriers; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.junit.jupiter.api.Test; import java.io.Closeable; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class TestDistributedDoubleBarrier extends BaseClassForTests { private static final int QTY = 5; @Test public void testMultiClient() throws Exception { final Timing timing = new Timing(); final CountDownLatch postEnterLatch = new CountDownLatch(QTY); final CountDownLatch postLeaveLatch = new CountDownLatch(QTY); final AtomicInteger count = new AtomicInteger(0); final AtomicInteger max = new AtomicInteger(0); List> futures = Lists.newArrayList(); ExecutorService service = Executors.newCachedThreadPool(); for ( int i = 0; i < QTY; ++i ) { Future future = service.submit ( new Callable() { @Override public Void call() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); DistributedDoubleBarrier barrier = new DistributedDoubleBarrier(client, "/barrier", QTY); assertTrue(barrier.enter(timing.seconds(), TimeUnit.SECONDS)); synchronized(TestDistributedDoubleBarrier.this) { int thisCount = count.incrementAndGet(); if ( thisCount > max.get() ) { max.set(thisCount); } } postEnterLatch.countDown(); assertTrue(timing.awaitLatch(postEnterLatch)); assertEquals(count.get(), QTY); assertTrue(barrier.leave(timing.seconds(), TimeUnit.SECONDS)); count.decrementAndGet(); postLeaveLatch.countDown(); assertTrue(timing.awaitLatch(postEnterLatch)); } finally { CloseableUtils.closeQuietly(client); } return null; } } ); futures.add(future); } for ( Future f : futures ) { f.get(); } assertEquals(count.get(), 0); assertEquals(max.get(), QTY); } @Test public void testOverSubscribed() throws Exception { final Timing timing = new Timing(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); ExecutorService service = Executors.newCachedThreadPool(); ExecutorCompletionService completionService = new ExecutorCompletionService(service); try { client.start(); final Semaphore semaphore = new Semaphore(0); final CountDownLatch latch = new CountDownLatch(1); for ( int i = 0; i < (QTY + 1); ++i ) { completionService.submit ( new Callable() { @Override public Void call() throws Exception { DistributedDoubleBarrier barrier = new DistributedDoubleBarrier(client, "/barrier", QTY) { @Override protected List getChildrenForEntering() throws Exception { semaphore.release(); assertTrue(timing.awaitLatch(latch)); return super.getChildrenForEntering(); } }; assertTrue(barrier.enter(timing.seconds(), TimeUnit.SECONDS)); assertTrue(barrier.leave(timing.seconds(), TimeUnit.SECONDS)); return null; } } ); } assertTrue(semaphore.tryAcquire(QTY + 1, timing.seconds(), TimeUnit.SECONDS)); // wait until all QTY+1 barriers are trying to enter latch.countDown(); for ( int i = 0; i < (QTY + 1); ++i ) { completionService.take().get(); // to check for assertions } } finally { service.shutdown(); CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { final Timing timing = new Timing(); final List closeables = Lists.newArrayList(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { closeables.add(client); client.start(); final CountDownLatch postEnterLatch = new CountDownLatch(QTY); final CountDownLatch postLeaveLatch = new CountDownLatch(QTY); final AtomicInteger count = new AtomicInteger(0); final AtomicInteger max = new AtomicInteger(0); List> futures = Lists.newArrayList(); ExecutorService service = Executors.newCachedThreadPool(); for ( int i = 0; i < QTY; ++i ) { Future future = service.submit ( new Callable() { @Override public Void call() throws Exception { DistributedDoubleBarrier barrier = new DistributedDoubleBarrier(client, "/barrier", QTY); assertTrue(barrier.enter(timing.seconds(), TimeUnit.SECONDS)); synchronized(TestDistributedDoubleBarrier.this) { int thisCount = count.incrementAndGet(); if ( thisCount > max.get() ) { max.set(thisCount); } } postEnterLatch.countDown(); assertTrue(timing.awaitLatch(postEnterLatch)); assertEquals(count.get(), QTY); assertTrue(barrier.leave(10, TimeUnit.SECONDS)); count.decrementAndGet(); postLeaveLatch.countDown(); assertTrue(timing.awaitLatch(postLeaveLatch)); return null; } } ); futures.add(future); } for ( Future f : futures ) { f.get(); } assertEquals(count.get(), 0); assertEquals(max.get(), QTY); } finally { for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } } cache/000077500000000000000000000000001442004423600337735ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipesBaseTestTreeCache.java000066400000000000000000000166651442004423600401320ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class BaseTestTreeCache extends BaseClassForTests { CuratorFramework client; TreeCache cache; protected final AtomicBoolean hadBackgroundException = new AtomicBoolean(false); private final BlockingQueue events = new LinkedBlockingQueue(); private final Timing timing = new Timing(); /** * Automatically records all events into an easily testable event stream. */ final TreeCacheListener eventListener = new TreeCacheListener() { @Override public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { // Suppress any events related to /zookeeper paths if ( event.getData() != null && event.getData().getPath().startsWith("/zookeeper") ) { return; } events.add(event); } }; /** * Ensures that tests don't cause any background errors. */ final UnhandledErrorListener errorListener = new UnhandledErrorListener() { @Override public void unhandledError(String message, Throwable e) { hadBackgroundException.set(true); e.printStackTrace(System.err); } }; /** * Construct a TreeCache that records exceptions and automatically listens. */ protected TreeCache newTreeCacheWithListeners(CuratorFramework client, String path) { TreeCache result = new TreeCache(client, path); result.getListenable().addListener(eventListener); result.getUnhandledErrorListenable().addListener(errorListener); return result; } /** * Finish constructing a TreeCache that records exceptions and automatically listens. */ protected TreeCache buildWithListeners(TreeCache.Builder builder) { TreeCache result = builder.build(); result.getListenable().addListener(eventListener); result.getUnhandledErrorListenable().addListener(errorListener); return result; } @Override @BeforeEach public void setup() throws Exception { super.setup(); initCuratorFramework(); } void initCuratorFramework() { client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); client.getUnhandledErrorListenable().addListener(errorListener); } @Override @AfterEach public void teardown() throws Exception { try { try { assertFalse(hadBackgroundException.get(), "Background exceptions were thrown, see stderr for details"); assertNoMoreEvents(); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } finally { super.teardown(); } } /** * Asserts the event queue is empty. */ void assertNoMoreEvents() throws InterruptedException { timing.sleepABit(); assertTrue(events.isEmpty(), String.format("Expected no events, found %d; first event: %s", events.size(), events.peek())); } /** * Asserts the given event is next in the queue, and consumes it from the queue. */ TreeCacheEvent assertEvent(TreeCacheEvent.Type expectedType) throws InterruptedException { return assertEvent(expectedType, null); } /** * Asserts the given event is next in the queue, and consumes it from the queue. */ TreeCacheEvent assertEvent(TreeCacheEvent.Type expectedType, String expectedPath) throws InterruptedException { return assertEvent(expectedType, expectedPath, null); } /** * Asserts the given event is next in the queue, and consumes it from the queue. */ TreeCacheEvent assertEvent(TreeCacheEvent.Type expectedType, String expectedPath, byte[] expectedData) throws InterruptedException { return assertEvent(expectedType, expectedPath, expectedData, false); } TreeCacheEvent assertEvent(TreeCacheEvent.Type expectedType, String expectedPath, byte[] expectedData, boolean ignoreConnectionEvents) throws InterruptedException { TreeCacheEvent event = events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNotNull(event, String.format("Expected type: %s, path: %s", expectedType, expectedPath)); if ( ignoreConnectionEvents ) { if ( (event.getType() == TreeCacheEvent.Type.CONNECTION_SUSPENDED) || (event.getType() == TreeCacheEvent.Type.CONNECTION_LOST) || (event.getType() == TreeCacheEvent.Type.CONNECTION_RECONNECTED) ) { return assertEvent(expectedType, expectedPath, expectedData, ignoreConnectionEvents); } } String message = event.toString(); assertEquals(event.getType(), expectedType, message); if ( expectedPath == null ) { assertNull(event.getData(), message); } else { assertNotNull(event.getData(), message); assertEquals(event.getData().getPath(), expectedPath, message); } if ( expectedData != null ) { assertArrayEquals(event.getData().getData(), expectedData, message); } if ( event.getType() == TreeCacheEvent.Type.NODE_UPDATED) { assertNotNull(event.getOldData()); } else { assertNull(event.getOldData()); } return event; } } TestCuratorCache.java000066400000000000000000000177341442004423600400550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.apache.curator.framework.recipes.cache.CuratorCache.Options.DO_NOT_CLEAR_ON_CLOSE; import static org.apache.curator.framework.recipes.cache.CuratorCacheListener.builder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; @Tag(CuratorTestBase.zk36Group) public class TestCuratorCache extends CuratorTestBase { @Test public void testUpdateWhenNotCachingData() throws Exception // mostly copied from TestPathChildrenCache { CuratorCacheStorage storage = new StandardCuratorCacheStorage(false); try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { client.start(); final CountDownLatch updatedLatch = new CountDownLatch(1); final CountDownLatch addedLatch = new CountDownLatch(1); client.create().creatingParentsIfNeeded().forPath("/test"); try (CuratorCache cache = CuratorCache.builder(client, "/test").withStorage(storage).build()) { cache.listenable().addListener(builder().forChanges((__, ___) -> updatedLatch.countDown()).build()); cache.listenable().addListener(builder().forCreates(__ -> addedLatch.countDown()).build()); cache.start(); client.create().forPath("/test/foo", "first".getBytes()); assertTrue(timing.awaitLatch(addedLatch)); client.setData().forPath("/test/foo", "something new".getBytes()); assertTrue(timing.awaitLatch(updatedLatch)); } } } @Test public void testAfterInitialized() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { client.start(); client.create().creatingParentsIfNeeded().forPath("/test"); client.create().creatingParentsIfNeeded().forPath("/test/one"); client.create().creatingParentsIfNeeded().forPath("/test/one/two"); client.create().creatingParentsIfNeeded().forPath("/test/one/two/three"); try (CuratorCache cache = CuratorCache.build(client, "/test")) { CountDownLatch initializedLatch = new CountDownLatch(1); AtomicInteger eventCount = new AtomicInteger(0); CuratorCacheListener listener = new CuratorCacheListener() { @Override public void event(Type type, ChildData oldData, ChildData data) { eventCount.incrementAndGet(); } @Override public void initialized() { initializedLatch.countDown(); } }; cache.listenable().addListener(builder().forAll(listener).afterInitialized().build()); cache.start(); assertTrue(timing.awaitLatch(initializedLatch)); assertEquals(initializedLatch.getCount(), 0); assertEquals(cache.size(), 4); assertTrue(cache.get("/test").isPresent()); assertTrue(cache.get("/test/one").isPresent()); assertTrue(cache.get("/test/one/two").isPresent()); assertTrue(cache.get("/test/one/two/three").isPresent()); } } } @Test public void testListenerBuilder() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { client.start(); try (CuratorCache cache = CuratorCache.build(client, "/test")) { Semaphore all = new Semaphore(0); Semaphore deletes = new Semaphore(0); Semaphore changes = new Semaphore(0); Semaphore creates = new Semaphore(0); Semaphore createsAndChanges = new Semaphore(0); CuratorCacheListener listener = builder().forAll((__, ___, ____) -> all.release()).forDeletes(__ -> deletes.release()).forChanges((__, ___) -> changes.release()).forCreates(__ -> creates.release()).forCreatesAndChanges((__, ___) -> createsAndChanges.release()).build(); cache.listenable().addListener(listener); cache.start(); client.create().forPath("/test"); assertTrue(timing.acquireSemaphore(all, 1)); assertTrue(timing.acquireSemaphore(creates, 1)); assertTrue(timing.acquireSemaphore(createsAndChanges, 1)); assertEquals(changes.availablePermits(), 0); assertEquals(deletes.availablePermits(), 0); client.setData().forPath("/test", "new".getBytes()); assertTrue(timing.acquireSemaphore(all, 1)); assertTrue(timing.acquireSemaphore(changes, 1)); assertTrue(timing.acquireSemaphore(createsAndChanges, 1)); assertEquals(creates.availablePermits(), 0); assertEquals(deletes.availablePermits(), 0); client.delete().forPath("/test"); assertTrue(timing.acquireSemaphore(all, 1)); assertTrue(timing.acquireSemaphore(deletes, 1)); assertEquals(creates.availablePermits(), 0); assertEquals(changes.availablePermits(), 0); assertEquals(createsAndChanges.availablePermits(), 0); } } } @Test public void testClearOnClose() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { CuratorCacheStorage storage; client.start(); try ( CuratorCache cache = CuratorCache.builder(client, "/test").withOptions(DO_NOT_CLEAR_ON_CLOSE).build() ) { cache.start(); storage = ((CuratorCacheImpl)cache).storage(); client.create().forPath("/test", "foo".getBytes()); client.create().forPath("/test/bar", "bar".getBytes()); timing.sleepABit(); } assertEquals(storage.size(), 2); try ( CuratorCache cache = CuratorCache.build(client, "/test") ) { cache.start(); storage = ((CuratorCacheImpl)cache).storage(); timing.sleepABit(); } assertEquals(storage.size(), 0); } } } TestCuratorCacheBridge.java000066400000000000000000000051031442004423600411550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.Compatibility; import org.junit.jupiter.api.Test; public class TestCuratorCacheBridge extends CuratorTestBase { @Test public void testImplementationSelection() { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) { CuratorCacheBridge cache = CuratorCache.bridgeBuilder(client, "/foo").build(); if ( Compatibility.hasPersistentWatchers() ) { assertTrue(cache instanceof CuratorCacheImpl); assertTrue(cache.isCuratorCache()); } else { assertTrue(cache instanceof CompatibleCuratorCacheBridge); assertFalse(cache.isCuratorCache()); } } } @Test public void testForceTreeCache() { System.setProperty("curator-cache-bridge-force-tree-cache", "true"); try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) { CuratorCacheBridge cache = CuratorCache.bridgeBuilder(client, "/foo").build(); assertTrue(cache instanceof CompatibleCuratorCacheBridge); assertFalse(cache.isCuratorCache()); } finally { System.clearProperty("curator-cache-bridge-force-tree-cache"); } } } TestCuratorCacheConsistency.java000066400000000000000000000325621442004423600422730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.apache.curator.framework.recipes.cache.CuratorCache.Options.DO_NOT_CLEAR_ON_CLOSE; import static org.apache.curator.framework.recipes.cache.CuratorCacheListener.builder; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.ZKPaths; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.time.Duration; import java.time.Instant; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * Randomly create nodes in a tree while a set of CuratorCaches listens. Afterwards, validate * that the caches contain the same values as ZK itself */ @Tag(CuratorTestBase.zk36Group) public class TestCuratorCacheConsistency extends CuratorTestBase { private final Logger log = LoggerFactory.getLogger(getClass()); private final ThreadLocalRandom random = ThreadLocalRandom.current(); private static final Duration testLength = Duration.ofSeconds(30); private static final Duration thirdOfTestLength = Duration.ofMillis(testLength.toMillis() / 3); private static final Duration sleepLength = Duration.ofMillis(5); private static final int nodesPerLevel = 10; private static final int clusterSize = 5; private static final int maxServerKills = 2; private static final String BASE_PATH = "/test"; private class Client implements Closeable { private final CuratorFramework client; private final CuratorCache cache; private final int index; private final Map listenerDataMap = new HashMap<>(); Client(int index, String connectionString, AtomicReference errorSignal) { this.index = index; client = buildClient(connectionString); cache = CuratorCache.builder(client, BASE_PATH).withOptions(DO_NOT_CLEAR_ON_CLOSE).withExceptionHandler(errorSignal::set).build(); // listenerDataMap is a local data map that will hold values sent by listeners // this way, the listener code can be tested for validity and consistency CuratorCacheListener listener = builder().forCreates(node -> { ChildData previous = listenerDataMap.put(node.getPath(), node); if ( previous != null ) { errorSignal.set(new Exception(String.format("Client: %d - Create for existing node: %s", index, node.getPath()))); } }).forChanges((oldNode, node) -> { ChildData previous = listenerDataMap.put(node.getPath(), node); if ( (previous == null) || !Arrays.equals(previous.getData(), oldNode.getData()) ) { errorSignal.set(new Exception(String.format("Client: %d - Bad old value for change node: %s", index, node.getPath()))); } }).forDeletes(node -> { ChildData previous = listenerDataMap.remove(node.getPath()); if ( previous == null ) { errorSignal.set(new Exception(String.format("Client: %d - Delete for non-existent node: %s", index, node.getPath()))); } }).build(); cache.listenable().addListener(listener); } void start() { client.start(); cache.start(); } @Override public void close() { cache.close(); client.close(); } } @Test public void testConsistencyAfterSimulation() throws Exception { int clientQty = random.nextInt(10, 20); int maxDepth = random.nextInt(5, 10); log.info("clientQty: {}, maxDepth: {}", clientQty, maxDepth); List clients = Collections.emptyList(); Map actualTree; AtomicReference errorSignal = new AtomicReference<>(); try (TestingCluster cluster = new TestingCluster(clusterSize)) { cluster.start(); initializeBasePath(cluster); try { clients = buildClients(cluster, clientQty, errorSignal); workLoop(cluster, clients, maxDepth, errorSignal); log.info("Test complete - sleeping to allow events to complete"); timing.sleepABit(); } finally { clients.forEach(Client::close); } actualTree = buildActual(cluster); } log.info("client qty: {}", clientQty); Map> errorsList = clients.stream() .map(client -> findErrors(client, actualTree)) .filter(errorsEntry -> !errorsEntry.getValue().isEmpty()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); if ( !errorsList.isEmpty() ) { log.error("{} clients had errors", errorsList.size()); errorsList.forEach((index, errorList) -> { log.error("Client {}", index); errorList.forEach(log::error); log.error(""); }); fail("Errors found"); } } // build a data map recursively from the actual values in ZK private Map buildActual(TestingCluster cluster) { Map actual = new HashMap<>(); try (CuratorFramework client = buildClient(cluster.getConnectString())) { client.start(); buildActual(client, actual, BASE_PATH); } return actual; } private void buildActual(CuratorFramework client, Map actual, String fromPath) { try { byte[] bytes = client.getData().forPath(fromPath); actual.put(fromPath, new String(bytes)); client.getChildren().forPath(fromPath).forEach(child -> buildActual(client, actual, ZKPaths.makePath(fromPath, child))); } catch ( Exception e ) { fail("", e); } } private List buildClients(TestingCluster cluster, int clientQty, AtomicReference errorSignal) { return IntStream.range(0, clientQty) .mapToObj(index -> new Client(index, cluster.getConnectString(), errorSignal)) .peek(Client::start) .collect(Collectors.toList()); } private void initializeBasePath(TestingCluster cluster) throws Exception { try (CuratorFramework client = buildClient(cluster.getConnectString())) { client.start(); client.create().forPath(BASE_PATH, "".getBytes()); } } private void workLoop(TestingCluster cluster, List clients, int maxDepth, AtomicReference errorSignal) throws Exception { Instant start = Instant.now(); Instant lastServerKill = Instant.now(); int serverKillIndex = 0; while ( true ) { Duration elapsed = Duration.between(start, Instant.now()); if ( elapsed.compareTo(testLength) >= 0 ) { break; } Exception errorSignalException = errorSignal.get(); if ( errorSignalException != null ) { fail("A client's error handler was called", errorSignalException); } Duration elapsedFromLastServerKill = Duration.between(lastServerKill, Instant.now()); if ( elapsedFromLastServerKill.compareTo(thirdOfTestLength) >= 0 ) { lastServerKill = Instant.now(); if ( serverKillIndex < maxServerKills ) { doKillServer(cluster, serverKillIndex++); } } int thisDepth = random.nextInt(0, maxDepth); String thisPath = randomPath(thisDepth); CuratorFramework client = randomClient(clients); if ( random.nextBoolean() ) { doDelete(client, thisPath); } else { doChange(client, thisPath); } Thread.sleep(sleepLength.toMillis()); } } private void doChange(CuratorFramework client, String thisPath) { try { String thisData = Long.toString(random.nextLong()); client.create().orSetData().creatingParentsIfNeeded().forPath(thisPath, thisData.getBytes()); } catch ( Exception e ) { fail("Could not create/set: " + thisPath); } } private void doDelete(CuratorFramework client, String thisPath) { if ( thisPath.equals(BASE_PATH) ) { return; } try { client.delete().quietly().deletingChildrenIfNeeded().forPath(thisPath); } catch ( Exception e ) { fail("Could not delete: " + thisPath); } } private void doKillServer(TestingCluster cluster, int serverKillIndex) throws Exception { log.info("Killing server {}", serverKillIndex); InstanceSpec killSpec = new ArrayList<>(cluster.getInstances()).get(serverKillIndex); cluster.killServer(killSpec); } private CuratorFramework randomClient(List clients) { return clients.get(random.nextInt(clients.size())).client; } private Map.Entry> findErrors(Client client, Map tree) { CuratorCacheStorage storage = ((CuratorCacheImpl)client.cache).storage(); List errors = new ArrayList<>(); if ( tree.size() != storage.size() ) { errors.add(String.format("Size mismatch. Expected: %d - Actual: %d", tree.size(), storage.size())); } tree.keySet().forEach(path -> { if ( !storage.get(path).isPresent() ) { errors.add(String.format("Path %s in master but not client", path)); } }); storage.stream().forEach(data -> { String treeValue = tree.get(data.getPath()); if ( treeValue != null ) { if ( !treeValue.equals(new String(data.getData())) ) { errors.add(String.format("Data at %s is not the same", data.getPath())); } ChildData listenersMapData = client.listenerDataMap.get(data.getPath()); if ( listenersMapData == null ) { errors.add(String.format("listenersMap missing data at: %s", data.getPath())); } else if ( !treeValue.equals(new String(listenersMapData.getData())) ) { errors.add(String.format("Data at %s in listenersMap is not the same", data.getPath())); } } else { errors.add(String.format("Path %s in client but not master", data.getPath())); } }); client.listenerDataMap.keySet().forEach(path -> { if ( !storage.get(path).isPresent() ) { errors.add(String.format("Path %s in listenersMap but not storage", path)); } }); return new AbstractMap.SimpleEntry<>(client.index, errors); } private String randomPath(int depth) { StringBuilder str = new StringBuilder(BASE_PATH); while ( depth-- > 0 ) { int levelNodeName = random.nextInt(nodesPerLevel); str.append("/").append(levelNodeName); } return str.toString(); } @Override protected void createServer() { // do nothing - we'll be using TestingCluster instead } private CuratorFramework buildClient(String connectionString) { ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(100, 100); return CuratorFrameworkFactory.newClient(connectionString, timing.session(), timing.connection(), retryPolicy); } }TestCuratorCacheEdges.java000066400000000000000000000161611442004423600410160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.apache.curator.framework.recipes.cache.CuratorCache.Options.DO_NOT_CLEAR_ON_CLOSE; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.compatibility.CuratorTestBase; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; @Tag(CuratorTestBase.zk36Group) public class TestCuratorCacheEdges extends CuratorTestBase { @Test public void testReconnectConsistency() throws Exception { final byte[] first = "one".getBytes(); final byte[] second = "two".getBytes(); try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { client.start(); client.create().forPath("/root", first); client.create().forPath("/root/1", first); client.create().forPath("/root/2", first); client.create().forPath("/root/1/11", first); client.create().forPath("/root/1/12", first); client.create().forPath("/root/1/13", first); client.create().forPath("/root/2/21", first); client.create().forPath("/root/2/22", first); CuratorCacheStorage storage = CuratorCacheStorage.standard(); try (CuratorCache cache = CuratorCache.builder(client, "/root").withStorage(storage).withOptions(DO_NOT_CLEAR_ON_CLOSE).build()) { CountDownLatch latch = new CountDownLatch(1); cache.listenable().addListener(CuratorCacheListener.builder().forInitialized(latch::countDown).build()); cache.start(); assertTrue(timing.awaitLatch(latch)); } // we now have a storage loaded with the initial nodes created // simulate nodes changing during a partition client.delete().forPath("/root/2/21"); client.delete().forPath("/root/2/22"); client.delete().forPath("/root/2"); client.setData().forPath("/root", second); client.create().forPath("/root/1/11/111", second); client.create().forPath("/root/1/11/111/1111", second); client.create().forPath("/root/1/11/111/1112", second); client.create().forPath("/root/1/13/131", second); client.create().forPath("/root/1/13/132", second); client.create().forPath("/root/1/13/132/1321", second); try (CuratorCache cache = CuratorCache.builder(client, "/root").withStorage(storage).withOptions(DO_NOT_CLEAR_ON_CLOSE).build()) { CountDownLatch latch = new CountDownLatch(1); cache.listenable().addListener(CuratorCacheListener.builder().forInitialized(latch::countDown).build()); cache.start(); assertTrue(timing.awaitLatch(latch)); } assertEquals(storage.size(), 11); assertArrayEquals(storage.get("/root").map(ChildData::getData).orElse(null), second); assertArrayEquals(storage.get("/root/1").map(ChildData::getData).orElse(null), first); assertArrayEquals(storage.get("/root/1/11").map(ChildData::getData).orElse(null), first); assertArrayEquals(storage.get("/root/1/11/111").map(ChildData::getData).orElse(null), second); assertArrayEquals(storage.get("/root/1/11/111/1111").map(ChildData::getData).orElse(null), second); assertArrayEquals(storage.get("/root/1/11/111/1112").map(ChildData::getData).orElse(null), second); assertArrayEquals(storage.get("/root/1/12").map(ChildData::getData).orElse(null), first); assertArrayEquals(storage.get("/root/1/13").map(ChildData::getData).orElse(null), first); assertArrayEquals(storage.get("/root/1/13/131").map(ChildData::getData).orElse(null), second); assertArrayEquals(storage.get("/root/1/13/132").map(ChildData::getData).orElse(null), second); assertArrayEquals(storage.get("/root/1/13/132/1321").map(ChildData::getData).orElse(null), second); } } @Test public void testServerLoss() throws Exception // mostly copied from TestPathChildrenCacheInCluster { try (TestingCluster cluster = new TestingCluster(3)) { cluster.start(); try (CuratorFramework client = CuratorFrameworkFactory.newClient(cluster.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { client.start(); client.create().creatingParentsIfNeeded().forPath("/test"); try (CuratorCache cache = CuratorCache.build(client, "/test")) { cache.start(); CountDownLatch reconnectLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener((__, newState) -> { if ( newState == ConnectionState.RECONNECTED ) { reconnectLatch.countDown(); } }); CountDownLatch latch = new CountDownLatch(3); cache.listenable().addListener((__, ___, ____) -> latch.countDown()); client.create().forPath("/test/one"); client.create().forPath("/test/two"); client.create().forPath("/test/three"); assertTrue(timing.awaitLatch(latch)); InstanceSpec connectionInstance = cluster.findConnectionInstance(client.getZookeeperClient().getZooKeeper()); cluster.killServer(connectionInstance); assertTrue(timing.awaitLatch(reconnectLatch)); timing.sleepABit(); assertEquals(cache.stream().count(), 4); } } } } } TestCuratorCacheEventOrdering.java000066400000000000000000000036231442004423600425410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.test.compatibility.CuratorTestBase; import org.junit.jupiter.api.Tag; import java.util.concurrent.BlockingQueue; @Tag(CuratorTestBase.zk36Group) public class TestCuratorCacheEventOrdering extends TestEventOrdering { @Override protected int getActualQty(CuratorCache cache) { return cache.size(); } @Override protected CuratorCache newCache(CuratorFramework client, String path, BlockingQueue events) { CuratorCache cache = CuratorCache.build(client, path); cache.listenable().addListener((type, oldNode, node) -> { if ( type == CuratorCacheListener.Type.NODE_CREATED ) { events.add(new Event(EventType.ADDED, node.getPath())); } else if ( type == CuratorCacheListener.Type.NODE_DELETED ) { events.add(new Event(EventType.DELETED, oldNode.getPath())); } }); cache.start(); return cache; } } TestCuratorCacheWrappers.java000066400000000000000000000227361442004423600415770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.apache.curator.framework.recipes.cache.CuratorCache.Options.SINGLE_NODE_CACHE; import static org.apache.curator.framework.recipes.cache.CuratorCacheAccessor.parentPathFilter; import static org.apache.curator.framework.recipes.cache.CuratorCacheListener.builder; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.ImmutableSet; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.AbstractMap; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.concurrent.CopyOnWriteArrayList; @Tag(CuratorTestBase.zk36Group) public class TestCuratorCacheWrappers extends CuratorTestBase { @Test public void testPathChildrenCache() throws Exception // copied from TestPathChildrenCache#testBasics() { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { client.start(); client.create().forPath("/test"); final CopyOnWriteArrayList eventsTrace = new CopyOnWriteArrayList<>(); final BlockingQueue events = new LinkedBlockingQueue<>(); try (CuratorCache cache = CuratorCache.build(client, "/test")) { PathChildrenCacheListener listener = (__, event) -> { eventsTrace.add(event); if ( event.getData().getPath().equals("/test/one") ) { events.offer(event.getType()); } }; cache.listenable().addListener(builder().forPathChildrenCache("/test", client, listener).build()); cache.start(); client.create().forPath("/test/one", "hey there".getBytes()); assertEquals(events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_ADDED); client.setData().forPath("/test/one", "sup!".getBytes()); assertEquals(events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_UPDATED); assertEquals(new String(cache.get("/test/one").orElseThrow(AssertionError::new).getData()), "sup!"); client.delete().forPath("/test/one"); assertEquals(events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_REMOVED); // Please note that there is not guarantee on the order of events // For instance INITIALIZED event can appear in the middle of the observed sequence. for (PathChildrenCacheEvent event : eventsTrace) { switch (event.getType()) { case CHILD_ADDED: case CHILD_REMOVED: case CHILD_UPDATED: assertEquals("/test/one", event.getData().getPath()); break; case INITIALIZED: assertNull(event.getData()); break; default: fail(); } } assertEquals(eventsTrace.size(), 4); } } } @Test public void testTreeCache() throws Exception // copied from TestTreeCache#testBasics() { BaseTestTreeCache treeCacheBase = new BaseTestTreeCache(); try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { client.start(); client.create().forPath("/test"); try (CuratorCache cache = CuratorCache.build(client, "/test")) { cache.listenable().addListener(builder().forTreeCache(client, treeCacheBase.eventListener).build()); cache.start(); treeCacheBase.assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); treeCacheBase.assertEvent(TreeCacheEvent.Type.INITIALIZED); assertEquals(toMap(cache.stream().filter(parentPathFilter("/test"))).keySet(), ImmutableSet.of()); assertEquals(cache.stream().filter(parentPathFilter("/t")).count(), 0); assertEquals(cache.stream().filter(parentPathFilter("/testing")).count(), 0); client.create().forPath("/test/one", "hey there".getBytes()); treeCacheBase.assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertEquals(toMap(cache.stream().filter(parentPathFilter("/test"))).keySet(), ImmutableSet.of("/test/one")); assertEquals(new String(cache.get("/test/one").orElseThrow(AssertionError::new).getData()), "hey there"); assertEquals(toMap(cache.stream().filter(parentPathFilter("/test/one"))).keySet(), ImmutableSet.of()); assertEquals(cache.stream().filter(parentPathFilter("/test/o")).count(), 0); assertEquals(cache.stream().filter(parentPathFilter("/test/onely")).count(), 0); client.setData().forPath("/test/one", "sup!".getBytes()); treeCacheBase.assertEvent(TreeCacheEvent.Type.NODE_UPDATED, "/test/one"); assertEquals(toMap(cache.stream().filter(parentPathFilter("/test"))).keySet(), ImmutableSet.of("/test/one")); assertEquals(new String(cache.get("/test/one").orElseThrow(AssertionError::new).getData()), "sup!"); client.delete().forPath("/test/one"); treeCacheBase.assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/one", "sup!".getBytes()); assertEquals(toMap(cache.stream().filter(parentPathFilter("/test"))).keySet(), ImmutableSet.of()); } } } @Test public void testNodeCache() throws Exception // copied from TestNodeCache#testBasics() { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)) ) { client.start(); client.create().forPath("/test"); try (CuratorCache cache = CuratorCache.build(client, "/test/node", SINGLE_NODE_CACHE)) { Supplier getRootData = () -> cache.get("/test/node").orElseThrow(() -> new AssertionError("is not present")); cache.start(); final Semaphore semaphore = new Semaphore(0); cache.listenable().addListener(builder().forNodeCache(semaphore::release).build()); try { getRootData.get(); fail("Should have thrown"); } catch ( AssertionError expected ) { // expected } client.create().forPath("/test/node", "a".getBytes()); assertTrue(timing.acquireSemaphore(semaphore)); assertArrayEquals(getRootData.get().getData(), "a".getBytes()); client.setData().forPath("/test/node", "b".getBytes()); assertTrue(timing.acquireSemaphore(semaphore)); assertArrayEquals(getRootData.get().getData(), "b".getBytes()); client.delete().forPath("/test/node"); assertTrue(timing.acquireSemaphore(semaphore)); try { getRootData.get(); fail("Should have thrown"); } catch ( AssertionError expected ) { // expected } } } } private static Map toMap(Stream stream) { return stream.map(data -> new AbstractMap.SimpleEntry<>(data.getPath(), data)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } } TestEventOrdering.java000066400000000000000000000147601442004423600402610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.KeeperException; import org.junit.jupiter.api.Test; import java.io.Closeable; import java.util.List; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public abstract class TestEventOrdering extends BaseClassForTests { private final Timing2 timing = new Timing2(); private final long start = System.currentTimeMillis(); private static final int THREAD_QTY = 100; private static final int ITERATIONS = 100; private static final int NODE_QTY = 10; public enum EventType { ADDED, DELETED } public static class Event { public final EventType eventType; public final String path; public final long time = System.currentTimeMillis(); public Event(EventType eventType, String path) { this.eventType = eventType; this.path = path; } } @Test public void testEventOrdering() throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(THREAD_QTY); BlockingQueue events = Queues.newLinkedBlockingQueue(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); T cache = null; try { client.start(); client.create().forPath("/root"); cache = newCache(client, "/root", events); final Random random = new Random(); final Callable task = new Callable() { @Override public Void call() throws Exception { for ( int i = 0; i < ITERATIONS; ++i ) { String node = "/root/" + random.nextInt(NODE_QTY); try { switch ( random.nextInt(3) ) { default: case 0: client.create().forPath(node); break; case 1: client.setData().forPath(node, "new".getBytes()); break; case 2: client.delete().forPath(node); break; } } catch ( KeeperException ignore ) { // ignore } } return null; } }; final CountDownLatch latch = new CountDownLatch(THREAD_QTY); for ( int i = 0; i < THREAD_QTY; ++i ) { Callable wrapped = new Callable() { @Override public Void call() throws Exception { try { return task.call(); } finally { latch.countDown(); } } }; executorService.submit(wrapped); } assertTrue(timing.awaitLatch(latch)); timing.sleepABit(); List localEvents = Lists.newArrayList(); int eventSuggestedQty = 0; while ( events.size() > 0 ) { Event event = timing.takeFromQueue(events); localEvents.add(event); eventSuggestedQty += (event.eventType == EventType.ADDED) ? 1 : -1; } int actualQty = getActualQty(cache); assertEquals(actualQty, eventSuggestedQty, String.format("actual %s expected %s:\n %s", actualQty, eventSuggestedQty, asString(localEvents))); } finally { executorService.shutdownNow(); //noinspection ThrowFromFinallyBlock executorService.awaitTermination(timing.milliseconds(), TimeUnit.MILLISECONDS); CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(client); } } protected abstract int getActualQty(T cache); protected abstract T newCache(CuratorFramework client, String path, BlockingQueue events) throws Exception; private String asString(List events) { int qty = 0; StringBuilder str = new StringBuilder(); for ( Event event : events ) { qty += (event.eventType == EventType.ADDED) ? 1 : -1; str.append(event.eventType).append(" ").append(event.path).append(" @ ").append(event.time - start).append(' ').append(qty); str.append("\n"); } return str.toString(); } } TestNodeCache.java000066400000000000000000000234671442004423600373230ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestNodeCache extends BaseClassForTests { @Test public void testDeleteThenCreate() throws Exception { NodeCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.create().creatingParentsIfNeeded().forPath("/test/foo", "one".getBytes()); final AtomicReference error = new AtomicReference(); client.getUnhandledErrorListenable().addListener ( new UnhandledErrorListener() { @Override public void unhandledError(String message, Throwable e) { error.set(e); } } ); final Semaphore semaphore = new Semaphore(0); cache = new NodeCache(client, "/test/foo"); cache.getListenable().addListener ( new NodeCacheListener() { @Override public void nodeChanged() throws Exception { semaphore.release(); } } ); cache.start(true); assertArrayEquals(cache.getCurrentData().getData(), "one".getBytes()); client.delete().forPath("/test/foo"); assertTrue(semaphore.tryAcquire(1, 10, TimeUnit.SECONDS)); client.create().forPath("/test/foo", "two".getBytes()); assertTrue(semaphore.tryAcquire(1, 10, TimeUnit.SECONDS)); Throwable t = error.get(); if ( t != null ) { fail("Assert", t); } assertArrayEquals(cache.getCurrentData().getData(), "two".getBytes()); cache.close(); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testRebuildAgainstOtherProcesses() throws Exception { Timing2 timing2 = new Timing2(); NodeCache cache = null; final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/test"); client.create().forPath("/test/snafu", "original".getBytes()); final CountDownLatch latch = new CountDownLatch(1); cache = new NodeCache(client, "/test/snafu"); cache.getListenable().addListener ( new NodeCacheListener() { @Override public void nodeChanged() throws Exception { latch.countDown(); } } ); cache.rebuildTestExchanger = new Exchanger(); ExecutorService service = Executors.newSingleThreadExecutor(); final NodeCache finalCache = cache; Future future = service.submit ( new Callable() { @Override public Object call() throws Exception { finalCache.rebuildTestExchanger.exchange(new Object(), timing2.forWaiting().seconds(), TimeUnit.SECONDS); // simulate another process updating the node while we're rebuilding client.setData().forPath("/test/snafu", "other".getBytes()); ChildData currentData = finalCache.getCurrentData(); assertNotNull(currentData); finalCache.rebuildTestExchanger.exchange(new Object(), timing2.forWaiting().seconds(), TimeUnit.SECONDS); return null; } } ); cache.start(false); future.get(); assertTrue(timing2.awaitLatch(latch)); assertNotNull(cache.getCurrentData()); assertArrayEquals(cache.getCurrentData().getData(), "other".getBytes()); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testKilledSession() throws Exception { NodeCache cache = null; Timing timing = new Timing(); CuratorFramework client = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); client.create().creatingParentsIfNeeded().forPath("/test/node", "start".getBytes()); cache = new NodeCache(client, "/test/node"); cache.start(true); final CountDownLatch latch = new CountDownLatch(1); cache.getListenable().addListener ( new NodeCacheListener() { @Override public void nodeChanged() throws Exception { latch.countDown(); } } ); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); Thread.sleep(timing.multiple(1.5).session()); assertArrayEquals(cache.getCurrentData().getData(), "start".getBytes()); client.setData().forPath("/test/node", "new data".getBytes()); assertTrue(timing.awaitLatch(latch)); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testBasics() throws Exception { NodeCache cache = null; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/test"); cache = new NodeCache(client, "/test/node"); cache.start(true); final Semaphore semaphore = new Semaphore(0); cache.getListenable().addListener ( new NodeCacheListener() { @Override public void nodeChanged() throws Exception { semaphore.release(); } } ); assertNull(cache.getCurrentData()); client.create().forPath("/test/node", "a".getBytes()); assertTrue(timing.acquireSemaphore(semaphore)); assertArrayEquals(cache.getCurrentData().getData(), "a".getBytes()); client.setData().forPath("/test/node", "b".getBytes()); assertTrue(timing.acquireSemaphore(semaphore)); assertArrayEquals(cache.getCurrentData().getData(), "b".getBytes()); client.delete().forPath("/test/node"); assertTrue(timing.acquireSemaphore(semaphore)); assertNull(cache.getCurrentData()); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } } TestPathChildrenCache.java000066400000000000000000001347321442004423600410010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.ExecuteCalledWatchingExecutorService; import org.apache.curator.test.TestingServer; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestPathChildrenCache extends BaseClassForTests { @Test public void testParentContainerMissing() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); PathChildrenCache cache = new PathChildrenCache(client, "/a/b/test", true); try { client.start(); CountDownLatch startedLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener((__, newState) -> { if ( newState == ConnectionState.CONNECTED ) { startedLatch.countDown(); } }); assertTrue(timing.awaitLatch(startedLatch)); final BlockingQueue events = Queues.newLinkedBlockingQueue(); PathChildrenCacheListener listener = new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { events.add(event.getType()); } }; cache.getListenable().addListener(listener); cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); assertEquals(events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.INITIALIZED); client.create().forPath("/a/b/test/one"); client.create().forPath("/a/b/test/two"); assertEquals(events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CHILD_ADDED); assertEquals(events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CHILD_ADDED); client.delete().forPath("/a/b/test/one"); client.delete().forPath("/a/b/test/two"); client.delete().forPath("/a/b/test"); assertEquals(events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CHILD_REMOVED); assertEquals(events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CHILD_REMOVED); timing.sleepABit(); client.create().creatingParentContainersIfNeeded().forPath("/a/b/test/new"); assertEquals(events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CHILD_ADDED); } finally { CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(client); } } @Test public void testInitializedEvenIfChildDeleted() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); PathChildrenCache cache = new PathChildrenCache(client, "/a/b/test", true) { @Override void getDataAndStat(final String fullPath) throws Exception { // before installing a data watcher on the child, let's delete this child client.delete().forPath("/a/b/test/one"); super.getDataAndStat(fullPath); } }; Timing timing = new Timing(); try { client.start(); final CountDownLatch cacheInitialized = new CountDownLatch(1); PathChildrenCacheListener listener = new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.INITIALIZED ) { cacheInitialized.countDown(); } } }; cache.getListenable().addListener(listener); client.create().creatingParentsIfNeeded().forPath("/a/b/test/one"); cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); assertTrue(timing.awaitLatch(cacheInitialized)); assertEquals(cache.getCurrentData().size(), 0); } finally { CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(client); } } @Test public void testWithBadConnect() throws Exception { final int serverPort = server.getPort(); server.close(); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), 1000, 1000, new RetryOneTime(1)); try { client.start(); final CountDownLatch ensurePathLatch = new CountDownLatch(1); PathChildrenCache cache = new PathChildrenCache(client, "/", true) { @Override protected void ensurePath() throws Exception { try { super.ensurePath(); } catch ( Exception e ) { ensurePathLatch.countDown(); throw e; } } }; final CountDownLatch addedLatch = new CountDownLatch(1); PathChildrenCacheListener listener = new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED && event.getData().getPath().equals("/baz")) { addedLatch.countDown(); } } }; cache.getListenable().addListener(listener); cache.start(); assertTrue(timing.awaitLatch(ensurePathLatch)); final CountDownLatch connectedLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if(newState == ConnectionState.CONNECTED) { connectedLatch.countDown(); } } }); server = new TestingServer(serverPort, true); assertTrue(timing.awaitLatch(connectedLatch)); client.create().creatingParentContainersIfNeeded().forPath("/baz", new byte[]{1, 2, 3}); assertNotNull(client.checkExists().forPath("/baz"), "/baz does not exist"); assertTrue(timing.awaitLatch(addedLatch)); assertNotNull(cache.getCurrentData("/baz"), "cache doesn't see /baz"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testPostInitializedForEmpty() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final CountDownLatch latch = new CountDownLatch(1); cache = new PathChildrenCache(client, "/test", true); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.INITIALIZED ) { latch.countDown(); } } } ); cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); assertTrue(timing.awaitLatch(latch)); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testAsyncInitialPopulation() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/test"); client.create().forPath("/test/one", "hey there".getBytes()); final BlockingQueue events = new LinkedBlockingQueue(); cache = new PathChildrenCache(client, "/test", true); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { events.offer(event); } } ); cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); PathChildrenCacheEvent event = events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertEquals(event.getType(), PathChildrenCacheEvent.Type.CHILD_ADDED); event = events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertEquals(event.getType(), PathChildrenCacheEvent.Type.INITIALIZED); assertEquals(event.getInitialData().size(), 1); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testChildrenInitialized() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/test"); cache = new PathChildrenCache(client, "/test", true); final CountDownLatch addedLatch = new CountDownLatch(3); final CountDownLatch initLatch = new CountDownLatch(1); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ) { addedLatch.countDown(); } else if ( event.getType() == PathChildrenCacheEvent.Type.INITIALIZED ) { initLatch.countDown(); } } } ); client.create().forPath("/test/1", "1".getBytes()); client.create().forPath("/test/2", "2".getBytes()); client.create().forPath("/test/3", "3".getBytes()); cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); assertTrue(timing.awaitLatch(addedLatch)); assertTrue(timing.awaitLatch(initLatch)); assertEquals(cache.getCurrentData().size(), 3); assertArrayEquals(cache.getCurrentData().get(0).getData(), "1".getBytes()); assertArrayEquals(cache.getCurrentData().get(1).getData(), "2".getBytes()); assertArrayEquals(cache.getCurrentData().get(2).getData(), "3".getBytes()); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testChildrenInitializedNormal() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/test"); cache = new PathChildrenCache(client, "/test", true); final CountDownLatch addedLatch = new CountDownLatch(3); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { assertNotEquals(event.getType(), PathChildrenCacheEvent.Type.INITIALIZED); if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ) { addedLatch.countDown(); } } } ); client.create().forPath("/test/1", "1".getBytes()); client.create().forPath("/test/2", "2".getBytes()); client.create().forPath("/test/3", "3".getBytes()); cache.start(PathChildrenCache.StartMode.NORMAL); assertTrue(timing.awaitLatch(addedLatch)); assertEquals(cache.getCurrentData().size(), 3); assertArrayEquals(cache.getCurrentData().get(0).getData(), "1".getBytes()); assertArrayEquals(cache.getCurrentData().get(1).getData(), "2".getBytes()); assertArrayEquals(cache.getCurrentData().get(2).getData(), "3".getBytes()); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testUpdateWhenNotCachingData() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final CountDownLatch updatedLatch = new CountDownLatch(1); final CountDownLatch addedLatch = new CountDownLatch(1); client.create().creatingParentsIfNeeded().forPath("/test"); cache = new PathChildrenCache(client, "/test", false); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED ) { updatedLatch.countDown(); } else if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ) { addedLatch.countDown(); } } } ); cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); client.create().forPath("/test/foo", "first".getBytes()); assertTrue(timing.awaitLatch(addedLatch)); client.setData().forPath("/test/foo", "something new".getBytes()); assertTrue(timing.awaitLatch(updatedLatch)); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testEnsurePath() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { try ( PathChildrenCache cache = new PathChildrenCache(client, "/one/two/three", false) ) { cache.start(); timing.sleepABit(); try { client.create().forPath("/one/two/three/four"); } catch ( KeeperException.NoNodeException e ) { fail("Path should exist", e); } } timing.sleepABit(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testDeleteThenCreate() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/test"); client.create().forPath("/test/foo", "one".getBytes()); final AtomicReference error = new AtomicReference(); client.getUnhandledErrorListenable().addListener ( new UnhandledErrorListener() { @Override public void unhandledError(String message, Throwable e) { error.set(e); } } ); final CountDownLatch removedLatch = new CountDownLatch(1); final CountDownLatch postRemovedLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1); try ( PathChildrenCache cache = new PathChildrenCache(client, "/test", true) ) { cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED ) { removedLatch.countDown(); assertTrue(postRemovedLatch.await(10, TimeUnit.SECONDS)); } else { try { assertArrayEquals(event.getData().getData(), "two".getBytes()); } finally { dataLatch.countDown(); } } } } ); cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); client.delete().forPath("/test/foo"); assertTrue(timing.awaitLatch(removedLatch)); client.create().forPath("/test/foo", "two".getBytes()); postRemovedLatch.countDown(); assertTrue(timing.awaitLatch(dataLatch)); Throwable t = error.get(); if ( t != null ) { fail("Assert", t); } } } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testRebuildAgainstOtherProcesses() throws Exception { Timing timing = new Timing(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/test"); client.create().forPath("/test/foo"); client.create().forPath("/test/bar"); client.create().forPath("/test/snafu", "original".getBytes()); final CountDownLatch addedLatch = new CountDownLatch(2); try ( final PathChildrenCache cache = new PathChildrenCache(client, "/test", true) ) { cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ) { if ( event.getData().getPath().equals("/test/test") ) { addedLatch.countDown(); } } else if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED ) { if ( event.getData().getPath().equals("/test/snafu") ) { addedLatch.countDown(); } } } } ); cache.rebuildTestExchanger = new Exchanger(); ExecutorService service = Executors.newSingleThreadExecutor(); final AtomicReference deletedPath = new AtomicReference(); Future future = service.submit ( new Callable() { @Override public Object call() throws Exception { cache.rebuildTestExchanger.exchange(new Object()); // simulate another process adding a node while we're rebuilding client.create().forPath("/test/test"); List currentData = cache.getCurrentData(); assertTrue(currentData.size() > 0); // simulate another process removing a node while we're rebuilding client.delete().forPath(currentData.get(0).getPath()); deletedPath.set(currentData.get(0).getPath()); cache.rebuildTestExchanger.exchange(new Object()); ChildData childData = null; while ( childData == null ) { childData = cache.getCurrentData("/test/snafu"); Thread.sleep(1000); } assertArrayEquals(childData.getData(), "original".getBytes()); client.setData().forPath("/test/snafu", "grilled".getBytes()); cache.rebuildTestExchanger.exchange(new Object()); return null; } } ); cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); future.get(); assertTrue(timing.awaitLatch(addedLatch)); assertNotNull(cache.getCurrentData("/test/test")); assertNull(cache.getCurrentData(deletedPath.get())); assertArrayEquals(cache.getCurrentData("/test/snafu").getData(), "grilled".getBytes()); } } finally { TestCleanState.closeAndTestClean(client); } } // see https://github.com/Netflix/curator/issues/27 - was caused by not comparing old->new data @Test public void testIssue27() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/base"); client.create().forPath("/base/a"); client.create().forPath("/base/b"); client.create().forPath("/base/c"); client.getChildren().forPath("/base"); final List events = Lists.newArrayList(); final Semaphore semaphore = new Semaphore(0); cache = new PathChildrenCache(client, "/base", true); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { events.add(event.getType()); semaphore.release(); } } ); cache.start(); assertTrue(timing.acquireSemaphore(semaphore, 3)); client.delete().forPath("/base/a"); assertTrue(timing.acquireSemaphore(semaphore, 1)); client.create().forPath("/base/a"); assertTrue(timing.acquireSemaphore(semaphore, 1)); List expected = Lists.newArrayList ( PathChildrenCacheEvent.Type.CHILD_ADDED, PathChildrenCacheEvent.Type.CHILD_ADDED, PathChildrenCacheEvent.Type.CHILD_ADDED, PathChildrenCacheEvent.Type.CHILD_REMOVED, PathChildrenCacheEvent.Type.CHILD_ADDED ); assertEquals(expected, events); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } // test Issue 27 using new rebuild() method @Test public void testIssue27Alt() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/base"); client.create().forPath("/base/a"); client.create().forPath("/base/b"); client.create().forPath("/base/c"); client.getChildren().forPath("/base"); final List events = Lists.newArrayList(); final Semaphore semaphore = new Semaphore(0); cache = new PathChildrenCache(client, "/base", true); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { events.add(event.getType()); semaphore.release(); } } ); cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); client.delete().forPath("/base/a"); assertTrue(timing.acquireSemaphore(semaphore, 1)); client.create().forPath("/base/a"); assertTrue(timing.acquireSemaphore(semaphore, 1)); List expected = Lists.newArrayList ( PathChildrenCacheEvent.Type.CHILD_REMOVED, PathChildrenCacheEvent.Type.CHILD_ADDED ); assertEquals(expected, events); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testKilledSession() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); client.create().forPath("/test"); cache = new PathChildrenCache(client, "/test", true); cache.start(); final CountDownLatch childAddedLatch = new CountDownLatch(1); final CountDownLatch lostLatch = new CountDownLatch(1); final CountDownLatch reconnectedLatch = new CountDownLatch(1); final CountDownLatch removedLatch = new CountDownLatch(1); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ) { childAddedLatch.countDown(); } else if ( event.getType() == PathChildrenCacheEvent.Type.CONNECTION_LOST ) { lostLatch.countDown(); } else if ( event.getType() == PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED ) { reconnectedLatch.countDown(); } else if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED ) { removedLatch.countDown(); } } } ); client.create().withMode(CreateMode.EPHEMERAL).forPath("/test/me", "data".getBytes()); assertTrue(timing.awaitLatch(childAddedLatch)); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertTrue(timing.awaitLatch(lostLatch)); assertTrue(timing.awaitLatch(reconnectedLatch)); assertTrue(timing.awaitLatch(removedLatch)); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testModes() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/test"); for ( boolean cacheData : new boolean[]{false, true} ) { internalTestMode(client, cacheData); client.delete().forPath("/test/one"); client.delete().forPath("/test/two"); } } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testRebuildNode() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().creatingParentsIfNeeded().forPath("/test/one", "one".getBytes()); final CountDownLatch latch = new CountDownLatch(1); final AtomicInteger counter = new AtomicInteger(); final Semaphore semaphore = new Semaphore(1); cache = new PathChildrenCache(client, "/test", true) { @Override void getDataAndStat(String fullPath) throws Exception { semaphore.acquire(); counter.incrementAndGet(); super.getDataAndStat(fullPath); latch.countDown(); } }; cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); assertTrue(timing.awaitLatch(latch)); int saveCounter = counter.get(); client.setData().forPath("/test/one", "alt".getBytes()); cache.rebuildNode("/test/one"); assertArrayEquals(cache.getCurrentData("/test/one").getData(), "alt".getBytes()); assertEquals(saveCounter, counter.get()); semaphore.release(1000); timing.sleepABit(); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } private void internalTestMode(CuratorFramework client, boolean cacheData) throws Exception { try ( PathChildrenCache cache = new PathChildrenCache(client, "/test", cacheData) ) { final CountDownLatch latch = new CountDownLatch(2); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ) { latch.countDown(); } } } ); cache.start(); client.create().forPath("/test/one", "one".getBytes()); client.create().forPath("/test/two", "two".getBytes()); assertTrue(latch.await(10, TimeUnit.SECONDS)); for ( ChildData data : cache.getCurrentData() ) { if ( cacheData ) { assertNotNull(data.getData()); assertNotNull(data.getStat()); } else { assertNull(data.getData()); assertNotNull(data.getStat()); } } } } @Test public void testBasics() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/test"); final BlockingQueue events = new LinkedBlockingQueue(); try ( PathChildrenCache cache = new PathChildrenCache(client, "/test", true) ) { cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getData().getPath().equals("/test/one") ) { events.offer(event.getType()); } } } ); cache.start(); client.create().forPath("/test/one", "hey there".getBytes()); assertEquals(events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_ADDED); client.setData().forPath("/test/one", "sup!".getBytes()); assertEquals(events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_UPDATED); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "sup!"); client.delete().forPath("/test/one"); assertEquals(events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_REMOVED); } } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testBasicsOnTwoCachesWithSameExecutor() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/test"); final BlockingQueue events = new LinkedBlockingQueue(); final ExecutorService exec = Executors.newSingleThreadExecutor(); try ( PathChildrenCache cache = new PathChildrenCache(client, "/test", true, false, exec) ) { cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getData().getPath().equals("/test/one") ) { events.offer(event.getType()); } } } ); cache.start(); final BlockingQueue events2 = new LinkedBlockingQueue(); try ( PathChildrenCache cache2 = new PathChildrenCache(client, "/test", true, false, exec) ) { cache2.getListenable().addListener( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getData().getPath().equals("/test/one") ) { events2.offer(event.getType()); } } } ); cache2.start(); client.create().forPath("/test/one", "hey there".getBytes()); assertEquals(events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_ADDED); assertEquals(events2.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_ADDED); client.setData().forPath("/test/one", "sup!".getBytes()); assertEquals(events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_UPDATED); assertEquals(events2.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_UPDATED); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "sup!"); assertEquals(new String(cache2.getCurrentData("/test/one").getData()), "sup!"); client.delete().forPath("/test/one"); assertEquals(events.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_REMOVED); assertEquals(events2.poll(timing.forWaiting().seconds(), TimeUnit.SECONDS), PathChildrenCacheEvent.Type.CHILD_REMOVED); } } } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testDeleteNodeAfterCloseDoesntCallExecutor() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/test"); final ExecuteCalledWatchingExecutorService exec = new ExecuteCalledWatchingExecutorService(Executors.newSingleThreadExecutor()); try ( PathChildrenCache cache = new PathChildrenCache(client, "/test", true, false, exec) ) { cache.start(); client.create().forPath("/test/one", "hey there".getBytes()); cache.rebuild(); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there"); assertTrue(exec.isExecuteCalled()); exec.setExecuteCalled(false); } assertFalse(exec.isExecuteCalled()); client.delete().forPath("/test/one"); timing.sleepABit(); assertFalse(exec.isExecuteCalled()); } finally { TestCleanState.closeAndTestClean(client); } } /** * Tests the case where there's an outstanding operation being executed when the cache is * shut down. See CURATOR-121, this was causing misleading warning messages to be logged. * @throws Exception */ @Test public void testInterruptedOperationOnShutdown() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), 30000, 30000, new RetryOneTime(1)); client.start(); try { final CountDownLatch latch = new CountDownLatch(1); try ( final PathChildrenCache cache = new PathChildrenCache(client, "/test", false) { @Override protected void handleException(Throwable e) { latch.countDown(); } } ) { cache.start(); cache.offerOperation(new Operation() { @Override public void invoke() throws Exception { Thread.sleep(5000); } }); Thread.sleep(1000); } latch.await(5, TimeUnit.SECONDS); assertTrue(latch.getCount() == 1, "Unexpected exception occurred"); } finally { TestCleanState.closeAndTestClean(client); } } } TestPathChildrenCacheEventOrdering.java000066400000000000000000000042731442004423600434710ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import java.util.concurrent.BlockingQueue; public class TestPathChildrenCacheEventOrdering extends TestEventOrdering { @Override protected int getActualQty(PathChildrenCache cache) { return cache.getCurrentData().size(); } @Override protected PathChildrenCache newCache(CuratorFramework client, String path, final BlockingQueue events) throws Exception { PathChildrenCache cache = new PathChildrenCache(client, path, false); PathChildrenCacheListener listener = new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ) { events.add(new Event(EventType.ADDED, event.getData().getPath())); } if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED ) { events.add(new Event(EventType.DELETED, event.getData().getPath())); } } }; cache.getListenable().addListener(listener); cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); return cache; } } TestPathChildrenCacheInCluster.java000066400000000000000000000166611442004423600426320ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Queues; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.Timing; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestPathChildrenCacheInCluster extends BaseClassForTests { @Test @Disabled // this test is very flakey - it needs to be re-written at some point public void testMissedDelete() throws Exception { Timing timing = new Timing(); PathChildrenCache cache = null; CuratorFramework client1 = null; CuratorFramework client2 = null; TestingCluster cluster = createAndStartCluster(3); try { // client 1 only connects to 1 server InstanceSpec client1Instance = cluster.getInstances().iterator().next(); client1 = CuratorFrameworkFactory.newClient(client1Instance.getConnectString(), 1000, 1000, new RetryOneTime(1)); cache = new PathChildrenCache(client1, "/test", true); final BlockingQueue events = Queues.newLinkedBlockingQueue(); PathChildrenCacheListener listener = new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { events.add(event.getType()); } }; cache.getListenable().addListener(listener); client2 = CuratorFrameworkFactory.newClient(cluster.getConnectString(), 1000, 1000, new RetryOneTime(1)); client1.start(); client2.start(); cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); assertEquals(events.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED); assertEquals(events.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.INITIALIZED); client2.create().creatingParentsIfNeeded().forPath("/test/node", "first".getBytes()); assertEquals(events.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CHILD_ADDED); cluster.killServer(client1Instance); assertEquals(events.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED); assertEquals(events.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CONNECTION_LOST); client2.delete().forPath("/test/node"); client2.create().forPath("/test/node", "second".getBytes()); cluster.restartServer(client1Instance); assertEquals(events.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED); assertEquals(events.poll(timing.milliseconds(), TimeUnit.MILLISECONDS), PathChildrenCacheEvent.Type.CHILD_UPDATED); // "/test/node" is different - should register as updated } finally { CloseableUtils.closeQuietly(client1); CloseableUtils.closeQuietly(client2); CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(cluster); } } @Test public void testServerLoss() throws Exception { Timing timing = new Timing(); CuratorFramework client = null; PathChildrenCache cache = null; TestingCluster cluster = createAndStartCluster(3); try { client = CuratorFrameworkFactory.newClient(cluster.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); client.create().creatingParentsIfNeeded().forPath("/test"); cache = new PathChildrenCache(client, "/test", false); cache.start(); final CountDownLatch resetLatch = new CountDownLatch(1); final CountDownLatch reconnectLatch = new CountDownLatch(1); final AtomicReference latch = new AtomicReference(new CountDownLatch(3)); cache.getListenable().addListener ( new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( event.getType() == PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED ) { resetLatch.countDown(); } else if ( event.getType() == PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED ) { reconnectLatch.countDown(); } else if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED ) { latch.get().countDown(); } } } ); client.create().forPath("/test/one"); client.create().forPath("/test/two"); client.create().forPath("/test/three"); assertTrue(latch.get().await(10, TimeUnit.SECONDS)); InstanceSpec connectionInstance = cluster.findConnectionInstance(client.getZookeeperClient().getZooKeeper()); cluster.killServer(connectionInstance); assertTrue(timing.awaitLatch(reconnectLatch)); assertEquals(cache.getCurrentData().size(), 3); } finally { CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(cluster); } } } TestTreeCache.java000066400000000000000000000632321442004423600373270ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.ImmutableSet; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.recipes.cache.TreeCacheEvent.Type; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.CreateMode; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestTreeCache extends BaseTestTreeCache { @Test public void testSelector() throws Exception { client.create().forPath("/root"); client.create().forPath("/root/n1-a"); client.create().forPath("/root/n1-b"); client.create().forPath("/root/n1-b/n2-a"); client.create().forPath("/root/n1-b/n2-b"); client.create().forPath("/root/n1-b/n2-b/n3-a"); client.create().forPath("/root/n1-c"); client.create().forPath("/root/n1-d"); TreeCacheSelector selector = new TreeCacheSelector() { @Override public boolean traverseChildren(String fullPath) { return !fullPath.equals("/root/n1-b/n2-b"); } @Override public boolean acceptChild(String fullPath) { return !fullPath.equals("/root/n1-c"); } }; cache = buildWithListeners(TreeCache.newBuilder(client, "/root").setSelector(selector)); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/root"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/root/n1-a"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/root/n1-b"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/root/n1-d"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/root/n1-b/n2-a"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/root/n1-b/n2-b"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); } @Test public void testStartup() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/1", "one".getBytes()); client.create().forPath("/test/2", "two".getBytes()); client.create().forPath("/test/3", "three".getBytes()); client.create().forPath("/test/2/sub", "two-sub".getBytes()); cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/1", "one".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/2", "two".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/3", "three".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/2/sub", "two-sub".getBytes()); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of("1", "2", "3")); assertEquals(cache.getCurrentChildren("/test/1").keySet(), ImmutableSet.of()); assertEquals(cache.getCurrentChildren("/test/2").keySet(), ImmutableSet.of("sub")); assertNull(cache.getCurrentChildren("/test/non_exist")); } @Test public void testCreateParents() throws Exception { cache = newTreeCacheWithListeners(client, "/one/two/three"); cache.start(); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); assertNull(client.checkExists().forPath("/one/two/three")); cache.close(); cache = buildWithListeners(TreeCache.newBuilder(client, "/one/two/three").setCreateParentNodes(true)); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/one/two/three"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); assertNotNull(client.checkExists().forPath("/one/two/three")); } @Test public void testStartEmpty() throws Exception { cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.create().forPath("/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertNoMoreEvents(); } @Test public void testStartEmptyDeeper() throws Exception { cache = newTreeCacheWithListeners(client, "/test/foo/bar"); cache.start(); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.create().creatingParentsIfNeeded().forPath("/test/foo"); assertNoMoreEvents(); client.create().forPath("/test/foo/bar"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo/bar"); assertNoMoreEvents(); } @Test public void testDepth0() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/1", "one".getBytes()); client.create().forPath("/test/2", "two".getBytes()); client.create().forPath("/test/3", "three".getBytes()); client.create().forPath("/test/2/sub", "two-sub".getBytes()); cache = buildWithListeners(TreeCache.newBuilder(client, "/test").setMaxDepth(0)); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of()); assertNull(cache.getCurrentData("/test/1")); assertNull(cache.getCurrentChildren("/test/1")); assertNull(cache.getCurrentData("/test/non_exist")); } @Test public void testDepth1() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/1", "one".getBytes()); client.create().forPath("/test/2", "two".getBytes()); client.create().forPath("/test/3", "three".getBytes()); client.create().forPath("/test/2/sub", "two-sub".getBytes()); cache = buildWithListeners(TreeCache.newBuilder(client, "/test").setMaxDepth(1)); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/1", "one".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/2", "two".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/3", "three".getBytes()); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of("1", "2", "3")); assertEquals(cache.getCurrentChildren("/test/1").keySet(), ImmutableSet.of()); assertEquals(cache.getCurrentChildren("/test/2").keySet(), ImmutableSet.of()); assertNull(cache.getCurrentData("/test/2/sub")); assertNull(cache.getCurrentChildren("/test/2/sub")); assertNull(cache.getCurrentChildren("/test/non_exist")); } @Test public void testDepth1Deeper() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/foo"); client.create().forPath("/test/foo/bar"); client.create().forPath("/test/foo/bar/1", "one".getBytes()); client.create().forPath("/test/foo/bar/2", "two".getBytes()); client.create().forPath("/test/foo/bar/3", "three".getBytes()); client.create().forPath("/test/foo/bar/2/sub", "two-sub".getBytes()); cache = buildWithListeners(TreeCache.newBuilder(client, "/test/foo/bar").setMaxDepth(1)); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo/bar"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo/bar/1", "one".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo/bar/2", "two".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo/bar/3", "three".getBytes()); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); } @Test public void testAsyncInitialPopulation() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/one", "hey there".getBytes()); cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); } @Test public void testFromRoot() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/one", "hey there".getBytes()); cache = newTreeCacheWithListeners(client, "/"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); assertTrue(cache.getCurrentChildren("/").keySet().contains("test")); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of("one")); assertEquals(cache.getCurrentChildren("/test/one").keySet(), ImmutableSet.of()); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there"); } @Test public void testFromRootWithDepth() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/one", "hey there".getBytes()); cache = buildWithListeners(TreeCache.newBuilder(client, "/").setMaxDepth(1)); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); assertTrue(cache.getCurrentChildren("/").keySet().contains("test")); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of()); assertNull(cache.getCurrentData("/test/one")); assertNull(cache.getCurrentChildren("/test/one")); } @Test public void testWithNamespace() throws Exception { client.create().forPath("/outer"); client.create().forPath("/outer/foo"); client.create().forPath("/outer/test"); client.create().forPath("/outer/test/one", "hey there".getBytes()); cache = newTreeCacheWithListeners(client.usingNamespace("outer"), "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of("one")); assertEquals(cache.getCurrentChildren("/test/one").keySet(), ImmutableSet.of()); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there"); } @Test public void testWithNamespaceAtRoot() throws Exception { client.create().forPath("/outer"); client.create().forPath("/outer/foo"); client.create().forPath("/outer/test"); client.create().forPath("/outer/test/one", "hey there".getBytes()); cache = newTreeCacheWithListeners(client.usingNamespace("outer"), "/"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/foo"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); assertEquals(cache.getCurrentChildren("/").keySet(), ImmutableSet.of("foo", "test")); assertEquals(cache.getCurrentChildren("/foo").keySet(), ImmutableSet.of()); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of("one")); assertEquals(cache.getCurrentChildren("/test/one").keySet(), ImmutableSet.of()); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there"); } @Test public void testSyncInitialPopulation() throws Exception { cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.create().forPath("/test"); client.create().forPath("/test/one", "hey there".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertNoMoreEvents(); } @Test public void testChildrenInitialized() throws Exception { client.create().forPath("/test", "".getBytes()); client.create().forPath("/test/1", "1".getBytes()); client.create().forPath("/test/2", "2".getBytes()); client.create().forPath("/test/3", "3".getBytes()); cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/1"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/2"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/3"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertNoMoreEvents(); } @Test public void testUpdateWhenNotCachingData() throws Exception { client.create().forPath("/test"); cache = buildWithListeners(TreeCache.newBuilder(client, "/test").setCacheData(false)); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.create().forPath("/test/foo", "first".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); client.setData().forPath("/test/foo", "something new".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_UPDATED, "/test/foo"); assertNoMoreEvents(); assertNotNull(cache.getCurrentData("/test/foo")); // No byte data querying the tree because we're not caching data. assertNull(cache.getCurrentData("/test/foo").getData()); } @Test public void testDeleteThenCreate() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/foo", "one".getBytes()); cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.delete().forPath("/test/foo"); assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/foo", "one".getBytes()); client.create().forPath("/test/foo", "two".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); client.delete().forPath("/test/foo"); assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/foo", "two".getBytes()); client.create().forPath("/test/foo", "two".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); assertNoMoreEvents(); } @Test public void testDeleteThenCreateRoot() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/foo", "one".getBytes()); cache = newTreeCacheWithListeners(client, "/test/foo"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.delete().forPath("/test/foo"); assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/foo"); client.create().forPath("/test/foo", "two".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); client.delete().forPath("/test/foo"); assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/foo"); client.create().forPath("/test/foo", "two".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); assertNoMoreEvents(); } @Test public void testKilledSession() throws Exception { client.create().forPath("/test"); cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.create().forPath("/test/foo", "foo".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/foo"); client.create().withMode(CreateMode.EPHEMERAL).forPath("/test/me", "data".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/me"); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertEvent(TreeCacheEvent.Type.INITIALIZED, null, null, true); assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/me", "data".getBytes(), true); assertNoMoreEvents(); } @Test public void testBasics() throws Exception { client.create().forPath("/test"); cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of()); assertNull(cache.getCurrentChildren("/t")); assertNull(cache.getCurrentChildren("/testing")); client.create().forPath("/test/one", "hey there".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of("one")); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there"); assertEquals(cache.getCurrentChildren("/test/one").keySet(), ImmutableSet.of()); assertNull(cache.getCurrentChildren("/test/o")); assertNull(cache.getCurrentChildren("/test/onely")); client.setData().forPath("/test/one", "sup!".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_UPDATED, "/test/one"); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of("one")); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "sup!"); client.delete().forPath("/test/one"); assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/one", "sup!".getBytes()); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of()); assertNoMoreEvents(); } @Test public void testBasicsWithNoZkWatches() throws Exception { client.create().forPath("/test"); client.create().forPath("/test/one", "hey there".getBytes()); cache = buildWithListeners(TreeCache.newBuilder(client, "/test").disableZkWatches(true)); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertEvent(TreeCacheEvent.Type.INITIALIZED); assertEquals(cache.getCurrentChildren("/test").keySet(), ImmutableSet.of("one")); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there"); assertEquals(cache.getCurrentChildren("/test/one").keySet(), ImmutableSet.of()); assertNull(cache.getCurrentChildren("/test/o")); assertNull(cache.getCurrentChildren("/test/onely")); assertNull(cache.getCurrentChildren("/t")); assertNull(cache.getCurrentChildren("/testing")); assertNoMoreEvents(); } @Test public void testBasicsOnTwoCaches() throws Exception { TreeCache cache2 = newTreeCacheWithListeners(client, "/test"); cache2.getListenable().removeListener(eventListener); // Don't listen on the second cache. // Just ensures the same event count; enables test flow control on cache2. final Semaphore semaphore = new Semaphore(0); cache2.getListenable().addListener(new TreeCacheListener() { @Override public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { semaphore.release(); } }); try { client.create().forPath("/test"); cache = newTreeCacheWithListeners(client, "/test"); cache.start(); cache2.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.INITIALIZED); semaphore.acquire(2); client.create().forPath("/test/one", "hey there".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there"); semaphore.acquire(); assertEquals(new String(cache2.getCurrentData("/test/one").getData()), "hey there"); client.setData().forPath("/test/one", "sup!".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_UPDATED, "/test/one"); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "sup!"); semaphore.acquire(); assertEquals(new String(cache2.getCurrentData("/test/one").getData()), "sup!"); client.delete().forPath("/test/one"); assertEvent(TreeCacheEvent.Type.NODE_REMOVED, "/test/one", "sup!".getBytes()); assertNull(cache.getCurrentData("/test/one")); semaphore.acquire(); assertNull(cache2.getCurrentData("/test/one")); assertNoMoreEvents(); assertEquals(semaphore.availablePermits(), 0); } finally { CloseableUtils.closeQuietly(cache2); } } @Test public void testDeleteNodeAfterCloseDoesntCallExecutor() throws Exception { client.create().forPath("/test"); cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.create().forPath("/test/one", "hey there".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test/one"); assertEquals(new String(cache.getCurrentData("/test/one").getData()), "hey there"); cache.close(); assertNoMoreEvents(); client.delete().forPath("/test/one"); assertNoMoreEvents(); } /** * Make sure TreeCache gets to a sane state when we can't initially connect to server. */ @Test public void testServerNotStartedYet() throws Exception { // Stop the existing server. server.stop(); // Shutdown the existing client and re-create it started. client.close(); initCuratorFramework(); // Start the client disconnected. cache = newTreeCacheWithListeners(client, "/test"); cache.start(); assertNoMoreEvents(); // Now restart the server. server.restart(); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.create().forPath("/test"); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertNoMoreEvents(); } @Test public void testErrorListener() throws Exception { client.create().forPath("/test"); cache = buildWithListeners(TreeCache.newBuilder(client, "/test")); // Register a listener that throws an exception for the event cache.getListenable().addListener(new TreeCacheListener() { @Override public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { if ( event.getType() == Type.NODE_UPDATED ) { throw new RuntimeException("Test Exception"); } } }); cache.getUnhandledErrorListenable().removeListener(errorListener); final AtomicBoolean isProcessed = new AtomicBoolean(false); cache.getUnhandledErrorListenable().addListener(new UnhandledErrorListener() { @Override public void unhandledError(String message, Throwable e) { assertFalse(isProcessed.compareAndSet(false, true)); } }); cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/test"); assertEvent(TreeCacheEvent.Type.INITIALIZED); client.setData().forPath("/test", "hey there".getBytes()); assertEvent(TreeCacheEvent.Type.NODE_UPDATED, "/test"); assertNoMoreEvents(); } } TestTreeCacheEventOrdering.java000066400000000000000000000043371442004423600420240ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import org.apache.curator.framework.CuratorFramework; import java.util.concurrent.BlockingQueue; public class TestTreeCacheEventOrdering extends TestEventOrdering { @Override protected int getActualQty(TreeCache cache) { return cache.getCurrentChildren("/root").size(); } @Override protected TreeCache newCache(CuratorFramework client, String path, final BlockingQueue events) throws Exception { TreeCache cache = new TreeCache(client, path); TreeCacheListener listener = new TreeCacheListener() { @Override public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { if ( (event.getData() != null) && (event.getData().getPath().startsWith("/root/")) ) { if ( event.getType() == TreeCacheEvent.Type.NODE_ADDED ) { events.add(new Event(EventType.ADDED, event.getData().getPath())); } if ( event.getType() == TreeCacheEvent.Type.NODE_REMOVED ) { events.add(new Event(EventType.DELETED, event.getData().getPath())); } } } }; cache.getListenable().addListener(listener); cache.start(); return cache; } } TestTreeCacheIteratorAndSize.java000066400000000000000000000170431442004423600423160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Sets; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; public class TestTreeCacheIteratorAndSize extends CuratorTestBase { @Test public void testBasic() throws Exception { final String[] nodes = { "/base/test", "/base/test/3", "/base/test/3/0", "/base/test/3/0/0", "/base/test/3/0/1", "/base/test/3/1", "/base/test/3/1/0", "/base/test/3/1/1", "/base/test/3/2", "/base/test/3/2/0", "/base/test/3/2/1", "/base/test/3/2/3", "/base/test/3/3", "/base/test/3/3/1", "/base/test/3/3/3" }; try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); String basePath = "/base/test"; try (TreeCache treeCache = new TreeCache(client, basePath) ) { treeCache.start(); for ( String node : nodes ) { client.create().creatingParentsIfNeeded().forPath(node, node.getBytes()); } timing.sleepABit(); // let the cache settle Iterator iterator = treeCache.iterator(); Map iteratorValues = new HashMap<>(); while ( iterator.hasNext() ) { ChildData next = iterator.next(); iteratorValues.put(next.getPath(), next.getData()); } assertEquals(iteratorValues.size(), nodes.length); for ( String node : nodes ) { assertArrayEquals(iteratorValues.get(node), node.getBytes()); } assertEquals(treeCache.size(), nodes.length); } } } @Test public void testIteratorWithRandomGraph() throws Exception { Map pathAndData = new HashMap<>(); ThreadLocalRandom random = ThreadLocalRandom.current(); int nodeQty = random.nextInt(100, 200); int maxPerRow = random.nextInt(1, 10); int maxDepth = random.nextInt(3, 5); try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); String basePath = "/base/test"; try (TreeCache treeCache = new TreeCache(client, basePath) ) { treeCache.start(); client.create().creatingParentsIfNeeded().forPath(basePath, "0".getBytes()); pathAndData.put(basePath, "0"); while ( nodeQty-- > 0 ) { int thisDepth = random.nextInt(1, maxDepth + 1); StringBuilder path = new StringBuilder(basePath); for ( int i = 0; i < thisDepth; ++i ) { path.append("/").append(random.nextInt(maxPerRow)); long value = random.nextLong(); pathAndData.put(path.toString(), Long.toString(value)); client.create().orSetData().forPath(path.toString(), Long.toString(value).getBytes()); } } timing.sleepABit(); // let the cache settle assertEquals(treeCache.size(), pathAndData.size()); // at this point we have a cached graph of random nodes with random values Iterator iterator = treeCache.iterator(); while ( iterator.hasNext() ) { ChildData next = iterator.next(); assertTrue(pathAndData.containsKey(next.getPath())); assertArrayEquals(pathAndData.get(next.getPath()).getBytes(), next.getData()); pathAndData.remove(next.getPath()); } assertEquals(pathAndData.size(), 0); // above loop should have removed all nodes } } } @Test public void testEmptyTree() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) { client.start(); try (TreeCache treeCache = new TreeCache(client, "/base/test")) { treeCache.start(); Iterator iterator = treeCache.iterator(); assertFalse(iterator.hasNext()); assertEquals(treeCache.size(), 0); } } } @Test public void testWithDeletedNodes() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) { client.start(); try (TreeCache treeCache = new TreeCache(client, "/foo")) { treeCache.start(); client.create().forPath("/foo"); client.create().forPath("/foo/a1"); client.create().forPath("/foo/a2"); client.create().forPath("/foo/a2/a2.1"); client.create().forPath("/foo/a2/a2.2"); client.create().forPath("/foo/a3"); client.create().forPath("/foo/a3/a3.1"); client.create().forPath("/foo/a3/a3.2"); client.delete().forPath("/foo/a2/a2.2"); client.delete().forPath("/foo/a3/a3.1"); timing.sleepABit(); // let the cache settle Iterator iterator = treeCache.iterator(); Set paths = new HashSet<>(); while ( iterator.hasNext() ) { ChildData next = iterator.next(); paths.add(next.getPath()); } assertEquals(paths, Sets.newHashSet("/foo", "/foo/a1", "/foo/a2", "/foo/a2/a2.1", "/foo/a3", "/foo/a3/a3.2")); assertEquals(treeCache.size(), 6); } } } } TestTreeCacheRandomTree.java000066400000000000000000000212521442004423600413040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import com.google.common.collect.Iterables; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.utils.ZKPaths; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Random; public class TestTreeCacheRandomTree extends BaseTestTreeCache { /** * A randomly generated source-of-truth node for {@link #testGiantRandomDeepTree()} */ private static final class TestNode { String fullPath; byte[] data; Map children = new HashMap(); TestNode(String fullPath, byte[] data) { this.fullPath = fullPath; this.data = data; } } // These constants will produce a tree about 10 levels deep. private static final int ITERATIONS = 1000; private static final double DIVE_CHANCE = 0.9; private static final int TEST_DEPTH = 5; private final Random random = new Random(); private boolean withDepth = false; @Test public void testGiantRandomDeepTree() throws Exception { doTestGiantRandomDeepTree(); } @Test public void testGiantRandomDeepTreeWithDepth() throws Exception { withDepth = true; doTestGiantRandomDeepTree(); } /** * Randomly construct a large tree of test data in memory, mirror it into ZK, and then use * a TreeCache to follow the changes. At each step, assert that TreeCache matches our * source-of-truth test data, and that we see exactly the set of events we expect to see. */ private void doTestGiantRandomDeepTree() throws Exception { client.create().forPath("/tree", null); CuratorFramework cl = client.usingNamespace("tree"); if ( withDepth ) { cache = buildWithListeners(TreeCache.newBuilder(cl, "/").setMaxDepth(TEST_DEPTH)); } else { cache = newTreeCacheWithListeners(cl, "/"); } cache.start(); assertEvent(TreeCacheEvent.Type.NODE_ADDED, "/"); assertEvent(TreeCacheEvent.Type.INITIALIZED); TestNode root = new TestNode("/", null); int maxDepth = 0; int adds = 0; int removals = 0; int updates = 0; for ( int i = 0; i < ITERATIONS; ++i ) { // Select a node to update, randomly navigate down through the tree int depth = 0; TestNode last = null; TestNode node = root; while ( !node.children.isEmpty() && random.nextDouble() < DIVE_CHANCE ) { // Go down a level in the tree. Select a random child for the next iteration. last = node; node = Iterables.get(node.children.values(), random.nextInt(node.children.size())); ++depth; } maxDepth = Math.max(depth, maxDepth); // Okay we found a node, let's do something interesting with it. switch ( random.nextInt(3) ) { case 0: // Try a removal if we have no children and we're not the root node. if ( node != root && node.children.isEmpty() ) { // Delete myself from parent. TestNode removed = last.children.remove(ZKPaths.getNodeFromPath(node.fullPath)); assertSame(node, removed); // Delete from ZK cl.delete().forPath(node.fullPath); // TreeCache should see the delete. if (shouldSeeEventAt(node.fullPath)) { assertEvent(TreeCacheEvent.Type.NODE_REMOVED, node.fullPath); } ++removals; } break; case 1: // Do an update. byte[] newData = new byte[10]; random.nextBytes(newData); if ( Arrays.equals(node.data, newData) ) { // Randomly generated the same data! Very small chance, just skip. continue; } // Update source-of-truth. node.data = newData; // Update in ZK. cl.setData().forPath(node.fullPath, node.data); // TreeCache should see the update. if (shouldSeeEventAt(node.fullPath)) { assertEvent(TreeCacheEvent.Type.NODE_UPDATED, node.fullPath, node.data); } ++updates; break; case 2: // Add a new child. String name = Long.toHexString(random.nextLong()); if ( node.children.containsKey(name) ) { // Randomly generated the same name! Very small chance, just skip. continue; } // Add a new child to our test tree. byte[] data = new byte[10]; random.nextBytes(data); TestNode child = new TestNode(ZKPaths.makePath(node.fullPath, name), data); node.children.put(name, child); // Add to ZK. cl.create().forPath(child.fullPath, child.data); // TreeCache should see the add. if (shouldSeeEventAt(child.fullPath)) { assertEvent(TreeCacheEvent.Type.NODE_ADDED, child.fullPath, child.data); } ++adds; break; } // Each iteration, ensure the cached state matches our source-of-truth tree. assertNodeEquals(cache.getCurrentData("/"), root); assertTreeEquals(cache, root, 0); } // Typical stats for this test: maxDepth: 10, adds: 349, removals: 198, updates: 320 // We get more adds than removals because removals only happen if we're at a leaf. System.out.println(String.format("maxDepth: %s, adds: %s, removals: %s, updates: %s", maxDepth, adds, removals, updates)); assertNoMoreEvents(); } /** * Returns true we should see an event at this path based on maxDepth, false otherwise. */ private boolean shouldSeeEventAt(String fullPath) { return !withDepth || ZKPaths.split(fullPath).size() <= TEST_DEPTH; } /** * Recursively assert that current children equal expected children. */ private void assertTreeEquals(TreeCache cache, TestNode expectedNode, int depth) { String path = expectedNode.fullPath; Map cacheChildren = cache.getCurrentChildren(path); assertNotNull(cacheChildren, path); if (withDepth && depth == TEST_DEPTH) { return; } assertEquals(cacheChildren.keySet(), expectedNode.children.keySet(), path); for ( Map.Entry entry : expectedNode.children.entrySet() ) { String nodeName = entry.getKey(); ChildData childData = cacheChildren.get(nodeName); TestNode expectedChild = entry.getValue(); assertNodeEquals(childData, expectedChild); assertTreeEquals(cache, expectedChild, depth + 1); } } /** * Assert that the given node data matches expected test node data. */ private static void assertNodeEquals(ChildData actualChild, TestNode expectedNode) { String path = expectedNode.fullPath; assertNotNull(actualChild, path); assertArrayEquals(actualChild.getData(), expectedNode.data, path); } } TestWrappedNodeCache.java000066400000000000000000000154771442004423600406500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/cache/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.cache; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.function.Supplier; import static org.apache.curator.framework.recipes.cache.CuratorCacheListener.builder; @Tag(CuratorTestBase.zk36Group) public class TestWrappedNodeCache extends CuratorTestBase { @Test public void testDeleteThenCreate() throws Exception { CuratorCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); client.create().creatingParentsIfNeeded().forPath("/test/foo", "one".getBytes()); final Semaphore semaphore = new Semaphore(0); cache = CuratorCache.build(client, "/test/foo"); NodeCacheListener listener = semaphore::release; cache.listenable().addListener(builder().forNodeCache(listener).build()); Supplier> rootData = getRootDataProc(cache, "/test/foo"); cache.start(); assertTrue(timing.acquireSemaphore(semaphore)); assertTrue(rootData.get().isPresent()); assertArrayEquals(rootData.get().get().getData(), "one".getBytes()); client.delete().forPath("/test/foo"); assertTrue(timing.acquireSemaphore(semaphore)); client.create().forPath("/test/foo", "two".getBytes()); assertTrue(timing.acquireSemaphore(semaphore)); assertTrue(rootData.get().isPresent()); assertArrayEquals(rootData.get().get().getData(), "two".getBytes()); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @Test public void testKilledSession() throws Exception { CuratorCache cache = null; CuratorFramework client = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); client.create().creatingParentsIfNeeded().forPath("/test/node", "start".getBytes()); CountDownLatch lostLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener((__, newState) -> { if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } }); cache = CuratorCache.build(client,"/test/node"); Semaphore latch = new Semaphore(0); NodeCacheListener listener = latch::release; cache.listenable().addListener(builder().forNodeCache(listener).build()); Supplier> rootData = getRootDataProc(cache, "/test/node"); cache.start(); assertTrue(timing.acquireSemaphore(latch)); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertTrue(timing.awaitLatch(lostLatch)); assertTrue(rootData.get().isPresent()); assertArrayEquals(rootData.get().get().getData(), "start".getBytes()); client.setData().forPath("/test/node", "new data".getBytes()); assertTrue(timing.acquireSemaphore(latch)); assertTrue(rootData.get().isPresent()); assertArrayEquals(rootData.get().get().getData(), "new data".getBytes()); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } @SuppressWarnings("ConstantConditions") @Test public void testBasics() throws Exception { CuratorCache cache = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().forPath("/test"); cache = CuratorCache.build(client, "/test/node"); cache.start(); Supplier> rootData = getRootDataProc(cache, "/test/node"); final Semaphore semaphore = new Semaphore(0); NodeCacheListener listener = semaphore::release; cache.listenable().addListener(builder().forNodeCache(listener).build()); assertNull(rootData.get().orElse(null)); client.create().forPath("/test/node", "a".getBytes()); assertTrue(timing.acquireSemaphore(semaphore)); assertArrayEquals(rootData.get().orElse(null).getData(), "a".getBytes()); client.setData().forPath("/test/node", "b".getBytes()); assertTrue(timing.acquireSemaphore(semaphore)); assertArrayEquals(rootData.get().orElse(null).getData(), "b".getBytes()); client.delete().forPath("/test/node"); assertTrue(timing.acquireSemaphore(semaphore)); assertNull(rootData.get().orElse(null)); } finally { CloseableUtils.closeQuietly(cache); TestCleanState.closeAndTestClean(client); } } private Supplier> getRootDataProc(CuratorCache cache, String rootPath) { return () -> cache.get(rootPath); } } leader/000077500000000000000000000000001442004423600341645ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipesChaosMonkeyCnxnFactory.java000066400000000000000000000121121442004423600414230ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import org.apache.curator.test.Compatibility; import org.apache.curator.test.TestingZooKeeperMain; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.server.ByteBufferInputStream; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooKeeperServerShutdownHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.ByteBuffer; /** * A connection factory that will behave like the NIOServerCnxnFactory except that * it will unexpectedly close the connection right after the first znode has * been created in Zookeeper. * Subsequent create operations will succeed. */ public class ChaosMonkeyCnxnFactory extends NIOServerCnxnFactory { public static final String CHAOS_ZNODE = "/mylock"; public static final String CHAOS_ZNODE_PREFIX = CHAOS_ZNODE + "/"; private static final Logger log = LoggerFactory.getLogger(ChaosMonkeyCnxnFactory.class); /* How long after the first error, connections are rejected */ public static final long LOCKOUT_DURATION_MS = 6000; @Override public void startup(ZooKeeperServer zks) throws IOException, InterruptedException { super.startup(new ChaosMonkeyZookeeperServer(zks)); } public static class ChaosMonkeyZookeeperServer extends ZooKeeperServer { private final ZooKeeperServer zks; private long firstError = 0; public ChaosMonkeyZookeeperServer(ZooKeeperServer zks) { this.zks = zks; setTxnLogFactory(zks.getTxnLogFactory()); setTickTime(zks.getTickTime()); setMinSessionTimeout(zks.getMinSessionTimeout()); setMaxSessionTimeout(zks.getMaxSessionTimeout()); } @Override public void startup() { super.startup(); if ( zks instanceof TestingZooKeeperMain.TestZooKeeperServer ) { ((TestingZooKeeperMain.TestZooKeeperServer)zks).noteStartup(); } } @Override public void submitRequest(Request si) { long remaining = firstError != 0 ? LOCKOUT_DURATION_MS - (System.currentTimeMillis() - firstError) : 0; if ( si.type != ZooDefs.OpCode.createSession && si.type != ZooDefs.OpCode.sync && si.type != ZooDefs.OpCode.ping && firstError != 0 && remaining > 0 ) { log.debug("Rejected : " + si.toString()); // Still reject request log.debug("Still not ready for " + remaining + "ms"); Compatibility.serverCnxnClose(si.cnxn); return; } // Submit the request to the legacy Zookeeper server log.debug("Applied : " + si.toString()); super.submitRequest(si); // Raise an error if a lock is created if ( (si.type == ZooDefs.OpCode.create) || (si.type == ZooDefs.OpCode.create2) ) { CreateRequest createRequest = new CreateRequest(); try { ByteBuffer duplicate = si.request.duplicate(); duplicate.rewind(); ByteBufferInputStream.byteBuffer2Record(duplicate, createRequest); if ( createRequest.getPath().startsWith(CHAOS_ZNODE_PREFIX) && firstError == 0 ) { firstError = System.currentTimeMillis(); // The znode has been created, close the connection and don't tell it to client log.warn("Closing connection right after " + createRequest.getPath() + " creation"); Compatibility.serverCnxnClose(si.cnxn); } } catch ( Exception e ) { // Should not happen Compatibility.serverCnxnClose(si.cnxn); } } } @Override public ZooKeeperServerShutdownHandler getZkShutdownHandler() { return zks.getZkShutdownHandler(); } } } TestLeaderAcls.java000066400000000000000000000120501442004423600376640ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestLeaderAcls extends BaseClassForTests { private final Timing timing = new Timing(); @Test @DisplayName("Validation test for CURATOR-365") public void testAclErrorWithLeader() throws Exception { ACLProvider provider = new ACLProvider() { @Override public List getDefaultAcl() { return ZooDefs.Ids.OPEN_ACL_UNSAFE; } @Override public List getAclForPath(String path) { if ( path.equals("/base") ) { try { String testDigest = DigestAuthenticationProvider.generateDigest("test:test"); return Collections.singletonList(new ACL(ZooDefs.Perms.ALL, new Id("digest", testDigest))); } catch ( NoSuchAlgorithmException e ) { e.printStackTrace(); } } return getDefaultAcl(); } }; RetryPolicy retryPolicy = new ExponentialBackoffRetry(timing.milliseconds(), 3); CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(retryPolicy) .aclProvider(provider) .authorization("digest", "test:test".getBytes()) ; CuratorFramework client = builder.build(); LeaderLatch latch = null; try { client.start(); latch = new LeaderLatch(client, "/base"); latch.start(); assertTrue(latch.await(timing.forWaiting().seconds(), TimeUnit.SECONDS)); latch.close(); latch = null; CuratorFramework noAuthClient = CuratorFrameworkFactory.newClient(server.getConnectString(), retryPolicy); try { noAuthClient.start(); final CountDownLatch noAuthLatch = new CountDownLatch(1); UnhandledErrorListener listener = new UnhandledErrorListener() { @Override public void unhandledError(String message, Throwable e) { if ( e instanceof KeeperException.NoAuthException ) { noAuthLatch.countDown(); } } }; noAuthClient.getUnhandledErrorListenable().addListener(listener); // use a path below "base" as noAuthClient is not authorized to create nodes in "/base" // but also making sure that the code goes through the backgroundCreateParentsThenNode() codepath latch = new LeaderLatch(noAuthClient, "/base/second"); latch.start(); assertTrue(timing.awaitLatch(noAuthLatch)); } finally { CloseableUtils.closeQuietly(noAuthClient); } } finally { CloseableUtils.closeQuietly(latch); CloseableUtils.closeQuietly(client); } } } TestLeaderLatch.java000066400000000000000000001406641442004423600400520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.io.Closeable; import java.time.Duration; import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.ForkJoinPool; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.framework.state.ConnectionStateListenerManagerFactory; import org.apache.curator.framework.state.SessionConnectionStateErrorPolicy; import org.apache.curator.framework.state.StandardConnectionStateErrorPolicy; import org.apache.curator.retry.RetryForever; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.TestingServer; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.awaitility.Awaitility; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.IntStream; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestLeaderLatch extends BaseClassForTests { private static final String PATH_NAME = "/one/two/me"; private static final int MAX_LOOPS = 5; private static class Holder { final BlockingQueue stateChanges = new LinkedBlockingQueue<>(); final CountDownLatch isLockedLatch = new CountDownLatch(1); volatile LeaderLatch latch; } @Test public void testWithCircuitBreaker() throws Exception { final int threadQty = 5; ExecutorService executorService = Executors.newFixedThreadPool(threadQty); List holders = Collections.emptyList(); Timing2 timing = new Timing2(); ConnectionStateListenerManagerFactory managerFactory = ConnectionStateListenerManagerFactory.circuitBreaking(new RetryForever(timing.multiple(2).milliseconds())); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .connectionStateListenerManagerFactory(managerFactory) .connectionTimeoutMs(timing.connection()) .sessionTimeoutMs(timing.session()) .build(); try { client.start(); client.create().forPath("/hey"); Semaphore lostSemaphore = new Semaphore(0); ConnectionStateListener unProxiedListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { lostSemaphore.release(); } } @Override public boolean doNotProxy() { return true; } }; client.getConnectionStateListenable().addListener(unProxiedListener); holders = IntStream.range(0, threadQty) .mapToObj(index -> { Holder holder = new Holder(); holder.latch = new LeaderLatch(client, "/foo/bar/" + index) { @Override protected void handleStateChange(ConnectionState newState) { holder.stateChanges.offer(newState); super.handleStateChange(newState); } }; return holder; }) .collect(Collectors.toList()); holders.forEach(holder -> { executorService.submit(() -> { holder.latch.start(); assertTrue(holder.latch.await(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); holder.isLockedLatch.countDown(); return null; }); timing.awaitLatch(holder.isLockedLatch); }); for ( int i = 0; i < 4; ++i ) // note: 4 is just a random number of loops to simulate disconnections { server.stop(); assertTrue(timing.acquireSemaphore(lostSemaphore)); server.restart(); timing.sleepABit(); } for ( Holder holder : holders ) { assertTrue(holder.latch.await(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); assertEquals(timing.takeFromQueue(holder.stateChanges), ConnectionState.SUSPENDED); assertEquals(timing.takeFromQueue(holder.stateChanges), ConnectionState.LOST); assertEquals(timing.takeFromQueue(holder.stateChanges), ConnectionState.RECONNECTED); } } finally { holders.forEach(holder -> CloseableUtils.closeQuietly(holder.latch)); CloseableUtils.closeQuietly(client); executorService.shutdownNow(); } } @Test public void testUncreatedPathGetLeader() throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); LeaderLatch latch = new LeaderLatch(client, "/foo/bar"); latch.getLeader(); // CURATOR-436 - was throwing NoNodeException } } @Test public void testWatchedNodeDeletedOnReconnect() throws Exception { final String latchPath = "/foo/bar"; Timing2 timing = new Timing2(); try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)) ) { client.start(); LeaderLatch latch1 = new LeaderLatch(client, latchPath, "1"); try ( LeaderLatch latch2 = new LeaderLatch(client, latchPath, "2") ) { latch1.start(); assertTrue(latch1.await(timing.milliseconds(), TimeUnit.MILLISECONDS)); latch2.start(); // will get a watcher on latch1's node timing.sleepABit(); latch2.debugCheckLeaderShipLatch = new CountDownLatch(1); latch1.close(); // simulate the leader's path getting deleted latch1 = null; timing.sleepABit(); // after this, latch2 should be blocked just before getting the path in checkLeadership() latch2.reset(); // force the internal "ourPath" to get reset latch2.debugCheckLeaderShipLatch.countDown(); // allow checkLeadership() to continue assertTrue(latch2.await(timing.forSessionSleep().forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); timing.sleepABit(); assertEquals(client.getChildren().forPath(latchPath).size(), 1); } finally { CloseableUtils.closeQuietly(latch1); } } } @Test public void testResettingOfLeadershipAfterConcurrentLeadershipChange() throws Exception { final String latchPath = "/test"; final Timing2 timing = new Timing2(); final BlockingQueue events = Queues.newLinkedBlockingQueue(); final List closeableResources = new ArrayList<>(); try { final String id0 = "id0"; final CuratorFramework client0 = createAndStartClient(server.getConnectString(), timing, id0, events); closeableResources.add(client0); final LeaderLatch latch0 = createAndStartLeaderLatch(client0, latchPath, id0, events); closeableResources.add(latch0); assertEquals(new TestEvent(id0, TestEventType.GAINED_CONNECTION), events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); assertEquals(new TestEvent(id0, TestEventType.GAINED_LEADERSHIP), events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); final String id1 = "id1"; final CuratorFramework client1 = createAndStartClient(server.getConnectString(), timing, id1, events); closeableResources.add(client1); final LeaderLatch latch1 = createAndStartLeaderLatch(client1, latchPath, id1, events); closeableResources.add(latch1); assertEquals(new TestEvent(id1, TestEventType.GAINED_CONNECTION), events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); // wait for the non-leading LeaderLatch (i.e. latch1) instance to be done with its creation // this call is time-consuming but necessary because we don't have a handle to detect the end of the reset call timing.forWaiting().sleepABit(); assertTrue(latch0.hasLeadership()); assertFalse(latch1.hasLeadership()); latch1.debugResetWaitBeforeNodeDeleteLatch = new CountDownLatch(1); latch1.debugResetWaitLatch = new CountDownLatch(1); latch0.debugResetWaitLatch = new CountDownLatch(1); // force latch0 and latch1 reset to trigger the actual test latch0.reset(); // latch1 needs to be called within a separate thread since it's going to be blocked by the CountDownLatch outside an async call ForkJoinPool.commonPool().submit(() -> { latch1.reset(); return null; }); // latch0.reset() will result in it losing its leadership, deleting its old child node and creating a new child node before being blocked by its debugResetWaitLatch assertEquals(new TestEvent(id0, TestEventType.LOST_LEADERSHIP), events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); // latch1.reset() is blocked but latch1 will gain leadership due its node watching latch0's node to be deleted assertEquals(new TestEvent(id1, TestEventType.GAINED_LEADERSHIP), events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); assertFalse(latch0.hasLeadership()); assertTrue(latch1.hasLeadership()); // latch0.reset() continues with the getChildren call, finds itself not being the leader and starts listening to the node created by latch1 latch0.debugResetWaitLatch.countDown(); timing.sleepABit(); // latch1.reset() continues, deletes its old child node and creates a new child node before being blocked by its debugResetWaitLatch latch1.debugResetWaitBeforeNodeDeleteLatch.countDown(); // latch0 receives NodeDeleteEvent and then finds itself to be the leader assertEquals(new TestEvent(id0, TestEventType.GAINED_LEADERSHIP), events.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); assertTrue(latch0.hasLeadership()); // latch1.reset() continues and finds itself not being the leader latch1.debugResetWaitLatch.countDown(); // this call is time-consuming but necessary because we don't have a handle to detect the end of the reset call timing.forWaiting().sleepABit(); assertTrue(latch0.hasLeadership()); assertFalse(latch1.hasLeadership()); } finally { // reverse is necessary for closing the LeaderLatch instances before closing the corresponding client Collections.reverse(closeableResources); closeableResources.forEach(CloseableUtils::closeQuietly); } } private static CuratorFramework createAndStartClient(String zkConnectString, Timing2 timing, String id, Collection events) { final CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(zkConnectString) .connectionTimeoutMs(timing.connection()) .sessionTimeoutMs(timing.session()) .retryPolicy(new RetryOneTime(1)) .connectionStateErrorPolicy(new StandardConnectionStateErrorPolicy()) .build(); client.getConnectionStateListenable().addListener((client1, newState) -> { if ( newState == ConnectionState.CONNECTED ) { events.add(new TestEvent(id, TestEventType.GAINED_CONNECTION)); } }); client.start(); return client; } private static LeaderLatch createAndStartLeaderLatch(CuratorFramework client, String latchPath, String id, Collection events) throws Exception { final LeaderLatch latch = new LeaderLatch(client, latchPath, id); latch.addListener(new LeaderLatchListener() { @Override public void isLeader() { events.add(new TestEvent(latch.getId(), TestEventType.GAINED_LEADERSHIP)); } @Override public void notLeader() { events.add(new TestEvent(latch.getId(), TestEventType.LOST_LEADERSHIP)); } }); latch.start(); return latch; } private enum TestEventType { GAINED_LEADERSHIP, LOST_LEADERSHIP, GAINED_CONNECTION; } private static class TestEvent { private final String id; private final TestEventType eventType; public TestEvent(String id, TestEventType eventType) { this.id = id; this.eventType = eventType; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TestEvent testEvent = (TestEvent) o; return Objects.equals(id, testEvent.id) && eventType == testEvent.eventType; } } @Test public void testLeadershipElectionWhenNodeDisappearsAfterChildrenAreRetrieved() throws Exception { final String latchPath = "/foo/bar"; final Timing2 timing = new Timing2(); final Duration pollInterval = Duration.ofMillis(100); try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { client.start(); LeaderLatch latchInitialLeader = new LeaderLatch(client, latchPath, "initial-leader"); LeaderLatch latchCandidate0 = new LeaderLatch(client, latchPath, "candidate-0"); LeaderLatch latchCandidate1 = new LeaderLatch(client, latchPath, "candidate-1"); try { latchInitialLeader.start(); // we want to make sure that the leader gets leadership before other instances are going to join the party waitForALeader(Collections.singletonList(latchInitialLeader), new Timing()); // candidate #0 will wait for the leader to go away - this should happen after the child nodes are retrieved by candidate #0 latchCandidate0.debugCheckLeaderShipLatch = new CountDownLatch(1); latchCandidate0.start(); final int expectedChildrenAfterCandidate0Joins = 2; Awaitility.await("There should be " + expectedChildrenAfterCandidate0Joins + " child nodes created after candidate #0 joins the leader election.") .pollInterval(pollInterval) .pollInSameThread() .until(() -> client.getChildren().forPath(latchPath).size() == expectedChildrenAfterCandidate0Joins); // no extra CountDownLatch needs to be set here because candidate #1 will rely on candidate #0 latchCandidate1.start(); final int expectedChildrenAfterCandidate1Joins = 3; Awaitility.await("There should be " + expectedChildrenAfterCandidate1Joins + " child nodes created after candidate #1 joins the leader election.") .pollInterval(pollInterval) .pollInSameThread() .until(() -> client.getChildren().forPath(latchPath).size() == expectedChildrenAfterCandidate1Joins); // triggers the removal of the corresponding child node after candidate #0 retrieved the children latchInitialLeader.close(); latchCandidate0.debugCheckLeaderShipLatch.countDown(); waitForALeader(Arrays.asList(latchCandidate0, latchCandidate1), new Timing()); assertTrue(latchCandidate0.hasLeadership() ^ latchCandidate1.hasLeadership()); } finally { for (LeaderLatch latchToClose : Arrays.asList(latchInitialLeader, latchCandidate0, latchCandidate1)) { latchToClose.closeOnDemand(); } } } } @Test public void testSessionErrorPolicy() throws Exception { Timing timing = new Timing(); LeaderLatch latch = null; CuratorFramework client = null; for ( int i = 0; i < 2; ++i ) { boolean isSessionIteration = (i == 0); try { client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .connectionTimeoutMs(10000) .sessionTimeoutMs(60000) .retryPolicy(new RetryOneTime(1)) .connectionStateErrorPolicy(isSessionIteration ? new SessionConnectionStateErrorPolicy() : new StandardConnectionStateErrorPolicy()) .build(); final BlockingQueue states = Queues.newLinkedBlockingQueue(); ConnectionStateListener stateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { states.add(newState.name()); } }; client.getConnectionStateListenable().addListener(stateListener); client.start(); final String latchPatch = "/test"; latch = new LeaderLatch(client, latchPatch); LeaderLatchListener listener = new LeaderLatchListener() { @Override public void isLeader() { states.add("true"); } @Override public void notLeader() { states.add("false"); } }; latch.addListener(listener); latch.start(); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED.name()); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), "true"); final List beforeResetChildren = client.getChildren().forPath(latchPatch); server.stop(); if ( isSessionIteration ) { assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED.name()); server.restart(); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.RECONNECTED.name()); assertNull(states.poll(timing.milliseconds(), TimeUnit.MILLISECONDS)); } else { String s = states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); assertTrue("false".equals(s) || ConnectionState.SUSPENDED.name().equals(s)); s = states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); assertTrue("false".equals(s) || ConnectionState.SUSPENDED.name().equals(s)); server.restart(); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.RECONNECTED.name()); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), "true"); final List afterResetChildren = client.getChildren().forPath(latchPatch); assertEquals(beforeResetChildren, afterResetChildren); } } finally { CloseableUtils.closeQuietly(latch); CloseableUtils.closeQuietly(client); } } } @Test public void testErrorPolicies() throws Exception { Timing2 timing = new Timing2(); LeaderLatch latch = null; CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .connectionTimeoutMs(1000) .sessionTimeoutMs(timing.session()) .retryPolicy(new RetryOneTime(1)) .connectionStateErrorPolicy(new StandardConnectionStateErrorPolicy()) .build(); try { final BlockingQueue states = Queues.newLinkedBlockingQueue(); ConnectionStateListener stateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { states.add(newState.name()); } }; client.getConnectionStateListenable().addListener(stateListener); client.start(); latch = new LeaderLatch(client, "/test"); LeaderLatchListener listener = new LeaderLatchListener() { @Override public void isLeader() { states.add("true"); } @Override public void notLeader() { states.add("false"); } }; latch.addListener(listener); latch.start(); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED.name()); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), "true"); server.close(); List next = Lists.newArrayList(); next.add(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); next.add(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); assertTrue(next.equals(Arrays.asList(ConnectionState.SUSPENDED.name(), "false")) || next.equals(Arrays.asList("false", ConnectionState.SUSPENDED.name())), next.toString()); assertEquals(states.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST.name()); latch.close(); client.close(); timing.sleepABit(); states.clear(); server = new TestingServer(); client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .connectionTimeoutMs(1000) .sessionTimeoutMs(timing.session()) .retryPolicy(new RetryOneTime(1)) .connectionStateErrorPolicy(new SessionConnectionStateErrorPolicy()) .build(); client.getConnectionStateListenable().addListener(stateListener); client.start(); latch = new LeaderLatch(client, "/test"); latch.addListener(listener); latch.start(); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED.name()); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), "true"); server.close(); assertEquals(states.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED.name()); next = Lists.newArrayList(); next.add(states.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS)); next.add(states.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS)); assertTrue(next.equals(Arrays.asList(ConnectionState.LOST.name(), "false")) || next.equals(Arrays.asList("false", ConnectionState.LOST.name())), next.toString()); } finally { CloseableUtils.closeQuietly(latch); CloseableUtils.closeQuietly(client); } } @Test public void testProperCloseWithoutConnectionEstablished() throws Exception { server.stop(); Timing timing = new Timing(); LeaderLatch latch = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final AtomicBoolean resetCalled = new AtomicBoolean(false); final CountDownLatch cancelStartTaskLatch = new CountDownLatch(1); latch = new LeaderLatch(client, PATH_NAME) { @Override void reset() throws Exception { resetCalled.set(true); super.reset(); } @Override protected boolean cancelStartTask() { if ( super.cancelStartTask() ) { cancelStartTaskLatch.countDown(); return true; } return false; } }; latch.start(); latch.close(); latch = null; assertTrue(timing.awaitLatch(cancelStartTaskLatch)); assertFalse(resetCalled.get()); } finally { CloseableUtils.closeQuietly(latch); TestCleanState.closeAndTestClean(client); } } @Test public void testResetRace() throws Exception { Timing timing = new Timing(); LeaderLatch latch = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); latch = new LeaderLatch(client, PATH_NAME); latch.debugResetWaitLatch = new CountDownLatch(1); latch.start(); // will call reset() latch.reset(); // should not result in two nodes timing.sleepABit(); latch.debugResetWaitLatch.countDown(); timing.sleepABit(); assertEquals(client.getChildren().forPath(PATH_NAME).size(), 1); } finally { CloseableUtils.closeQuietly(latch); TestCleanState.closeAndTestClean(client); } } @Test public void testCreateDeleteRace() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().creatingParentsIfNeeded().forPath(PATH_NAME); LeaderLatch latch = new LeaderLatch(client, PATH_NAME); latch.debugResetWaitLatch = new CountDownLatch(1); latch.start(); latch.close(); timing.sleepABit(); latch.debugResetWaitLatch.countDown(); timing.sleepABit(); assertEquals(client.getChildren().forPath(PATH_NAME).size(), 0); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testLostConnection() throws Exception { final int PARTICIPANT_QTY = 10; List latches = Lists.newArrayList(); final Timing timing = new Timing(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final CountDownLatch countDownLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { countDownLatch.countDown(); } } }); for ( int i = 0; i < PARTICIPANT_QTY; ++i ) { LeaderLatch latch = new LeaderLatch(client, PATH_NAME); latch.start(); latches.add(latch); } waitForALeader(latches, timing); server.stop(); assertTrue(timing.awaitLatch(countDownLatch)); timing.forWaiting().sleepABit(); assertEquals(getLeaders(latches).size(), 0); server.restart(); assertEquals(waitForALeader(latches, timing).size(), 1); // should reconnect } finally { for ( LeaderLatch latch : latches ) { CloseableUtils.closeQuietly(latch); } TestCleanState.closeAndTestClean(client); } } @Test public void testCorrectWatching() throws Exception { final int PARTICIPANT_QTY = 10; final int PARTICIPANT_ID = 2; List latches = Lists.newArrayList(); final Timing timing = new Timing(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); for ( int i = 0; i < PARTICIPANT_QTY; ++i ) { LeaderLatch latch = new LeaderLatch(client, PATH_NAME); latch.start(); latches.add(latch); waitForALeader(latches, timing); } //we need to close a Participant that doesn't be actual leader (first Participant) nor the last latches.get(PARTICIPANT_ID).close(); //As the previous algorithm assumed that if the watched node is deleted gets the leadership //we need to ensure that the PARTICIPANT_ID-1 is not getting (wrongly) elected as leader. assertTrue(!latches.get(PARTICIPANT_ID - 1).hasLeadership()); } finally { //removes the already closed participant latches.remove(PARTICIPANT_ID); for ( LeaderLatch latch : latches ) { CloseableUtils.closeQuietly(latch); } TestCleanState.closeAndTestClean(client); } } @Test public void testWaiting() throws Exception { final int LOOPS = 10; for ( int i = 0; i < LOOPS; ++i ) { System.out.println("TRY #" + i); internalTestWaitingOnce(); Thread.sleep(10); } } private void internalTestWaitingOnce() throws Exception { final int PARTICIPANT_QTY = 10; ExecutorService executorService = Executors.newFixedThreadPool(PARTICIPANT_QTY); ExecutorCompletionService service = new ExecutorCompletionService(executorService); final Timing timing = new Timing(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final AtomicBoolean thereIsALeader = new AtomicBoolean(false); for ( int i = 0; i < PARTICIPANT_QTY; ++i ) { service.submit(new Callable() { @Override public Void call() throws Exception { LeaderLatch latch = new LeaderLatch(client, PATH_NAME); try { latch.start(); assertTrue(latch.await(timing.forWaiting().seconds(), TimeUnit.SECONDS)); assertTrue(thereIsALeader.compareAndSet(false, true)); Thread.sleep((int)(10 * Math.random())); thereIsALeader.set(false); } finally { latch.close(); } return null; } }); } for ( int i = 0; i < PARTICIPANT_QTY; ++i ) { service.take().get(); } } finally { executorService.shutdownNow(); TestCleanState.closeAndTestClean(client); } } @Test public void testBasic() throws Exception { basic(Mode.START_IMMEDIATELY); } @Test public void testBasicAlt() throws Exception { basic(Mode.START_IN_THREADS); } @Test public void testCallbackSanity() throws Exception { final int PARTICIPANT_QTY = 10; final CountDownLatch timesSquare = new CountDownLatch(PARTICIPANT_QTY); final AtomicLong masterCounter = new AtomicLong(0); final AtomicLong notLeaderCounter = new AtomicLong(0); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); ExecutorService exec = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("callbackSanity-%s").build()); List latches = Lists.newArrayList(); for ( int i = 0; i < PARTICIPANT_QTY; ++i ) { final LeaderLatch latch = new LeaderLatch(client, PATH_NAME); latch.addListener(new LeaderLatchListener() { boolean beenLeader = false; @Override public void isLeader() { if ( !beenLeader ) { masterCounter.incrementAndGet(); beenLeader = true; try { latch.reset(); } catch ( Exception e ) { throw Throwables.propagate(e); } } else { masterCounter.incrementAndGet(); CloseableUtils.closeQuietly(latch); timesSquare.countDown(); } } @Override public void notLeader() { notLeaderCounter.incrementAndGet(); } }, exec); latches.add(latch); } try { client.start(); for ( LeaderLatch latch : latches ) { latch.start(); } timesSquare.await(); assertEquals(masterCounter.get(), PARTICIPANT_QTY * 2); assertEquals(notLeaderCounter.get(), PARTICIPANT_QTY); for ( LeaderLatch latch : latches ) { assertEquals(latch.getState(), LeaderLatch.State.CLOSED); } } finally { for ( LeaderLatch latch : latches ) { if ( latch.getState() != LeaderLatch.State.CLOSED ) { CloseableUtils.closeQuietly(latch); } } TestCleanState.closeAndTestClean(client); } } @Test public void testCallbackNotifyLeader() throws Exception { final int PARTICIPANT_QTY = 10; final int SILENT_QTY = 3; final CountDownLatch timesSquare = new CountDownLatch(PARTICIPANT_QTY); final AtomicLong masterCounter = new AtomicLong(0); final AtomicLong notLeaderCounter = new AtomicLong(0); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); ExecutorService exec = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("callbackNotifyLeader-%s").build()); List latches = Lists.newArrayList(); for ( int i = 0; i < PARTICIPANT_QTY; ++i ) { LeaderLatch.CloseMode closeMode = i < SILENT_QTY ? LeaderLatch.CloseMode.SILENT : LeaderLatch.CloseMode.NOTIFY_LEADER; final LeaderLatch latch = new LeaderLatch(client, PATH_NAME, "", closeMode); latch.addListener(new LeaderLatchListener() { boolean beenLeader = false; @Override public void isLeader() { if ( !beenLeader ) { masterCounter.incrementAndGet(); beenLeader = true; try { latch.reset(); } catch ( Exception e ) { throw Throwables.propagate(e); } } else { masterCounter.incrementAndGet(); CloseableUtils.closeQuietly(latch); timesSquare.countDown(); } } @Override public void notLeader() { notLeaderCounter.incrementAndGet(); } }, exec); latches.add(latch); } try { client.start(); for ( LeaderLatch latch : latches ) { latch.start(); } timesSquare.await(); assertEquals(masterCounter.get(), PARTICIPANT_QTY * 2); assertEquals(notLeaderCounter.get(), PARTICIPANT_QTY * 2 - SILENT_QTY); for ( LeaderLatch latch : latches ) { assertEquals(latch.getState(), LeaderLatch.State.CLOSED); } } finally { for ( LeaderLatch latch : latches ) { if ( latch.getState() != LeaderLatch.State.CLOSED ) { CloseableUtils.closeQuietly(latch); } } TestCleanState.closeAndTestClean(client); } } @Test public void testCallbackDontNotify() throws Exception { final AtomicLong masterCounter = new AtomicLong(0); final AtomicLong notLeaderCounter = new AtomicLong(0); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); final LeaderLatch leader = new LeaderLatch(client, PATH_NAME); final LeaderLatch notifiedLeader = new LeaderLatch(client, PATH_NAME, "", LeaderLatch.CloseMode.NOTIFY_LEADER); leader.addListener(new LeaderLatchListener() { @Override public void isLeader() { } @Override public void notLeader() { masterCounter.incrementAndGet(); } }); notifiedLeader.addListener(new LeaderLatchListener() { @Override public void isLeader() { } @Override public void notLeader() { notLeaderCounter.incrementAndGet(); } }); try { client.start(); leader.start(); timing.sleepABit(); notifiedLeader.start(); timing.sleepABit(); notifiedLeader.close(); timing.sleepABit(); // Test the close override leader.close(LeaderLatch.CloseMode.NOTIFY_LEADER); assertEquals(leader.getState(), LeaderLatch.State.CLOSED); assertEquals(notifiedLeader.getState(), LeaderLatch.State.CLOSED); assertEquals(masterCounter.get(), 1); assertEquals(notLeaderCounter.get(), 0); } finally { if ( leader.getState() != LeaderLatch.State.CLOSED ) { CloseableUtils.closeQuietly(leader); } if ( notifiedLeader.getState() != LeaderLatch.State.CLOSED ) { CloseableUtils.closeQuietly(notifiedLeader); } TestCleanState.closeAndTestClean(client); } } @Test public void testNoServerAtStart() { CloseableUtils.closeQuietly(server); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryNTimes(5, 1000)); client.start(); final LeaderLatch leader = new LeaderLatch(client, PATH_NAME); final CountDownLatch leaderCounter = new CountDownLatch(1); final AtomicInteger leaderCount = new AtomicInteger(0); final AtomicInteger notLeaderCount = new AtomicInteger(0); leader.addListener(new LeaderLatchListener() { @Override public void isLeader() { leaderCounter.countDown(); leaderCount.incrementAndGet(); } @Override public void notLeader() { notLeaderCount.incrementAndGet(); } }); try { leader.start(); timing.sleepABit(); // Start the new server server = new TestingServer(server.getPort(), server.getTempDirectory()); assertTrue(timing.awaitLatch(leaderCounter), "Not elected leader"); assertEquals(leaderCount.get(), 1, "Elected too many times"); assertEquals(notLeaderCount.get(), 0, "Unelected too many times"); } catch ( Exception e ) { fail("Unexpected exception", e); } finally { CloseableUtils.closeQuietly(leader); TestCleanState.closeAndTestClean(client); CloseableUtils.closeQuietly(server); } } private enum Mode { START_IMMEDIATELY, START_IN_THREADS } private void basic(Mode mode) throws Exception { final int PARTICIPANT_QTY = 1;//0; List latches = Lists.newArrayList(); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); for ( int i = 0; i < PARTICIPANT_QTY; ++i ) { LeaderLatch latch = new LeaderLatch(client, PATH_NAME); if ( mode == Mode.START_IMMEDIATELY ) { latch.start(); } latches.add(latch); } if ( mode == Mode.START_IN_THREADS ) { ExecutorService service = Executors.newFixedThreadPool(latches.size()); for ( final LeaderLatch latch : latches ) { service.submit(new Callable() { @Override public Object call() throws Exception { Thread.sleep((int)(100 * Math.random())); latch.start(); return null; } }); } service.shutdown(); } while ( latches.size() > 0 ) { List leaders = waitForALeader(latches, timing); assertEquals(leaders.size(), 1); // there can only be one leader LeaderLatch theLeader = leaders.get(0); if ( mode == Mode.START_IMMEDIATELY ) { assertEquals(latches.indexOf(theLeader), 0); // assert ordering - leadership should advance in start order } theLeader.close(); latches.remove(theLeader); } } finally { for ( LeaderLatch latch : latches ) { CloseableUtils.closeQuietly(latch); } TestCleanState.closeAndTestClean(client); } } private List waitForALeader(List latches, Timing timing) throws InterruptedException { for ( int i = 0; i < MAX_LOOPS; ++i ) { List leaders = getLeaders(latches); if ( leaders.size() != 0 ) { return leaders; } timing.sleepABit(); } return Lists.newArrayList(); } private List getLeaders(Collection latches) { List leaders = Lists.newArrayList(); for ( LeaderLatch latch : latches ) { if ( latch.hasLeadership() ) { leaders.add(latch); } } return leaders; } @Test public void testRelativePath() { assertThrows(IllegalArgumentException.class, ()->{ Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); new LeaderLatch(client, "parent"); }); } } TestLeaderLatchCluster.java000066400000000000000000000106061442004423600414040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.List; public class TestLeaderLatchCluster extends CuratorTestBase { private static final int MAX_LOOPS = 5; private static class ClientAndLatch { final CuratorFramework client; final LeaderLatch latch; final int index; private ClientAndLatch(CuratorFramework client, LeaderLatch latch, int index) { this.client = client; this.latch = latch; this.index = index; } } @Test public void testInCluster() throws Exception { final int PARTICIPANT_QTY = 3; final int sessionLength = timing.session() / 4; List clients = Lists.newArrayList(); TestingCluster cluster = createAndStartCluster(PARTICIPANT_QTY); try { List instances = Lists.newArrayList(cluster.getInstances()); for ( int i = 0; i < PARTICIPANT_QTY; ++i ) { CuratorFramework client = CuratorFrameworkFactory.newClient(instances.get(i).getConnectString(), sessionLength, sessionLength, new ExponentialBackoffRetry(100, 3)); LeaderLatch latch = new LeaderLatch(client, "/latch"); clients.add(new ClientAndLatch(client, latch, i)); client.start(); latch.start(); } ClientAndLatch leader = waitForALeader(clients, timing); assertNotNull(leader); cluster.killServer(instances.get(leader.index)); Thread.sleep(sessionLength * 2); leader = waitForALeader(clients, timing); assertNotNull(leader); assertEquals(getLeaders(clients).size(), 1); } finally { for ( ClientAndLatch client : clients ) { CloseableUtils.closeQuietly(client.latch); CloseableUtils.closeQuietly(client.client); } CloseableUtils.closeQuietly(cluster); } } @Override protected void createServer() { // NOP } private ClientAndLatch waitForALeader(List latches, Timing2 timing) throws InterruptedException { for ( int i = 0; i < MAX_LOOPS; ++i ) { List leaders = getLeaders(latches); if ( leaders.size() != 0 ) { return leaders.get(0); } timing.sleepABit(); } return null; } private List getLeaders(Collection latches) { List leaders = Lists.newArrayList(); for ( ClientAndLatch clientAndLatch : latches ) { if ( clientAndLatch.latch.hasLeadership() ) { leaders.add(clientAndLatch); } } return leaders; } } TestLeaderSelector.java000066400000000000000000001001141442004423600405610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import com.google.common.collect.Sets; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.framework.state.SessionConnectionStateErrorPolicy; import org.apache.curator.framework.state.StandardConnectionStateErrorPolicy; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.TestingServer; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class TestLeaderSelector extends BaseClassForTests { private static final String PATH_NAME = "/one/two/me"; @Test public void testInterruption() throws Exception { Timing2 timing = new Timing2(); LeaderSelector selector = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); CountDownLatch exitLatch = new CountDownLatch(1); BlockingQueue threadExchange = new ArrayBlockingQueue<>(1); LeaderSelectorListener listener = new LeaderSelectorListenerAdapter() { @Override public void takeLeadership(CuratorFramework client) throws Exception { threadExchange.put(Thread.currentThread()); try { Thread.currentThread().join(); } finally { exitLatch.countDown(); } } }; selector = new LeaderSelector(client, PATH_NAME, listener); selector.failedMutexReleaseCount = new AtomicInteger(); selector.start(); Thread leaderThread = timing.takeFromQueue(threadExchange); leaderThread.interrupt(); assertTrue(timing.awaitLatch(exitLatch)); timing.sleepABit(); // wait for leader selector to clear nodes assertEquals(0, selector.failedMutexReleaseCount.get()); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } @Test public void testErrorPolicies() throws Exception { Timing2 timing = new Timing2(); LeaderSelector selector = null; CuratorFramework client = CuratorFrameworkFactory .builder() .connectString(server.getConnectString()) .connectionTimeoutMs(timing.connection()) .sessionTimeoutMs(timing.session()) .retryPolicy(new RetryOneTime(1)) .connectionStateErrorPolicy(new StandardConnectionStateErrorPolicy()) .build(); try { final BlockingQueue changes = Queues.newLinkedBlockingQueue(); ConnectionStateListener stateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { changes.add(newState.name()); } }; client.getConnectionStateListenable().addListener(stateListener); client.start(); LeaderSelectorListener listener = new LeaderSelectorListenerAdapter() { @Override public void takeLeadership(CuratorFramework client) throws Exception { changes.add("leader"); try { Thread.currentThread().join(); } catch ( InterruptedException e ) { changes.add("release"); Thread.currentThread().interrupt(); } } }; selector = new LeaderSelector(client, "/test", listener); selector.start(); assertEquals(changes.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED.name()); assertEquals(changes.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), "leader"); server.close(); List next = Lists.newArrayList(); next.add(changes.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS)); next.add(changes.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS)); assertTrue(next.equals(Arrays.asList(ConnectionState.SUSPENDED.name(), "release")) || next.equals(Arrays.asList("release", ConnectionState.SUSPENDED.name())), next.toString()); assertEquals(changes.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.LOST.name()); selector.close(); client.close(); timing.sleepABit(); changes.clear(); server = new TestingServer(); client = CuratorFrameworkFactory .builder() .connectString(server.getConnectString()) .connectionTimeoutMs(timing.connection()) .sessionTimeoutMs(timing.session()) .retryPolicy(new RetryOneTime(1)) .connectionStateErrorPolicy(new SessionConnectionStateErrorPolicy()) .build(); client.getConnectionStateListenable().addListener(stateListener); client.start(); selector = new LeaderSelector(client, "/test", listener); selector.start(); assertEquals(changes.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.CONNECTED.name()); assertEquals(changes.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), "leader"); server.stop(); assertEquals(changes.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), ConnectionState.SUSPENDED.name()); next = Lists.newArrayList(); next.add(changes.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS)); next.add(changes.poll(timing.forSessionSleep().milliseconds(), TimeUnit.MILLISECONDS)); assertTrue(next.equals(Arrays.asList(ConnectionState.LOST.name(), "release")) || next.equals(Arrays.asList("release", ConnectionState.LOST.name())), next.toString()); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } @Test public void testLeaderNodeDeleteOnInterrupt() throws Exception { Timing2 timing = new Timing2(); LeaderSelector selector = null; CuratorFramework client = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); final CountDownLatch reconnectedLatch = new CountDownLatch(1); ConnectionStateListener connectionStateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.RECONNECTED ) { reconnectedLatch.countDown(); } } }; client.getConnectionStateListenable().addListener(connectionStateListener); client.start(); final BlockingQueue queue = new ArrayBlockingQueue(1); LeaderSelectorListener listener = new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { queue.add(Thread.currentThread()); try { Thread.currentThread().join(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; selector = new LeaderSelector(client, "/leader", listener); selector.start(); Thread leaderThread = timing.takeFromQueue(queue); server.stop(); leaderThread.interrupt(); server.restart(); assertTrue(timing.awaitLatch(reconnectedLatch)); timing.sleepABit(); assertEquals(client.getChildren().forPath("/leader").size(), 0); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } private static class DelayedExecutorService extends AbstractExecutorService { private final ExecutorService executor = Executors.newFixedThreadPool(4); @Override public void shutdown() { executor.shutdown(); } @Override public List shutdownNow() { return executor.shutdownNow(); } @Override public boolean isShutdown() { return executor.isShutdown(); } @Override public boolean isTerminated() { return executor.isTerminated(); } @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return executor.awaitTermination(timeout, unit); } @Override public void execute(Runnable command) { executor.execute(() -> { try { Thread.sleep(100); } catch (InterruptedException ignored) { // Mimic cancel before execution. return; } command.run(); }); } } @Test public void testInterruptLeadershipWithRequeue() throws Exception { Timing timing = new Timing(); LeaderSelector selector = null; CuratorFramework client = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); final Semaphore semaphore = new Semaphore(0); LeaderSelectorListener listener = new LeaderSelectorListenerAdapter() { @Override public void takeLeadership(CuratorFramework client) throws Exception { semaphore.release(); Thread.currentThread().join(); } }; selector = new LeaderSelector(client, "/leader", new DelayedExecutorService(), listener); selector.autoRequeue(); selector.start(); // There is only one participant, so semaphore acquire should succeed in finite // duration no matter how we interrupt. assertTrue(timing.acquireSemaphore(semaphore)); Thread.sleep(20); selector.interruptLeadership(); Thread.sleep(20); selector.interruptLeadership(); assertTrue(timing.acquireSemaphore(semaphore)); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } @Test public void testInterruptLeadership() throws Exception { LeaderSelector selector = null; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final CountDownLatch isLeaderLatch = new CountDownLatch(1); final CountDownLatch losingLeaderLatch = new CountDownLatch(1); LeaderSelectorListener listener = new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { isLeaderLatch.countDown(); try { Thread.currentThread().join(); } finally { losingLeaderLatch.countDown(); } } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; selector = new LeaderSelector(client, "/leader", listener); selector.start(); assertTrue(timing.awaitLatch(isLeaderLatch)); selector.interruptLeadership(); assertTrue(timing.awaitLatch(losingLeaderLatch)); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } @Test public void testRaceAtStateChanged() throws Exception { LeaderSelector selector = null; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final CountDownLatch takeLeadershipLatch = new CountDownLatch(1); final CountDownLatch lostLatch = new CountDownLatch(1); final CountDownLatch reconnectedLatch = new CountDownLatch(1); LeaderSelectorListener listener = new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { takeLeadershipLatch.countDown(); // should never get here } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.RECONNECTED ) { reconnectedLatch.countDown(); } else if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); throw new CancelLeadershipException(); } } }; selector = new LeaderSelector(client, "/leader", listener); CountDownLatch debugLeadershipLatch = new CountDownLatch(1); CountDownLatch debugLeadershipWaitLatch = new CountDownLatch(1); selector.debugLeadershipLatch = debugLeadershipLatch; selector.debugLeadershipWaitLatch = debugLeadershipWaitLatch; selector.start(); assertTrue(timing.awaitLatch(debugLeadershipLatch)); server.stop(); assertTrue(timing.awaitLatch(lostLatch)); timing.sleepABit(); debugLeadershipWaitLatch.countDown(); server.restart(); assertTrue(timing.awaitLatch(reconnectedLatch)); assertFalse(takeLeadershipLatch.await(3, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } @Test public void testAutoRequeue() throws Exception { Timing timing = new Timing(); LeaderSelector selector = null; CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).sessionTimeoutMs(timing.session()).build(); try { client.start(); final Semaphore semaphore = new Semaphore(0); LeaderSelectorListener listener = new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { Thread.sleep(10); semaphore.release(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; selector = new LeaderSelector(client, "/leader", listener); selector.autoRequeue(); selector.start(); assertTrue(timing.acquireSemaphore(semaphore, 2)); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } @Test public void testServerDying() throws Exception { Timing timing = new Timing(); LeaderSelector selector = null; CuratorFramework client = CuratorFrameworkFactory.builder().connectionTimeoutMs(timing.connection()).connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).sessionTimeoutMs(timing.session()).build(); client.start(); try { final Semaphore semaphore = new Semaphore(0); LeaderSelectorListener listener = new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { semaphore.release(); Thread.sleep(Integer.MAX_VALUE); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { semaphore.release(); } } }; selector = new LeaderSelector(client, "/leader", listener); selector.start(); timing.acquireSemaphore(semaphore); server.close(); timing.acquireSemaphore(semaphore); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } @Test public void testKillSessionThenCloseShouldElectNewLeader() throws Exception { final Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final Semaphore semaphore = new Semaphore(0); final CountDownLatch interruptedLatch = new CountDownLatch(1); final AtomicInteger leaderCount = new AtomicInteger(0); LeaderSelectorListener listener = new LeaderSelectorListenerAdapter() { @Override public void takeLeadership(CuratorFramework client) throws Exception { leaderCount.incrementAndGet(); try { semaphore.release(); try { Thread.currentThread().join(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); interruptedLatch.countDown(); } } finally { leaderCount.decrementAndGet(); } } }; LeaderSelector leaderSelector1 = new LeaderSelector(client, PATH_NAME, listener); LeaderSelector leaderSelector2 = new LeaderSelector(client, PATH_NAME, listener); boolean leaderSelector1Closed = false; boolean leaderSelector2Closed = false; leaderSelector1.start(); leaderSelector2.start(); assertTrue(timing.acquireSemaphore(semaphore, 1)); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertTrue(timing.awaitLatch(interruptedLatch)); timing.sleepABit(); boolean requeued1 = leaderSelector1.requeue(); boolean requeued2 = leaderSelector2.requeue(); assertTrue(requeued1); assertTrue(requeued2); assertTrue(timing.acquireSemaphore(semaphore, 1)); assertEquals(leaderCount.get(), 1); if ( leaderSelector1.hasLeadership() ) { leaderSelector1.close(); leaderSelector1Closed = true; } else if ( leaderSelector2.hasLeadership() ) { leaderSelector2.close(); leaderSelector2Closed = true; } else { fail("No leaderselector has leadership!"); } // Verify that the other leader took over leadership. assertTrue(timing.acquireSemaphore(semaphore, 1)); assertEquals(leaderCount.get(), 1); if ( !leaderSelector1Closed ) { leaderSelector1.close(); } if ( !leaderSelector2Closed ) { leaderSelector2.close(); } } finally { client.close(); } } /** * This is similar to TestLeaderSelector.testKillSessionThenCloseShouldElectNewLeader * The differences are: * it restarts the TestingServer instead of killing the session * it uses autoRequeue instead of explicitly calling requeue */ @Test public void testKillServerThenCloseShouldElectNewLeader() throws Exception { final Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final Semaphore semaphore = new Semaphore(0); final CountDownLatch interruptedLatch = new CountDownLatch(1); final AtomicInteger leaderCount = new AtomicInteger(0); LeaderSelectorListener listener = new LeaderSelectorListenerAdapter() { @Override public void takeLeadership(CuratorFramework client) throws Exception { leaderCount.incrementAndGet(); try { semaphore.release(); try { Thread.currentThread().join(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); interruptedLatch.countDown(); } } finally { leaderCount.decrementAndGet(); } } }; LeaderSelector leaderSelector1 = new LeaderSelector(client, PATH_NAME, listener); LeaderSelector leaderSelector2 = new LeaderSelector(client, PATH_NAME, listener); boolean leaderSelector1Closed = false; boolean leaderSelector2Closed = false; leaderSelector1.autoRequeue(); leaderSelector2.autoRequeue(); leaderSelector1.start(); leaderSelector2.start(); assertTrue(timing.acquireSemaphore(semaphore, 1)); int port = server.getPort(); server.stop(); timing.sleepABit(); server = new TestingServer(port); assertTrue(timing.awaitLatch(interruptedLatch)); timing.sleepABit(); assertTrue(timing.acquireSemaphore(semaphore, 1)); assertEquals(leaderCount.get(), 1); if ( leaderSelector1.hasLeadership() ) { leaderSelector1.close(); leaderSelector1Closed = true; } else if ( leaderSelector2.hasLeadership() ) { leaderSelector2.close(); leaderSelector2Closed = true; } else { fail("No leaderselector has leadership!"); } // Verify that the other leader took over leadership. assertTrue(timing.acquireSemaphore(semaphore, 1)); assertEquals(leaderCount.get(), 1); if ( !leaderSelector1Closed ) { leaderSelector1.close(); } if ( !leaderSelector2Closed ) { leaderSelector2.close(); } } finally { client.close(); } } @Test public void testClosing() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final CountDownLatch latch = new CountDownLatch(1); LeaderSelector leaderSelector1 = new LeaderSelector(client, PATH_NAME, new LeaderSelectorListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } @Override public void takeLeadership(CuratorFramework client) throws Exception { latch.await(10, TimeUnit.SECONDS); } }); LeaderSelector leaderSelector2 = new LeaderSelector(client, PATH_NAME, new LeaderSelectorListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } @Override public void takeLeadership(CuratorFramework client) throws Exception { latch.await(10, TimeUnit.SECONDS); } }); leaderSelector1.start(); leaderSelector2.start(); while ( !leaderSelector1.hasLeadership() && !leaderSelector2.hasLeadership() ) { Thread.sleep(1000); } assertNotSame(leaderSelector1.hasLeadership(), leaderSelector2.hasLeadership()); LeaderSelector positiveLeader; LeaderSelector negativeLeader; if ( leaderSelector1.hasLeadership() ) { positiveLeader = leaderSelector1; negativeLeader = leaderSelector2; } else { positiveLeader = leaderSelector2; negativeLeader = leaderSelector1; } negativeLeader.close(); Thread.sleep(1000); assertNotSame(positiveLeader.hasLeadership(), negativeLeader.hasLeadership()); assertTrue(positiveLeader.hasLeadership()); positiveLeader.close(); Thread.sleep(1000); assertFalse(positiveLeader.hasLeadership()); } finally { client.close(); } } @SuppressWarnings({"ForLoopReplaceableByForEach"}) @Test public void testRotatingLeadership() throws Exception { final int LEADER_QTY = 5; final int REPEAT_QTY = 3; final Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final BlockingQueue leaderList = new LinkedBlockingQueue(); List selectors = Lists.newArrayList(); for ( int i = 0; i < LEADER_QTY; ++i ) { final int ourIndex = i; LeaderSelector leaderSelector = new LeaderSelector(client, PATH_NAME, new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { timing.sleepABit(); leaderList.add(ourIndex); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }); selectors.add(leaderSelector); } List localLeaderList = Lists.newArrayList(); for ( int i = 1; i <= REPEAT_QTY; ++i ) { for ( LeaderSelector leaderSelector : selectors ) { if ( i > 1 ) { leaderSelector.requeue(); } else { leaderSelector.start(); } } while ( localLeaderList.size() != (i * selectors.size()) ) { Integer polledIndex = leaderList.poll(10, TimeUnit.SECONDS); assertNotNull(polledIndex); localLeaderList.add(polledIndex); } timing.sleepABit(); } for ( LeaderSelector leaderSelector : selectors ) { leaderSelector.close(); } System.out.println(localLeaderList); for ( int i = 0; i < REPEAT_QTY; ++i ) { Set uniques = Sets.newHashSet(); for ( int j = 0; j < selectors.size(); ++j ) { assertTrue(localLeaderList.size() > 0); int thisIndex = localLeaderList.remove(0); assertFalse(uniques.contains(thisIndex)); uniques.add(thisIndex); } } } finally { client.close(); } } } TestLeaderSelectorCluster.java000066400000000000000000000165601442004423600421360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.Timing; import org.apache.curator.utils.ZKPaths; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public class TestLeaderSelectorCluster extends CuratorTestBase { @Test public void testRestart() throws Exception { final Timing timing = new Timing(); CuratorFramework client = null; TestingCluster cluster = createAndStartCluster(3); try { client = CuratorFrameworkFactory.newClient(cluster.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); final Semaphore semaphore = new Semaphore(0); LeaderSelectorListener listener = new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { List names = client.getChildren().forPath("/leader"); assertTrue(names.size() > 0); semaphore.release(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; LeaderSelector selector = new LeaderSelector(client, "/leader", listener); selector.autoRequeue(); selector.start(); assertTrue(timing.acquireSemaphore(semaphore)); InstanceSpec connectionInstance = cluster.findConnectionInstance(client.getZookeeperClient().getZooKeeper()); cluster.killServer(connectionInstance); assertTrue(timing.multiple(4).acquireSemaphore(semaphore)); } finally { CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(cluster); } } @Test public void testLostRestart() throws Exception { final Timing timing = new Timing(); CuratorFramework client = null; TestingCluster cluster = createAndStartCluster(3); try { client = CuratorFrameworkFactory.newClient(cluster.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); client.sync().forPath("/"); final AtomicReference error = new AtomicReference(null); final AtomicReference lockNode = new AtomicReference(null); final Semaphore semaphore = new Semaphore(0); final CountDownLatch lostLatch = new CountDownLatch(1); final CountDownLatch internalLostLatch = new CountDownLatch(1); LeaderSelectorListener listener = new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { try { List names = client.getChildren().forPath("/leader"); if ( names.size() != 1 ) { semaphore.release(); Exception exception = new Exception("Names size isn't 1: " + names.size()); error.set(exception); return; } lockNode.set(names.get(0)); semaphore.release(); if ( !timing.multiple(4).awaitLatch(internalLostLatch) ) { error.set(new Exception("internalLostLatch await failed")); } } finally { lostLatch.countDown(); } } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { internalLostLatch.countDown(); } } }; LeaderSelector selector = new LeaderSelector(client, "/leader", listener); selector.start(); assertTrue(timing.multiple(4).acquireSemaphore(semaphore)); if ( error.get() != null ) { throw new AssertionError(error.get()); } Collection instances = cluster.getInstances(); cluster.stop(); assertTrue(timing.multiple(4).awaitLatch(lostLatch)); timing.sleepABit(); assertFalse(selector.hasLeadership()); assertNotNull(lockNode.get()); cluster = new TestingCluster(instances.toArray(new InstanceSpec[instances.size()])); cluster.start(); try { client.delete().forPath(ZKPaths.makePath("/leader", lockNode.get())); // simulate the lock deleting due to session expiration } catch ( Exception ignore ) { // ignore } assertTrue(semaphore.availablePermits() == 0); assertFalse(selector.hasLeadership()); selector.requeue(); assertTrue(timing.multiple(4).acquireSemaphore(semaphore)); } finally { CloseableUtils.closeQuietly(client); CloseableUtils.closeQuietly(cluster); } } @Override protected void createServer() throws Exception { // NOP } } TestLeaderSelectorEdges.java000066400000000000000000000230461442004423600415410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.test.BaseClassForTests; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.server.ServerCnxnFactory; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Test cases designed after CURATOR-45 */ public class TestLeaderSelectorEdges extends BaseClassForTests { private final Logger log = LoggerFactory.getLogger(getClass()); @BeforeAll public static void setCNXFactory() { System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, ChaosMonkeyCnxnFactory.class.getName()); } @AfterAll public static void resetCNXFactory() { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); } /** * Create a LeaderSelector but close the connection right after the "lock" znode * has been created. * * @throws Exception */ @Test public void flappingTest() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryNTimes(1, 500)) .sessionTimeoutMs(30000) .build(); final TestLeaderSelectorListener listener = new TestLeaderSelectorListener(); LeaderSelector leaderSelector1 = new LeaderSelector(client, ChaosMonkeyCnxnFactory.CHAOS_ZNODE, listener); LeaderSelector leaderSelector2 = null; client.start(); try { client.create().forPath(ChaosMonkeyCnxnFactory.CHAOS_ZNODE); leaderSelector1.start(); // At this point the ChaosMonkeyZookeeperServer must close the connection // right after the lock znode is created. assertTrue(listener.reconnected.await(10, TimeUnit.SECONDS), "Connection has not been lost"); // Check that leader ship has failed assertEquals(listener.takeLeadership.getCount(), 1); // Wait FailedDelete Thread.sleep(ChaosMonkeyCnxnFactory.LOCKOUT_DURATION_MS * 2); // Check that there is no znode final int children = client.getChildren().forPath(ChaosMonkeyCnxnFactory.CHAOS_ZNODE).size(); assertEquals(children, 0, "Still " + children + " znodes under " + ChaosMonkeyCnxnFactory.CHAOS_ZNODE + " lock"); // Check that a new LeaderSelector can be started leaderSelector2 = new LeaderSelector(client, ChaosMonkeyCnxnFactory.CHAOS_ZNODE, listener); leaderSelector2.start(); assertTrue(listener.takeLeadership.await(1, TimeUnit.SECONDS)); } finally { try { leaderSelector1.close(); } catch ( IllegalStateException e ) { fail(e.getMessage()); } try { if ( leaderSelector2 != null ) { leaderSelector2.close(); } } catch ( IllegalStateException e ) { fail(e.getMessage()); } client.close(); } } private class TestLeaderSelectorListener implements LeaderSelectorListener { final CountDownLatch takeLeadership = new CountDownLatch(1); final CountDownLatch reconnected = new CountDownLatch(1); @Override public void takeLeadership(CuratorFramework client) throws Exception { log.info("-->takeLeadership({})", client.toString()); takeLeadership.countDown(); log.info("<--takeLeadership({})", client.toString()); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.RECONNECTED ) { reconnected.countDown(); } } } /** * Create a protected node in background with a retry policy */ @Test public void createProtectedNodeInBackgroundTest() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryNTimes(2, 100)) .connectionTimeoutMs(1000) .sessionTimeoutMs(60000) .build(); final CountDownLatch latch = new CountDownLatch(1); client.start(); try { client.create().forPath(ChaosMonkeyCnxnFactory.CHAOS_ZNODE); client.create() .withProtection() .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) .inBackground( new BackgroundCallback() { public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { log.info("Receive event {}", event.toString()); if ( event.getResultCode() == KeeperException.Code.CONNECTIONLOSS.intValue() ) { latch.countDown(); } } } ) .forPath(ChaosMonkeyCnxnFactory.CHAOS_ZNODE_PREFIX + "foo-"); assertTrue(latch.await(30, TimeUnit.SECONDS), "Callback has not been called"); // Wait for the znode to be deleted Thread.sleep(ChaosMonkeyCnxnFactory.LOCKOUT_DURATION_MS * 2); // Check that there is no znode final int children = client.getChildren().forPath(ChaosMonkeyCnxnFactory.CHAOS_ZNODE).size(); assertEquals(children, 0, "Still " + children + " znodes under " + ChaosMonkeyCnxnFactory.CHAOS_ZNODE + " lock"); } finally { client.close(); } } /** * Same test as above but without a retry policy */ @Test public void createProtectedNodeInBackgroundTestNoRetry() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryNTimes(0, 0)) .connectionTimeoutMs(1000) .sessionTimeoutMs(60000) .build(); final CountDownLatch latch = new CountDownLatch(1); client.start(); try { client.create().forPath(ChaosMonkeyCnxnFactory.CHAOS_ZNODE); client.create() .withProtection() .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) .inBackground( new BackgroundCallback() { public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { log.info("Receive event {}", event.toString()); if ( event.getResultCode() == KeeperException.Code.CONNECTIONLOSS.intValue() ) { latch.countDown(); } } } ) .forPath(ChaosMonkeyCnxnFactory.CHAOS_ZNODE_PREFIX + "foo-"); assertTrue(latch.await(30, TimeUnit.SECONDS), "Callback has not been called"); // Wait for the znode to be deleted Thread.sleep(ChaosMonkeyCnxnFactory.LOCKOUT_DURATION_MS * 2); // Check that there is no znode final int children = client.getChildren().forPath(ChaosMonkeyCnxnFactory.CHAOS_ZNODE).size(); assertEquals(children, 0, "Still " + children + " znodes under " + ChaosMonkeyCnxnFactory.CHAOS_ZNODE + " lock"); } finally { client.close(); } } } TestLeaderSelectorParticipants.java000066400000000000000000000145141442004423600431530ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryOneTime; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestLeaderSelectorParticipants extends BaseClassForTests { @Test public void testId() throws Exception { LeaderSelector selector = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); final CountDownLatch latch = new CountDownLatch(1); LeaderSelectorListener listener = new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { latch.countDown(); Thread.currentThread().join(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; selector = new LeaderSelector(client, "/ls", listener); selector.setId("A is A"); selector.start(); assertTrue(latch.await(10, TimeUnit.SECONDS)); Participant leader = selector.getLeader(); assertTrue(leader.isLeader()); assertEquals(leader.getId(), "A is A"); Collection participants = selector.getParticipants(); assertEquals(participants.size(), 1); assertEquals(participants.iterator().next().getId(), "A is A"); assertEquals(participants.iterator().next().getId(), selector.getId()); } finally { CloseableUtils.closeQuietly(selector); CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { final int SELECTOR_QTY = 10; List selectors = Lists.newArrayList(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); final CountDownLatch leaderLatch = new CountDownLatch(1); final CountDownLatch workingLatch = new CountDownLatch(SELECTOR_QTY); LeaderSelectorListener listener = new LeaderSelectorListener() { @Override public void takeLeadership(CuratorFramework client) throws Exception { leaderLatch.countDown(); Thread.currentThread().join(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; for ( int i = 0; i < SELECTOR_QTY; ++i ) { LeaderSelector selector = new LeaderSelector(client, "/ls", listener) { @Override void doWork() throws Exception { workingLatch.countDown(); super.doWork(); } }; selector.setId(Integer.toString(i)); selectors.add(selector); } for ( LeaderSelector selector : selectors ) { selector.start(); } assertTrue(leaderLatch.await(10, TimeUnit.SECONDS)); assertTrue(workingLatch.await(10, TimeUnit.SECONDS)); Thread.sleep(1000); // some time for locks to acquire Collection participants = selectors.get(0).getParticipants(); for ( int i = 1; i < selectors.size(); ++i ) { assertEquals(participants, selectors.get(i).getParticipants()); } Set ids = Sets.newHashSet(); int leaderCount = 0; for ( Participant participant : participants ) { if ( participant.isLeader() ) { ++leaderCount; } assertFalse(ids.contains(participant.getId())); ids.add(participant.getId()); } assertEquals(leaderCount, 1); Set expectedIds = Sets.newHashSet(); for ( int i = 0; i < SELECTOR_QTY; ++i ) { expectedIds.add(Integer.toString(i)); } assertEquals(expectedIds, ids); } finally { for ( LeaderSelector selector : selectors ) { CloseableUtils.closeQuietly(selector); } CloseableUtils.closeQuietly(client); } } } TestLeaderSelectorWithExecutor.java000066400000000000000000000100321442004423600431330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/leader/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.leader; import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.Timing; import org.apache.curator.utils.ThreadUtils; import org.junit.jupiter.api.Test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; public class TestLeaderSelectorWithExecutor extends BaseClassForTests { private static final ThreadFactory threadFactory = ThreadUtils.newThreadFactory("FeedGenerator"); @Test public void test() throws Exception { Timing timing = new Timing(); LeaderSelector leaderSelector = null; CuratorFramework client = CuratorFrameworkFactory.builder() .retryPolicy(new ExponentialBackoffRetry(100, 3)) .connectString(server.getConnectString()) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()) .build(); try { client.start(); MyLeaderSelectorListener listener = new MyLeaderSelectorListener(); ExecutorService executorPool = Executors.newFixedThreadPool(20); leaderSelector = new LeaderSelector(client, "/test", threadFactory, executorPool, listener); leaderSelector.autoRequeue(); leaderSelector.start(); timing.sleepABit(); assertEquals(listener.getLeaderCount(), 1); } finally { CloseableUtils.closeQuietly(leaderSelector); CloseableUtils.closeQuietly(client); } } private class MyLeaderSelectorListener implements LeaderSelectorListener { private volatile Thread ourThread; private final AtomicInteger leaderCount = new AtomicInteger(0); public int getLeaderCount() { return leaderCount.get(); } @Override public void takeLeadership(CuratorFramework curatorFramework) throws Exception { ourThread = Thread.currentThread(); try { leaderCount.incrementAndGet(); while ( !Thread.currentThread().isInterrupted() ) { Thread.sleep(1000); } } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } finally { leaderCount.decrementAndGet(); } } @Override public void stateChanged(CuratorFramework curatorFramework, ConnectionState newState) { if ( (newState == ConnectionState.LOST) || (newState == ConnectionState.SUSPENDED) ) { if ( ourThread != null ) { ourThread.interrupt(); } } } } } locks/000077500000000000000000000000001442004423600340435ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipesCounter.java000066400000000000000000000016501442004423600363270ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; class Counter { int currentCount = 0; int maxCount = 0; } SemaphoreClient.java000066400000000000000000000110331442004423600377660ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.Timing; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; class SemaphoreClient implements Callable, ConnectionStateListener, Closeable { private final CuratorFramework client; private final String semaphorePath; private final Callable operation; private volatile boolean shouldRun; private volatile boolean hasAcquired; private static final int CLIENT_EXCEPTION_HANDLER_SLEEP_TIME_SECS = 10; private static final int MAX_SEMAPHORE_LEASES = 1; private static final AtomicReference activeClient = new AtomicReference(null); SemaphoreClient(String connectionString, String semaphorePath, Callable operation) throws IOException { Timing timing = new Timing(); this.client = CuratorFrameworkFactory.newClient(connectionString, timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); client.start(); this.semaphorePath = semaphorePath; this.operation = operation; } @Override public void close() throws IOException { shouldRun = false; } boolean hasAcquired() { return hasAcquired; } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { hasAcquired = false; } static SemaphoreClient getActiveClient() { return activeClient.get(); } @Override public Void call() throws Exception { shouldRun = true; client.getConnectionStateListenable().addListener(this); try { while ( shouldRun ) { try { acquireAndRun(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); // propagate up, don't sleep throw e; } catch ( Exception e ) { Thread.sleep(CLIENT_EXCEPTION_HANDLER_SLEEP_TIME_SECS * 1000L); } } } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } finally { CloseableUtils.closeQuietly(client); } return null; } private void acquireAndRun() throws Exception { InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, semaphorePath, MAX_SEMAPHORE_LEASES); Lease lease = semaphore.acquire(); try { hasAcquired = true; if ( activeClient.compareAndSet(null, this) ) { throw new Exception("Multiple acquirers"); } try { while ( hasAcquired && shouldRun ) { operation.call(); } } finally { if ( activeClient.compareAndSet(this, null) ) { //noinspection ThrowFromFinallyBlock throw new Exception("Bad release"); } } } finally { semaphore.returnLease(lease); } } } Stepper.java000066400000000000000000000022771442004423600363400ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; class Stepper { private int available = 0; synchronized void await() throws InterruptedException { while ( available == 0 ) { wait(); } --available; notifyAll(); } synchronized void countDown(int qty) { available += qty; notifyAll(); } } TestInterProcessMultiMutex.java000066400000000000000000000126251442004423600422320ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.retry.RetryOneTime; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class TestInterProcessMultiMutex extends TestInterProcessMutexBase { private static final String LOCK_PATH_1 = LOCK_BASE_PATH + "/our-lock-1"; private static final String LOCK_PATH_2 = LOCK_BASE_PATH + "/our-lock-2"; @Override protected InterProcessLock makeLock(CuratorFramework client) { return new InterProcessMultiLock(client, Arrays.asList(LOCK_PATH_1, LOCK_PATH_2)); } @Test public void testSomeReleasesFail() throws IOException { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { InterProcessLock goodLock = new InterProcessMutex(client, LOCK_PATH_1); final InterProcessLock otherGoodLock = new InterProcessMutex(client, LOCK_PATH_2); InterProcessLock badLock = new InterProcessLock() { @Override public void acquire() throws Exception { otherGoodLock.acquire(); } @Override public boolean acquire(long time, TimeUnit unit) throws Exception { return otherGoodLock.acquire(time, unit); } @Override public void release() throws Exception { throw new Exception("foo"); } @Override public boolean isAcquiredInThisProcess() { return otherGoodLock.isAcquiredInThisProcess(); } }; InterProcessMultiLock lock = new InterProcessMultiLock(Arrays.asList(goodLock, badLock)); try { lock.acquire(); lock.release(); fail(); } catch ( Exception e ) { // ignore } assertFalse(goodLock.isAcquiredInThisProcess()); assertTrue(otherGoodLock.isAcquiredInThisProcess()); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testSomeLocksFailToLock() throws IOException { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final AtomicBoolean goodLockWasLocked = new AtomicBoolean(false); final InterProcessLock goodLock = new InterProcessMutex(client, LOCK_PATH_1); InterProcessLock badLock = new InterProcessLock() { @Override public void acquire() throws Exception { if ( goodLock.isAcquiredInThisProcess() ) { goodLockWasLocked.set(true); } throw new Exception("foo"); } @Override public boolean acquire(long time, TimeUnit unit) throws Exception { throw new Exception("foo"); } @Override public void release() throws Exception { throw new Exception("foo"); } @Override public boolean isAcquiredInThisProcess() { return false; } }; InterProcessMultiLock lock = new InterProcessMultiLock(Arrays.asList(goodLock, badLock)); try { lock.acquire(); fail(); } catch ( Exception e ) { // ignore } assertFalse(goodLock.isAcquiredInThisProcess()); assertTrue(goodLockWasLocked.get()); } finally { TestCleanState.closeAndTestClean(client); } } } TestInterProcessMutex.java000066400000000000000000000164321442004423600412170ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.framework.schema.Schema; import org.apache.curator.framework.schema.SchemaSet; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.CreateMode; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class TestInterProcessMutex extends TestInterProcessMutexBase { private static final String LOCK_PATH = LOCK_BASE_PATH + "/our-lock"; @Override protected InterProcessLock makeLock(CuratorFramework client) { return new InterProcessMutex(client, LOCK_PATH); } @Test public void testWithSchema() throws Exception { Schema schemaRoot = Schema.builderForRecipeParent("/foo").name("root").build(); Schema schemaLocks = Schema.builderForRecipe("/foo").name("locks").build(); SchemaSet schemaSet = new SchemaSet(Lists.newArrayList(schemaRoot, schemaLocks), false); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .schemaSet(schemaSet) .build(); try { client.start(); InterProcessMutex lock = new InterProcessMutex(client, "/foo"); lock.acquire(); lock.release(); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRevoking() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); final InterProcessMutex lock = new InterProcessMutex(client, LOCK_PATH); ExecutorService executorService = Executors.newCachedThreadPool(); final CountDownLatch revokeLatch = new CountDownLatch(1); final CountDownLatch lockLatch = new CountDownLatch(1); Future f1 = executorService.submit ( new Callable() { @Override public Void call() throws Exception { RevocationListener listener = new RevocationListener() { @Override public void revocationRequested(InterProcessMutex lock) { revokeLatch.countDown(); } }; lock.makeRevocable(listener); lock.acquire(); lockLatch.countDown(); revokeLatch.await(); lock.release(); return null; } } ); Future f2 = executorService.submit ( new Callable() { @Override public Void call() throws Exception { assertTrue(lockLatch.await(10, TimeUnit.SECONDS)); Collection nodes = lock.getParticipantNodes(); assertEquals(nodes.size(), 1); Revoker.attemptRevoke(client, nodes.iterator().next()); InterProcessMutex l2 = new InterProcessMutex(client, LOCK_PATH); assertTrue(l2.acquire(5, TimeUnit.SECONDS)); l2.release(); return null; } } ); f2.get(); f1.get(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testPersistentLock() throws Exception { final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final InterProcessMutex lock = new InterProcessMutex(client, LOCK_PATH, new StandardLockInternalsDriver() { @Override public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception { String ourPath; if ( lockNodeBytes != null ) { ourPath = client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.PERSISTENT).forPath(path, lockNodeBytes); } else { ourPath = client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.PERSISTENT).forPath(path); } return ourPath; } }); // Get a persistent lock lock.acquire(10, TimeUnit.SECONDS); assertTrue(lock.isAcquiredInThisProcess()); // Kill the session, check that lock node still exists client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertNotNull(client.checkExists().forPath(LOCK_PATH)); // Release the lock and verify that the actual lock node created no longer exists String actualLockPath = lock.getLockPath(); lock.release(); assertNull(client.checkExists().forPath(actualLockPath)); } finally { TestCleanState.closeAndTestClean(client); } } } TestInterProcessMutexBase.java000066400000000000000000000513671442004423600420200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.TestingServer; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.KeeperException; import org.junit.jupiter.api.Test; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; public abstract class TestInterProcessMutexBase extends BaseClassForTests { protected static final String LOCK_BASE_PATH = "/locks"; private volatile CountDownLatch waitLatchForBar = null; private volatile CountDownLatch countLatchForBar = null; protected abstract InterProcessLock makeLock(CuratorFramework client); @Test public void testLocker() throws Exception { final Timing timing = new Timing(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); try { client.start(); InterProcessLock lock = makeLock(client); try ( Locker locker = new Locker(lock, timing.milliseconds(), TimeUnit.MILLISECONDS) ) { assertTrue(lock.isAcquiredInThisProcess()); } assertFalse(lock.isAcquiredInThisProcess()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testWaitingProcessKilledServer() throws Exception { final Timing timing = new Timing(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); try { client.start(); final CountDownLatch latch = new CountDownLatch(1); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( !newState.isConnected() ) { latch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); final AtomicBoolean isFirst = new AtomicBoolean(true); final Object result = new Object(); ExecutorCompletionService service = new ExecutorCompletionService(Executors.newFixedThreadPool(2)); for ( int i = 0; i < 2; ++i ) { service.submit ( new Callable() { @Override public Object call() throws Exception { InterProcessLock lock = makeLock(client); lock.acquire(); try { if ( isFirst.compareAndSet(true, false) ) { timing.sleepABit(); server.stop(); assertTrue(timing.forWaiting().awaitLatch(latch)); server.restart(); } } finally { try { lock.release(); } catch ( KeeperException.SessionExpiredException dummy ) { // happens sometimes with a few tests - ignore } } return result; } } ); } for ( int i = 0; i < 2; ++i ) { assertEquals(service.take().get(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS), result); } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testKilledSession() throws Exception { final Timing2 timing = new Timing2(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); client.start(); try { final InterProcessLock mutex1 = makeLock(client); final InterProcessLock mutex2 = makeLock(client); final Semaphore semaphore = new Semaphore(0); ExecutorCompletionService service = new ExecutorCompletionService(Executors.newFixedThreadPool(2)); service.submit ( new Callable() { @Override public Object call() throws Exception { mutex1.acquire(); semaphore.release(); Thread.sleep(1000000); return null; } } ); service.submit ( new Callable() { @Override public Object call() throws Exception { mutex2.acquire(); semaphore.release(); Thread.sleep(1000000); return null; } } ); assertTrue(timing.acquireSemaphore(semaphore, 1)); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); assertTrue(timing.forSessionSleep().acquireSemaphore(semaphore, 1)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testContainerCleanup() throws Exception { if ( !ZKPaths.hasContainerSupport() ) { System.out.println("ZooKeeper version does not support Containers. Skipping test"); return; } server.close(); System.setProperty("znode.container.checkIntervalMs", "10"); try { server = new TestingServer(); final int THREAD_QTY = 10; ExecutorService service = null; final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(100, 3)); try { client.start(); List> threads = Lists.newArrayList(); service = Executors.newCachedThreadPool(); for ( int i = 0; i < THREAD_QTY; ++i ) { Future t = service.submit ( new Callable() { @Override public Object call() throws Exception { InterProcessLock lock = makeLock(client); lock.acquire(); try { Thread.sleep(10); } finally { lock.release(); } return null; } } ); threads.add(t); } for ( Future t : threads ) { t.get(); } new Timing().sleepABit(); assertNull(client.checkExists().forPath(LOCK_BASE_PATH)); } finally { if ( service != null ) { service.shutdownNow(); } CloseableUtils.closeQuietly(client); } } finally { System.clearProperty("znode.container.checkIntervalMs"); } } @Test public void testWithNamespace() throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder(). connectString(server.getConnectString()). retryPolicy(new ExponentialBackoffRetry(100, 3)). namespace("test"). build(); client.start(); try { InterProcessLock mutex = makeLock(client); mutex.acquire(10, TimeUnit.SECONDS); Thread.sleep(100); mutex.release(); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testReentrantSingleLock() throws Exception { final int THREAD_QTY = 10; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(100, 3)); client.start(); try { final AtomicBoolean hasLock = new AtomicBoolean(false); final AtomicBoolean isFirst = new AtomicBoolean(true); final Semaphore semaphore = new Semaphore(1); final InterProcessLock mutex = makeLock(client); List> threads = Lists.newArrayList(); ExecutorService service = Executors.newCachedThreadPool(); for ( int i = 0; i < THREAD_QTY; ++i ) { Future t = service.submit ( new Callable() { @Override public Object call() throws Exception { semaphore.acquire(); mutex.acquire(); try { assertTrue(hasLock.compareAndSet(false, true)); if ( isFirst.compareAndSet(true, false) ) { semaphore.release(THREAD_QTY - 1); while ( semaphore.availablePermits() > 0 ) { Thread.sleep(100); } } else { Thread.sleep(100); } } finally { mutex.release(); hasLock.set(false); } return null; } } ); threads.add(t); } for ( Future t : threads ) { t.get(); } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testReentrant2Threads() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(100, 3)); client.start(); try { waitLatchForBar = new CountDownLatch(1); countLatchForBar = new CountDownLatch(1); final InterProcessLock mutex = makeLock(client); Executors.newSingleThreadExecutor().submit ( new Callable() { @Override public Object call() throws Exception { assertTrue(countLatchForBar.await(10, TimeUnit.SECONDS)); try { mutex.acquire(10, TimeUnit.SECONDS); fail(); } catch ( Exception e ) { // correct } finally { waitLatchForBar.countDown(); } return null; } } ); foo(mutex); assertFalse(mutex.isAcquiredInThisProcess()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testReentrant() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(100, 3)); client.start(); try { InterProcessLock mutex = makeLock(client); foo(mutex); assertFalse(mutex.isAcquiredInThisProcess()); } finally { CloseableUtils.closeQuietly(client); } } private void foo(InterProcessLock mutex) throws Exception { mutex.acquire(10, TimeUnit.SECONDS); assertTrue(mutex.isAcquiredInThisProcess()); bar(mutex); assertTrue(mutex.isAcquiredInThisProcess()); mutex.release(); } private void bar(InterProcessLock mutex) throws Exception { mutex.acquire(10, TimeUnit.SECONDS); assertTrue(mutex.isAcquiredInThisProcess()); if ( countLatchForBar != null ) { countLatchForBar.countDown(); waitLatchForBar.await(10, TimeUnit.SECONDS); } snafu(mutex); assertTrue(mutex.isAcquiredInThisProcess()); mutex.release(); } private void snafu(InterProcessLock mutex) throws Exception { mutex.acquire(10, TimeUnit.SECONDS); assertTrue(mutex.isAcquiredInThisProcess()); mutex.release(); assertTrue(mutex.isAcquiredInThisProcess()); } @Test public void test2Clients() throws Exception { CuratorFramework client1 = null; CuratorFramework client2 = null; try { client1 = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(100, 3)); client2 = CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(100, 3)); client1.start(); client2.start(); final InterProcessLock mutexForClient1 = makeLock(client1); final InterProcessLock mutexForClient2 = makeLock(client2); final CountDownLatch latchForClient1 = new CountDownLatch(1); final CountDownLatch latchForClient2 = new CountDownLatch(1); final CountDownLatch acquiredLatchForClient1 = new CountDownLatch(1); final CountDownLatch acquiredLatchForClient2 = new CountDownLatch(1); final AtomicReference exceptionRef = new AtomicReference(); ExecutorService service = Executors.newCachedThreadPool(); Future future1 = service.submit ( new Callable() { @Override public Object call() throws Exception { try { mutexForClient1.acquire(10, TimeUnit.SECONDS); acquiredLatchForClient1.countDown(); latchForClient1.await(10, TimeUnit.SECONDS); mutexForClient1.release(); } catch ( Exception e ) { exceptionRef.set(e); } return null; } } ); Future future2 = service.submit ( new Callable() { @Override public Object call() throws Exception { try { mutexForClient2.acquire(10, TimeUnit.SECONDS); acquiredLatchForClient2.countDown(); latchForClient2.await(10, TimeUnit.SECONDS); mutexForClient2.release(); } catch ( Exception e ) { exceptionRef.set(e); } return null; } } ); while ( !mutexForClient1.isAcquiredInThisProcess() && !mutexForClient2.isAcquiredInThisProcess() ) { Thread.sleep(1000); assertFalse(future1.isDone() && future2.isDone()); } assertTrue(mutexForClient1.isAcquiredInThisProcess() != mutexForClient2.isAcquiredInThisProcess()); Thread.sleep(1000); assertTrue(mutexForClient1.isAcquiredInThisProcess() || mutexForClient2.isAcquiredInThisProcess()); assertTrue(mutexForClient1.isAcquiredInThisProcess() != mutexForClient2.isAcquiredInThisProcess()); Exception exception = exceptionRef.get(); if ( exception != null ) { throw exception; } if ( mutexForClient1.isAcquiredInThisProcess() ) { latchForClient1.countDown(); assertTrue(acquiredLatchForClient2.await(10, TimeUnit.SECONDS)); assertTrue(mutexForClient2.isAcquiredInThisProcess()); } else { latchForClient2.countDown(); assertTrue(acquiredLatchForClient1.await(10, TimeUnit.SECONDS)); assertTrue(mutexForClient1.isAcquiredInThisProcess()); } future1.get(); future2.get(); } finally { CloseableUtils.closeQuietly(client1); CloseableUtils.closeQuietly(client2); } } } TestInterProcessReadWriteLock.java000066400000000000000000000373521442004423600426200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.KillSession; import org.apache.zookeeper.KeeperException; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; public class TestInterProcessReadWriteLock extends BaseClassForTests { @Test public void testGetParticipantNodes() throws Exception { final int READERS = 20; final int WRITERS = 8; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); final CountDownLatch latch = new CountDownLatch(READERS + WRITERS); final CountDownLatch readLatch = new CountDownLatch(READERS); final InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, "/lock"); final CountDownLatch exitLatch = new CountDownLatch(1); ExecutorCompletionService service = new ExecutorCompletionService(Executors.newCachedThreadPool()); for ( int i = 0; i < READERS; ++i ) { service.submit ( new Callable() { @Override public Void call() throws Exception { lock.readLock().acquire(); try { latch.countDown(); readLatch.countDown(); exitLatch.await(); } finally { lock.readLock().release(); } return null; } } ); } for ( int i = 0; i < WRITERS; ++i ) { service.submit ( new Callable() { @Override public Void call() throws Exception { assertTrue(readLatch.await(10, TimeUnit.SECONDS)); latch.countDown(); // must be before as there can only be one writer lock.writeLock().acquire(); try { exitLatch.await(); } finally { lock.writeLock().release(); } return null; } } ); } assertTrue(latch.await(10, TimeUnit.SECONDS)); Collection readers = lock.readLock().getParticipantNodes(); Collection writers = lock.writeLock().getParticipantNodes(); assertEquals(readers.size(), READERS); assertEquals(writers.size(), WRITERS); exitLatch.countDown(); for ( int i = 0; i < (READERS + WRITERS); ++i ) { service.take().get(); } } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testThatUpgradingIsDisallowed() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, "/lock"); lock.readLock().acquire(); assertFalse(lock.writeLock().acquire(5, TimeUnit.SECONDS)); lock.readLock().release(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testThatDowngradingRespectsThreads() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); final InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, "/lock"); ExecutorService t1 = Executors.newSingleThreadExecutor(); ExecutorService t2 = Executors.newSingleThreadExecutor(); final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch releaseLatch = new CountDownLatch(1); Future f1 = t1.submit ( new Callable() { @Override public Object call() throws Exception { lock.writeLock().acquire(); latch.countDown(); try { releaseLatch.await(); } finally { lock.writeLock().release(); } return null; } } ); Future f2 = t2.submit ( new Callable() { @Override public Object call() throws Exception { assertTrue(latch.await(10, TimeUnit.SECONDS)); assertFalse(lock.readLock().acquire(5, TimeUnit.SECONDS)); return null; } } ); f2.get(); releaseLatch.countDown(); f1.get(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testDowngrading() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, "/lock"); lock.writeLock().acquire(); assertTrue(lock.readLock().acquire(5, TimeUnit.SECONDS)); lock.writeLock().release(); lock.readLock().release(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testContendingDowngrading() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); ExecutorService executor = Executors.newCachedThreadPool(); try { client.start(); InterProcessReadWriteLock lock1 = new InterProcessReadWriteLock(client, "/lock"); lock1.writeLock().acquire(); CountDownLatch ready = new CountDownLatch(1); Future writeAcquire = executor.submit(() -> { ready.countDown(); InterProcessReadWriteLock lock2 = new InterProcessReadWriteLock(client, "/lock"); lock2.writeLock().acquire(); fail("expect no acquire"); return null; }); ready.await(); // Let lock2 have chance to do write-acquire before downgrading. Thread.sleep(20); assertTrue(lock1.readLock().acquire(5, TimeUnit.SECONDS)); lock1.writeLock().release(); // We still hold read lock, other write-acquire should block. assertThrows(TimeoutException.class, () -> { // Let lock2 have chance to respond to write-release writeAcquire.get(20, TimeUnit.MILLISECONDS); }); } finally { TestCleanState.closeAndTestClean(client); executor.shutdown(); } } @Test public void testBasic() throws Exception { final int CONCURRENCY = 8; final int ITERATIONS = 100; final Random random = new Random(); final AtomicInteger concurrentCount = new AtomicInteger(0); final AtomicInteger maxConcurrentCount = new AtomicInteger(0); final AtomicInteger writeCount = new AtomicInteger(0); final AtomicInteger readCount = new AtomicInteger(0); List> futures = Lists.newArrayList(); ExecutorService service = Executors.newCachedThreadPool(); for ( int i = 0; i < CONCURRENCY; ++i ) { Future future = service.submit ( new Callable() { @Override public Void call() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, "/lock"); for ( int i = 0; i < ITERATIONS; ++i ) { if ( random.nextInt(100) < 10 ) { doLocking(lock.writeLock(), concurrentCount, maxConcurrentCount, random, 1); writeCount.incrementAndGet(); } else { doLocking(lock.readLock(), concurrentCount, maxConcurrentCount, random, Integer.MAX_VALUE); readCount.incrementAndGet(); } } } finally { TestCleanState.closeAndTestClean(client); } return null; } } ); futures.add(future); } for ( Future future : futures ) { future.get(); } System.out.println("Writes: " + writeCount.get() + " - Reads: " + readCount.get() + " - Max Reads: " + maxConcurrentCount.get()); assertTrue(writeCount.get() > 0); assertTrue(readCount.get() > 0); assertTrue(maxConcurrentCount.get() > 1); } @Test public void testSetNodeData() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); final byte[] nodeData = new byte[]{1, 2, 3, 4}; InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, "/lock", nodeData); // mutate passed-in node data, lock has made copy nodeData[0] = 5; lock.writeLock().acquire(); List children = client.getChildren().forPath("/lock"); assertEquals(1, children.size()); byte dataInZk[] = client.getData().forPath("/lock/" + children.get(0)); assertNotNull(dataInZk); assertArrayEquals(new byte[]{1, 2, 3, 4}, dataInZk); lock.writeLock().release(); } finally { TestCleanState.closeAndTestClean(client); } } private void doLocking(InterProcessLock lock, AtomicInteger concurrentCount, AtomicInteger maxConcurrentCount, Random random, int maxAllowed) throws Exception { try { assertTrue(lock.acquire(10, TimeUnit.SECONDS)); int localConcurrentCount; synchronized(this) { localConcurrentCount = concurrentCount.incrementAndGet(); if ( localConcurrentCount > maxConcurrentCount.get() ) { maxConcurrentCount.set(localConcurrentCount); } } assertTrue(localConcurrentCount <= maxAllowed, "" + localConcurrentCount); Thread.sleep(random.nextInt(9) + 1); } finally { synchronized(this) { concurrentCount.decrementAndGet(); lock.release(); } } } @Test public void testLockPath() throws Exception { CuratorFramework client1 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); CuratorFramework client2 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client1.start(); client2.start(); InterProcessReadWriteLock lock1 = new InterProcessReadWriteLock(client1, "/lock"); InterProcessReadWriteLock lock2 = new InterProcessReadWriteLock(client2, "/lock"); lock1.writeLock().acquire(); KillSession.kill(client1.getZookeeperClient().getZooKeeper()); lock2.readLock().acquire(); try { client1.getData().forPath(lock1.writeLock().getLockPath()); fail("expected not to find node"); } catch (KeeperException.NoNodeException ignored) { } lock2.readLock().release(); lock1.writeLock().release(); } finally { TestCleanState.closeAndTestClean(client2); TestCleanState.closeAndTestClean(client1); } } } TestInterProcessSemaphore.java000066400000000000000000000770471442004423600420510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.framework.recipes.shared.SharedCount; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.List; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; @SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"}) public class TestInterProcessSemaphore extends BaseClassForTests { @Test public void testAcquireAfterLostServer() throws Exception { // CURATOR-335 final String SEMAPHORE_PATH = "/test"; final int MAX_SEMAPHORES = 1; final int NUM_CLIENTS = 10; ExecutorService executor = Executors.newFixedThreadPool(NUM_CLIENTS); final Timing timing = new Timing(); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.forWaiting().milliseconds(), timing.connection(), new RetryOneTime(1)); // long session time on purpose try { client.start(); InterProcessSemaphoreV2.debugAcquireLatch = new CountDownLatch(1); // cause one of the semaphores to create its node and then wait InterProcessSemaphoreV2.debugFailedGetChildrenLatch = new CountDownLatch(1); // semaphore will notify when getChildren() fails final CountDownLatch isReadyLatch = new CountDownLatch(NUM_CLIENTS); final BlockingQueue acquiredQueue = Queues.newLinkedBlockingQueue(); Runnable runner = new Runnable() { @Override public void run() { while ( !Thread.currentThread().isInterrupted() ) { InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, SEMAPHORE_PATH, MAX_SEMAPHORES); Lease lease = null; try { isReadyLatch.countDown(); lease = semaphore.acquire(); acquiredQueue.add(true); timing.sleepABit(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); break; } catch ( KeeperException e ) { try { timing.sleepABit(); } catch ( InterruptedException e2 ) { Thread.currentThread().interrupt(); break; } } catch ( Exception ignore ) { // ignore } finally { if ( lease != null ) { semaphore.returnLease(lease); } } } } }; for ( int i = 0; i < NUM_CLIENTS; ++i ) { executor.execute(runner); } assertTrue(timing.awaitLatch(isReadyLatch)); timing.sleepABit(); final CountDownLatch lostLatch = new CountDownLatch(1); final CountDownLatch restartedLatch = new CountDownLatch(1); client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } else if ( newState == ConnectionState.RECONNECTED ) { restartedLatch.countDown(); } } }); server.stop(); assertTrue(timing.multiple(1.25).awaitLatch(lostLatch)); InterProcessSemaphoreV2.debugAcquireLatch.countDown(); // the waiting semaphore proceeds to getChildren - which should fail assertTrue(timing.awaitLatch(InterProcessSemaphoreV2.debugFailedGetChildrenLatch)); // wait until getChildren fails server.restart(); assertTrue(timing.awaitLatch(restartedLatch)); for ( int i = 0; i < NUM_CLIENTS; ++i ) { // acquires should continue as normal after server restart Boolean polled = acquiredQueue.poll(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); if ( (polled == null) || !polled ) { fail("Semaphores not reacquired after restart"); } } } finally { executor.shutdownNow(); CloseableUtils.closeQuietly(client); } } @Test public void testThreadedLeaseIncrease() throws Exception { final Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final SharedCount count = new SharedCount(client, "/foo/count", 1); count.start(); final InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", count); ExecutorService service = Executors.newCachedThreadPool(); final CountDownLatch latch1 = new CountDownLatch(1); final CountDownLatch latch2 = new CountDownLatch(1); Future future1 = service.submit ( new Callable() { @Override public Object call() throws Exception { Lease lease = semaphore.acquire(timing.seconds(), TimeUnit.SECONDS); assertNotNull(lease); latch1.countDown(); lease = semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNotNull(lease); latch2.countDown(); return null; } } ); Future future2 = service.submit ( new Callable() { @Override public Object call() throws Exception { assertTrue(latch1.await(timing.forWaiting().seconds(), TimeUnit.SECONDS)); timing.sleepABit(); // make sure second acquire is waiting assertTrue(count.trySetCount(2)); //Make sure second acquire takes less than full waiting time: timing.sleepABit(); assertTrue(latch2.await(0, TimeUnit.SECONDS)); return null; } } ); future1.get(); future2.get(); count.close(); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testClientClose() throws Exception { final Timing timing = new Timing(); CuratorFramework client1 = null; CuratorFramework client2 = null; InterProcessSemaphoreV2 semaphore1; InterProcessSemaphoreV2 semaphore2; try { client1 = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client2 = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client1.start(); client2.start(); semaphore1 = new InterProcessSemaphoreV2(client1, "/test", 1); semaphore2 = new InterProcessSemaphoreV2(client2, "/test", 1); Lease lease = semaphore2.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNotNull(lease); lease.close(); lease = semaphore1.acquire(10, TimeUnit.SECONDS); assertNotNull(lease); client1.close(); // should release any held leases client1 = null; assertNotNull(semaphore2.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } finally { TestCleanState.closeAndTestClean(client1); TestCleanState.closeAndTestClean(client2); } } @Test public void testMaxPerSession() throws Exception { final int CLIENT_QTY = 10; final int LOOP_QTY = 100; final Random random = new Random(); final int SESSION_MAX = random.nextInt(75) + 25; final Timing timing = new Timing(); List> futures = Lists.newArrayList(); ExecutorService service = Executors.newCachedThreadPool(); final Counter counter = new Counter(); final AtomicInteger available = new AtomicInteger(SESSION_MAX); for ( int i = 0; i < CLIENT_QTY; ++i ) { futures.add ( service.submit ( new Callable() { @Override public Object call() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", SESSION_MAX); for ( int i = 0; i < LOOP_QTY; ++i ) { long start = System.currentTimeMillis(); int thisQty; synchronized(available) { if ( (System.currentTimeMillis() - start) > 10000 ) { throw new TimeoutException(); } while ( available.get() == 0 ) { available.wait(timing.forWaiting().milliseconds()); } thisQty = (available.get() > 1) ? (random.nextInt(available.get()) + 1) : 1; available.addAndGet(-1 * thisQty); assertTrue(available.get() >= 0); } Collection leases = semaphore.acquire(thisQty, timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNotNull(leases); try { synchronized(counter) { counter.currentCount += thisQty; if ( counter.currentCount > counter.maxCount ) { counter.maxCount = counter.currentCount; } } Thread.sleep(random.nextInt(25)); } finally { synchronized(counter) { counter.currentCount -= thisQty; } semaphore.returnAll(leases); synchronized(available) { available.addAndGet(thisQty); available.notifyAll(); } } } } finally { TestCleanState.closeAndTestClean(client); } return null; } } ) ); } for ( Future f : futures ) { f.get(); } synchronized(counter) { assertTrue(counter.currentCount == 0); assertTrue(counter.maxCount > 0); assertTrue(counter.maxCount <= SESSION_MAX); System.out.println(counter.maxCount); } } @Test public void testRelease1AtATime() throws Exception { final Timing timing = new Timing(); final int CLIENT_QTY = 10; final int MAX = CLIENT_QTY / 2; final AtomicInteger maxLeases = new AtomicInteger(0); final AtomicInteger activeQty = new AtomicInteger(0); final AtomicInteger uses = new AtomicInteger(0); List> futures = Lists.newArrayList(); ExecutorService service = Executors.newFixedThreadPool(CLIENT_QTY); for ( int i = 0; i < CLIENT_QTY; ++i ) { Future f = service.submit ( new Callable() { @Override public Object call() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", MAX); Lease lease = semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNotNull(lease); uses.incrementAndGet(); try { synchronized(maxLeases) { int qty = activeQty.incrementAndGet(); if ( qty > maxLeases.get() ) { maxLeases.set(qty); } } timing.sleepABit(); } finally { activeQty.decrementAndGet(); lease.close(); } } finally { TestCleanState.closeAndTestClean(client); } return null; } } ); futures.add(f); } for ( Future f : futures ) { f.get(); } assertEquals(uses.get(), CLIENT_QTY); assertEquals(maxLeases.get(), MAX); } @Test public void testReleaseInChunks() throws Exception { final Timing timing = new Timing(); final int MAX_LEASES = 11; final int THREADS = 100; final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final Stepper latch = new Stepper(); final Random random = new Random(); final Counter counter = new Counter(); ExecutorService service = Executors.newCachedThreadPool(); ExecutorCompletionService completionService = new ExecutorCompletionService(service); for ( int i = 0; i < THREADS; ++i ) { completionService.submit ( new Callable() { @Override public Object call() throws Exception { InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", MAX_LEASES); Lease lease = semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS); if ( lease == null ) { throw new Exception("timed out"); } try { synchronized(counter) { ++counter.currentCount; if ( counter.currentCount > counter.maxCount ) { counter.maxCount = counter.currentCount; } counter.notifyAll(); } latch.await(); } finally { synchronized(counter) { --counter.currentCount; } semaphore.returnLease(lease); } return null; } } ); } int remaining = THREADS; while ( remaining > 0 ) { int times = Math.min(random.nextInt(5) + 1, remaining); latch.countDown(times); remaining -= times; Thread.sleep(random.nextInt(100) + 1); } for ( int i = 0; i < THREADS; ++i ) { completionService.take(); } timing.sleepABit(); synchronized(counter) { assertTrue(counter.currentCount == 0); assertTrue(counter.maxCount > 0); assertTrue(counter.maxCount <= MAX_LEASES); System.out.println(counter.maxCount); } } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testThreads() throws Exception { final int THREAD_QTY = 10; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", 1); ExecutorService service = Executors.newFixedThreadPool(THREAD_QTY); for ( int i = 0; i < THREAD_QTY; ++i ) { service.submit ( new Callable() { @Override public Object call() throws Exception { Lease lease = semaphore.acquire(); try { Thread.sleep(1); } finally { lease.close(); } return null; } } ); } service.shutdown(); assertTrue(service.awaitTermination(10, TimeUnit.SECONDS)); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testSimple() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", 1); assertNotNull(semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS)); assertNull(semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testSimple2() throws Exception { final int MAX_LEASES = 3; Timing timing = new Timing(); List leases = Lists.newArrayList(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { for ( int i = 0; i < MAX_LEASES; ++i ) { InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", MAX_LEASES); Lease lease = semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNotNull(lease); leases.add(lease); } InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", MAX_LEASES); Lease lease = semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNull(lease); leases.remove(0).close(); assertNotNull(semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } finally { for ( Lease l : leases ) { CloseableUtils.closeQuietly(l); } TestCleanState.closeAndTestClean(client); } } @Test public void testGetParticipantNodes() throws Exception { final int LEASES = 3; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); List leases = Lists.newArrayList(); client.start(); try { InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", LEASES); for ( int i = 0; i < LEASES; ++i ) { leases.add(semaphore.acquire()); } assertEquals(semaphore.getParticipantNodes().size(), LEASES); } finally { for ( Lease l : leases ) { CloseableUtils.closeQuietly(l); } TestCleanState.closeAndTestClean(client); } } @Test public void testNoOrphanedNodes() throws Exception { final Timing timing = new Timing(); final ExecutorService executor = Executors.newFixedThreadPool(1); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/test", 1); Lease lease = semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNotNull(lease); final List childNodes = client.getChildren().forPath("/test/leases"); assertEquals(childNodes.size(), 1); final CountDownLatch nodeCreatedLatch = new CountDownLatch(1); client.getChildren().usingWatcher(new CuratorWatcher() { @Override public void process(WatchedEvent event) throws Exception { if ( event.getType() == Watcher.Event.EventType.NodeCreated ) { nodeCreatedLatch.countDown(); } } }).forPath("/test/leases"); final Future leaseFuture = executor.submit(new Callable() { @Override public Lease call() throws Exception { return semaphore.acquire(timing.forWaiting().multiple(2).seconds(), TimeUnit.SECONDS); } }); // wait for second lease to create its node timing.awaitLatch(nodeCreatedLatch); String newNode = null; for ( String c : client.getChildren().forPath("/test/leases") ) { if ( !childNodes.contains(c) ) { newNode = c; } } assertNotNull(newNode); // delete the ephemeral node to trigger a retry client.delete().forPath("/test/leases/" + newNode); // release first lease so second one can be acquired lease.close(); lease = leaseFuture.get(); assertNotNull(lease); lease.close(); assertEquals(client.getChildren().forPath("/test/leases").size(), 0); // no more lease exist. must be possible to acquire a new one assertNotNull(semaphore.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } finally { executor.shutdownNow(); TestCleanState.closeAndTestClean(client); } } @Test public void testInterruptAcquire() throws Exception { // CURATOR-462 final Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final InterProcessSemaphoreV2 s1 = new InterProcessSemaphoreV2(client, "/test", 1); final InterProcessSemaphoreV2 s2 = new InterProcessSemaphoreV2(client, "/test", 1); final InterProcessSemaphoreV2 s3 = new InterProcessSemaphoreV2(client, "/test", 1); final CountDownLatch debugWaitLatch = s2.debugWaitLatch = new CountDownLatch(1); // Acquire exclusive semaphore Lease lease = s1.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNotNull(lease); // Queue up another semaphore on the same path Future handle = Executors.newSingleThreadExecutor().submit(new Callable() { @Override public Object call() throws Exception { s2.acquire(); return null; } }); // Wait until second lease is created and the wait is started for it to become active assertTrue(timing.awaitLatch(debugWaitLatch)); // Interrupt the wait handle.cancel(true); // Assert that the second lease is gone timing.sleepABit(); assertEquals(client.getChildren().forPath("/test/leases").size(), 1); // Assert that after closing the first (current) semaphore, we can acquire a new one s1.returnLease(lease); assertNotNull(s3.acquire(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } finally { TestCleanState.closeAndTestClean(client); } } } TestInterProcessSemaphoreCluster.java000066400000000000000000000306211442004423600433760ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.apache.curator.ensemble.EnsembleProvider; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.InstanceSpec; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestInterProcessSemaphoreCluster extends BaseClassForTests { @Test public void testKilledServerWithEnsembleProvider() throws Exception { final int CLIENT_QTY = 10; final Timing timing = new Timing(); final String PATH = "/foo/bar/lock"; ExecutorService executorService = Executors.newFixedThreadPool(CLIENT_QTY); ExecutorCompletionService completionService = new ExecutorCompletionService(executorService); TestingCluster cluster = createAndStartCluster(3); try { final AtomicReference connectionString = new AtomicReference(cluster.getConnectString()); final EnsembleProvider provider = new EnsembleProvider() { @Override public void setConnectionString(String connectionString) { } @Override public boolean updateServerListEnabled() { return false; } @Override public void start() throws Exception { } @Override public String getConnectionString() { return connectionString.get(); } @Override public void close() throws IOException { } }; final Semaphore acquiredSemaphore = new Semaphore(0); final AtomicInteger acquireCount = new AtomicInteger(0); final CountDownLatch suspendedLatch = new CountDownLatch(CLIENT_QTY); for ( int i = 0; i < CLIENT_QTY; ++i ) { completionService.submit ( new Callable() { @Override public Void call() throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder() .ensembleProvider(provider) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()) .retryPolicy(new ExponentialBackoffRetry(100, 3)) .build(); try { final Semaphore suspendedSemaphore = new Semaphore(0); client.getConnectionStateListenable().addListener ( new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( (newState == ConnectionState.SUSPENDED) || (newState == ConnectionState.LOST) ) { suspendedLatch.countDown(); suspendedSemaphore.release(); } } } ); client.start(); InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, PATH, 1); while ( !Thread.currentThread().isInterrupted() ) { Lease lease = null; try { lease = semaphore.acquire(); acquiredSemaphore.release(); acquireCount.incrementAndGet(); suspendedSemaphore.acquire(); } catch ( Exception e ) { // just retry } finally { if ( lease != null ) { acquireCount.decrementAndGet(); CloseableUtils.closeQuietly(lease); } } } } finally { TestCleanState.closeAndTestClean(client); } return null; } } ); } assertTrue(timing.acquireSemaphore(acquiredSemaphore)); assertEquals(1, acquireCount.get()); cluster.close(); timing.awaitLatch(suspendedLatch); timing.forWaiting().sleepABit(); assertEquals(0, acquireCount.get()); cluster = createAndStartCluster(3); connectionString.set(cluster.getConnectString()); timing.forWaiting().sleepABit(); assertTrue(timing.acquireSemaphore(acquiredSemaphore)); timing.forWaiting().sleepABit(); assertEquals(1, acquireCount.get()); } finally { executorService.shutdown(); executorService.awaitTermination(10, TimeUnit.SECONDS); executorService.shutdownNow(); CloseableUtils.closeQuietly(cluster); } } @Test public void testCluster() throws Exception { final int QTY = 20; final int OPERATION_TIME_MS = 1000; final String PATH = "/foo/bar/lock"; ExecutorService executorService = Executors.newFixedThreadPool(QTY); ExecutorCompletionService completionService = new ExecutorCompletionService(executorService); final Timing timing = new Timing(); List semaphoreClients = Lists.newArrayList(); TestingCluster cluster = createAndStartCluster(3); try { final AtomicInteger opCount = new AtomicInteger(0); for ( int i = 0; i < QTY; ++i ) { SemaphoreClient semaphoreClient = new SemaphoreClient ( cluster.getConnectString(), PATH, new Callable() { @Override public Void call() throws Exception { opCount.incrementAndGet(); Thread.sleep(OPERATION_TIME_MS); return null; } } ); completionService.submit(semaphoreClient); semaphoreClients.add(semaphoreClient); } timing.forWaiting().sleepABit(); assertNotNull(SemaphoreClient.getActiveClient()); final CountDownLatch latch = new CountDownLatch(1); CuratorFramework client = CuratorFrameworkFactory.newClient(cluster.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.LOST ) { latch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); client.start(); try { client.getZookeeperClient().blockUntilConnectedOrTimedOut(); cluster.stop(); latch.await(); } finally { CloseableUtils.closeQuietly(client); } long startTicks = System.currentTimeMillis(); for(;;) { int thisOpCount = opCount.get(); Thread.sleep(2 * OPERATION_TIME_MS); if ( thisOpCount == opCount.get() ) { break; // checking that the op count isn't increasing } assertTrue((System.currentTimeMillis() - startTicks) < timing.forWaiting().milliseconds()); } int thisOpCount = opCount.get(); Iterator iterator = cluster.getInstances().iterator(); cluster = new TestingCluster(iterator.next(), iterator.next()); cluster.start(); timing.forWaiting().sleepABit(); startTicks = System.currentTimeMillis(); for(;;) { Thread.sleep(2 * OPERATION_TIME_MS); if ( opCount.get() > thisOpCount ) { break; // checking that semaphore has started working again } assertTrue((System.currentTimeMillis() - startTicks) < timing.forWaiting().milliseconds()); } } finally { for ( SemaphoreClient semaphoreClient : semaphoreClients ) { CloseableUtils.closeQuietly(semaphoreClient); } CloseableUtils.closeQuietly(cluster); executorService.shutdownNow(); } } } TestInterProcessSemaphoreMutex.java000066400000000000000000000030441442004423600430560ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import org.apache.curator.framework.CuratorFramework; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; public class TestInterProcessSemaphoreMutex extends TestInterProcessMutexBase { private static final String LOCK_PATH = LOCK_BASE_PATH + "/our-lock"; @Override @Test @Disabled public void testReentrant() { } @Override @Test @Disabled public void testReentrant2Threads() { } @Override @Test @Disabled public void testReentrantSingleLock() { } @Override protected InterProcessLock makeLock(CuratorFramework client) { return new InterProcessSemaphoreMutex(client, LOCK_PATH); } } TestLockACLs.java000066400000000000000000000114321442004423600371420ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.List; public class TestLockACLs extends BaseClassForTests { private static final List ACLS1 = Collections.singletonList(new ACL(ZooDefs.Perms.ALL, new Id("ip", "127.0.0.1"))); private static final List ACLS2 = Collections.singletonList(new ACL(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ, new Id("ip", "127.0.0.1"))); private CuratorFramework createClient(ACLProvider provider) throws Exception { RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); CuratorFramework client = CuratorFrameworkFactory.builder() .namespace("ns") .connectString(server.getConnectString()) .retryPolicy(retryPolicy) .aclProvider(provider) .build(); client.start(); return client; } @Test public void testLockACLs() throws Exception { CuratorFramework client = createClient(new TestLockACLsProvider()); try { client.create().forPath("/foo"); assertNotNull(client.checkExists().forPath("/foo")); assertEquals(ZooDefs.Perms.ALL, client.getACL().forPath("/foo").get(0).getPerms()); assertEquals("ip", client.getACL().forPath("/foo").get(0).getId().getScheme()); assertEquals("127.0.0.1", client.getACL().forPath("/foo").get(0).getId().getId()); InterProcessReadWriteLock lock = new InterProcessReadWriteLock(client, "/bar"); InterProcessMutex writeLock = lock.writeLock(); writeLock.acquire(); assertNotNull(client.checkExists().forPath("/bar")); assertEquals(ZooDefs.Perms.ALL, client.getACL().forPath("/bar").get(0).getPerms()); assertEquals("ip", client.getACL().forPath("/bar").get(0).getId().getScheme()); assertEquals("127.0.0.1", client.getACL().forPath("/bar").get(0).getId().getId()); } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testACLsCreatingParents() throws Exception { CuratorFramework client = createClient(new TestACLsCreatingParentsProvider()); try { client.create().creatingParentsIfNeeded().forPath("/parent/foo"); assertEquals(ZooDefs.Perms.CREATE | ZooDefs.Perms.READ, client.getACL().forPath("/parent").get(0).getPerms()); assertEquals(ZooDefs.Perms.ALL, client.getACL().forPath("/parent/foo").get(0).getPerms()); } finally { CloseableUtils.closeQuietly(client); } } private class TestACLsCreatingParentsProvider implements ACLProvider { @Override public List getDefaultAcl() { return ACLS1; } @Override public List getAclForPath(String path) { if ( path.equals("/ns/parent") ) { return ACLS2; } return ACLS1; } } private class TestLockACLsProvider implements ACLProvider { @Override public List getDefaultAcl() { return ACLS1; } @Override public List getAclForPath(String path) { return ACLS1; } } } TestLockCleanlinessWithFaults.java000066400000000000000000000052361442004423600426400ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/locks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.locks; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.test.BaseClassForTests; import org.apache.zookeeper.KeeperException; import org.junit.jupiter.api.Test; import java.util.List; public class TestLockCleanlinessWithFaults extends BaseClassForTests { @Test public void testNodeDeleted() throws Exception { final String PATH = "/foo/bar"; CuratorFramework client = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryNTimes(0, 0)); client.start(); client.create().creatingParentsIfNeeded().forPath(PATH); assertEquals(client.checkExists().forPath(PATH).getNumChildren(), 0); LockInternals internals = new LockInternals(client, new StandardLockInternalsDriver(), PATH, "lock-", 1) { @Override List getSortedChildren() throws Exception { throw new KeeperException.NoNodeException(); } }; try { internals.attemptLock(0, null, null); fail(); } catch ( KeeperException.NoNodeException dummy ) { // expected } // make sure no nodes are left lying around assertEquals(client.checkExists().forPath(PATH).getNumChildren(), 0); } finally { TestCleanState.closeAndTestClean(client); } } } nodes/000077500000000000000000000000001442004423600340405ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipesTestGroupMember.java000066400000000000000000000104411442004423600377670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.base.Function; import com.google.common.collect.Maps; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.Map; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestGroupMember extends BaseClassForTests { // NOTE - don't need many tests as this class is just a wrapper around two existing recipes @Test public void testBasic() throws Exception { Timing timing = new Timing(); GroupMember groupMember1 = null; GroupMember groupMember2 = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); groupMember1 = new GroupMember(client, "/member", "1"); assertTrue(groupMember1.getCurrentMembers().containsKey("1")); groupMember1.start(); groupMember2 = new GroupMember(client, "/member", "2"); groupMember2.start(); timing.sleepABit(); Map currentMembers1 = groupMember1.getCurrentMembers(); Map currentMembers2 = groupMember2.getCurrentMembers(); Map convertMembers1 = Maps.transformValues(currentMembers1, new Function() { @Override public String apply(byte[] input) { return new String(input); } }); Map convertMembers2 = Maps.transformValues(currentMembers2, new Function() { @Override public String apply(byte[] input) { return new String(input); } }); assertEquals(convertMembers1.size(), 2); assertEquals(convertMembers2.size(), 2); assertEquals(convertMembers1, convertMembers2); assertTrue(convertMembers1.containsKey("1")); assertTrue(convertMembers1.containsKey("2")); groupMember2.close(); timing.sleepABit(); currentMembers1 = groupMember1.getCurrentMembers(); assertEquals(currentMembers1.size(), 1); assertTrue(currentMembers1.containsKey("1")); assertFalse(currentMembers1.containsKey("2")); groupMember1.setThisData("something".getBytes()); timing.sleepABit(); currentMembers1 = groupMember1.getCurrentMembers(); assertTrue(currentMembers1.containsKey("1")); assertArrayEquals(currentMembers1.get("1"), "something".getBytes()); } finally { CloseableUtils.closeQuietly(groupMember1); CloseableUtils.closeQuietly(groupMember2); CloseableUtils.closeQuietly(client); } } } TestPersistentEphemeralNode.java000066400000000000000000001004241442004423600423350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.imps.TestCleanState; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ZKPaths; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @SuppressWarnings("deprecation") public class TestPersistentEphemeralNode extends BaseClassForTests { private static final Logger log = LoggerFactory.getLogger(TestPersistentEphemeralNode.class); private static final String DIR = "/test"; private static final String PATH = ZKPaths.makePath(DIR, "/foo"); private final Collection curatorInstances = Lists.newArrayList(); private final Collection createdNodes = Lists.newArrayList(); private final Timing2 timing = new Timing2(); @AfterEach @Override public void teardown() throws Exception { try { for ( PersistentEphemeralNode node : createdNodes ) { CloseableUtils.closeQuietly(node); } for ( CuratorFramework curator : curatorInstances ) { TestCleanState.closeAndTestClean(curator); } } finally { super.teardown(); } } @Test public void testListenersReconnectedIsFast() throws Exception { server.stop(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); try ( PersistentEphemeralNode node = new PersistentEphemeralNode(client, PersistentEphemeralNode.Mode.EPHEMERAL, "/abc/node", "hello".getBytes()) ) { node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); final CountDownLatch connectedLatch = new CountDownLatch(1); final CountDownLatch reconnectedLatch = new CountDownLatch(1); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.CONNECTED ) { connectedLatch.countDown(); } if ( newState == ConnectionState.RECONNECTED ) { reconnectedLatch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); timing.sleepABit(); server.restart(); assertTrue(timing.awaitLatch(connectedLatch)); timing.sleepABit(); assertTrue(node.waitForInitialCreate(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); server.stop(); timing.sleepABit(); server.restart(); timing.sleepABit(); assertTrue(timing.awaitLatch(reconnectedLatch)); } } finally { TestCleanState.closeAndTestClean(client); } } @Test public void testNoServerAtStart() throws Exception { server.stop(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); PersistentEphemeralNode node = null; try { client.start(); node = new PersistentEphemeralNode(client, PersistentEphemeralNode.Mode.EPHEMERAL, "/abc/node", "hello".getBytes()); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); final CountDownLatch connectedLatch = new CountDownLatch(1); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( newState == ConnectionState.CONNECTED ) { connectedLatch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); timing.sleepABit(); server.restart(); assertTrue(timing.awaitLatch(connectedLatch)); timing.sleepABit(); assertTrue(node.waitForInitialCreate(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); } finally { CloseableUtils.closeQuietly(node); TestCleanState.closeAndTestClean(client); } } @Test public void testNullCurator() { assertThrows(NullPointerException.class, ()-> { new PersistentEphemeralNode(null, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); }); } @Test public void testNullPath() { assertThrows(IllegalArgumentException.class, ()-> { CuratorFramework curator = newCurator(); new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, null, new byte[0]); }); } @Test public void testNullData() { assertThrows(NullPointerException.class, ()-> { CuratorFramework curator = newCurator(); new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, null); }); } @Test public void testNullMode() { assertThrows(NullPointerException.class, ()->{ CuratorFramework curator = newCurator(); new PersistentEphemeralNode(curator, null, PATH, new byte[0]); }); } @Test public void testSettingDataSequential() throws Exception { setDataTest(PersistentEphemeralNode.Mode.EPHEMERAL_SEQUENTIAL); } @Test public void testSettingData() throws Exception { setDataTest(PersistentEphemeralNode.Mode.EPHEMERAL); } protected void setDataTest(PersistentEphemeralNode.Mode mode) throws Exception { PersistentEphemeralNode node = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); node = new PersistentEphemeralNode(client, mode, PATH, "a".getBytes()); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); assertTrue(node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS)); assertArrayEquals(client.getData().forPath(node.getActualPath()), "a".getBytes()); final Semaphore semaphore = new Semaphore(0); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent arg0) { semaphore.release(); } }; client.checkExists().usingWatcher(watcher).forPath(node.getActualPath()); node.setData("b".getBytes()); assertTrue(timing.acquireSemaphore(semaphore)); assertEquals(node.getActualPath(), node.getActualPath()); assertArrayEquals(client.getData().usingWatcher(watcher).forPath(node.getActualPath()), "b".getBytes()); node.setData("c".getBytes()); assertTrue(timing.acquireSemaphore(semaphore)); assertEquals(node.getActualPath(), node.getActualPath()); assertArrayEquals(client.getData().usingWatcher(watcher).forPath(node.getActualPath()), "c".getBytes()); node.close(); assertTrue(timing.acquireSemaphore(semaphore)); } finally { CloseableUtils.closeQuietly(node); TestCleanState.closeAndTestClean(client); } } @Test public void testDeletesNodeWhenClosed() throws Exception { CuratorFramework curator = newCurator(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); String path = null; try { node.waitForInitialCreate(5, TimeUnit.SECONDS); path = node.getActualPath(); assertNodeExists(curator, path); } finally { CloseableUtils.closeQuietly(node); } assertNodeDoesNotExist(curator, path); } @Test public void testClosingMultipleTimes() throws Exception { CuratorFramework curator = newCurator(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); String path = node.getActualPath(); node.close(); assertNodeDoesNotExist(curator, path); node.close(); assertNodeDoesNotExist(curator, path); } @Test public void testDeletesNodeWhenSessionDisconnects() throws Exception { CuratorFramework curator = newCurator(); CuratorFramework observer = newCurator(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); try { node.start(); node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNodeExists(observer, node.getActualPath()); // Register a watch that will fire when the node is deleted... Trigger deletedTrigger = Trigger.deletedOrSetData(); observer.checkExists().usingWatcher(deletedTrigger).forPath(node.getActualPath()); node.debugCreateNodeLatch = new CountDownLatch(1); curator.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); // Make sure the node got deleted assertTrue(deletedTrigger.firedWithin(timing.forSessionSleep().seconds(), TimeUnit.SECONDS)); node.debugCreateNodeLatch.countDown(); } finally { CloseableUtils.closeQuietly(node); } } @Test public void testRecreatesNodeWhenSessionReconnects() throws Exception { CuratorFramework curator = newCurator(); CuratorFramework observer = newCurator(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); try { node.start(); node.waitForInitialCreate(5, TimeUnit.SECONDS); assertNodeExists(observer, node.getActualPath()); Trigger deletedTrigger = Trigger.deletedOrSetData(); observer.checkExists().usingWatcher(deletedTrigger).forPath(node.getActualPath()); node.debugCreateNodeLatch = new CountDownLatch(1); curator.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); // Make sure the node got deleted... assertTrue(deletedTrigger.firedWithin(timing.forSessionSleep().seconds(), TimeUnit.SECONDS)); node.debugCreateNodeLatch.countDown(); // Check for it to be recreated... Trigger createdTrigger = Trigger.created(); Stat stat = observer.checkExists().usingWatcher(createdTrigger).forPath(node.getActualPath()); assertTrue(stat != null || createdTrigger.firedWithin(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(node); } } @Test public void testRecreatesNodeWhenSessionReconnectsMultipleTimes() throws Exception { CuratorFramework curator = newCurator(); CuratorFramework observer = newCurator(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); try { node.start(); node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); String path = node.getActualPath(); assertNodeExists(observer, path); // We should be able to disconnect multiple times and each time the node should be recreated. for ( int i = 0; i < 5; i++ ) { Trigger deletionTrigger = Trigger.deletedOrSetData(); Stat stat = observer.checkExists().usingWatcher(deletionTrigger).forPath(path); assertNotNull(stat, "node should exist: " + path); node.debugCreateNodeLatch = new CountDownLatch(1); // Kill the session, thus cleaning up the node... curator.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); // Make sure the node ended up getting deleted... assertTrue(deletionTrigger.firedWithin(timing.multiple(1.5).forSessionSleep().seconds(), TimeUnit.SECONDS)); node.debugCreateNodeLatch.countDown(); // Now put a watch in the background looking to see if it gets created... Trigger creationTrigger = Trigger.created(); stat = observer.checkExists().usingWatcher(creationTrigger).forPath(path); assertTrue(stat != null || creationTrigger.firedWithin(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } } finally { CloseableUtils.closeQuietly(node); } } @Test public void testRecreatesNodeWhenEphemeralOwnerSessionExpires() throws Exception { CuratorFramework curator = newCurator(); CuratorFramework nodeCreator = newCurator(); CuratorFramework observer = newCurator(); nodeCreator.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(PATH, new byte[0]); Trigger dataChangedTrigger = Trigger.dataChanged(); observer.getData().usingWatcher(dataChangedTrigger).forPath(PATH); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); try { node.waitForInitialCreate(5, TimeUnit.SECONDS); assertNodeExists(observer, node.getActualPath()); assertTrue(dataChangedTrigger.firedWithin(timing.forWaiting().seconds(), TimeUnit.SECONDS)); Trigger deletedTrigger = Trigger.deletedOrSetData(); observer.checkExists().usingWatcher(deletedTrigger).forPath(node.getActualPath()); nodeCreator.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); // Make sure the node got deleted... assertTrue(deletedTrigger.firedWithin(timing.forWaiting().seconds(), TimeUnit.SECONDS)); // Check for it to be recreated... Trigger createdTrigger = Trigger.created(); Stat stat = observer.checkExists().usingWatcher(createdTrigger).forPath(node.getActualPath()); assertTrue(stat != null || createdTrigger.firedWithin(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } finally { node.close(); } } @Test public void testRecreatesNodeWhenItGetsDeleted() throws Exception { CuratorFramework curator = newCurator(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); try { node.start(); node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); String originalNode = node.getActualPath(); assertNodeExists(curator, originalNode); // Delete the original node... curator.delete().forPath(originalNode); // Since we're using an ephemeral node, and the original session hasn't been interrupted the name of the new // node that gets created is going to be exactly the same as the original. Trigger createdWatchTrigger = Trigger.created(); Stat stat = curator.checkExists().usingWatcher(createdWatchTrigger).forPath(originalNode); assertTrue(stat != null || createdWatchTrigger.firedWithin(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(node); } } @Test public void testNodesCreateUniquePaths() throws Exception { CuratorFramework curator = newCurator(); try ( PersistentEphemeralNode node1 = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL_SEQUENTIAL, PATH, new byte[0]) ) { node1.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node1.start(); node1.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); String path1 = node1.getActualPath(); PersistentEphemeralNode node2 = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL_SEQUENTIAL, PATH, new byte[0]); node2.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node2.start(); try { node2.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); String path2 = node2.getActualPath(); assertFalse(path1.equals(path2)); } finally { node2.close(); } } } @Test public void testData() throws Exception { CuratorFramework curator = newCurator(); byte[] data = "Hello World".getBytes(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, data); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); try { node.start(); node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), data)); } finally { CloseableUtils.closeQuietly(node); } } /** * Test that if a persistent ephemeral node is created and the node already exists * that if data is present in the PersistentEphermalNode that it is still set. * @throws Exception */ @Test public void testSetDataWhenNodeExists() throws Exception { CuratorFramework curator = newCurator(); curator.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(PATH, "InitialData".getBytes()); byte[] data = "Hello World".getBytes(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, data); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); try { node.start(); node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), data)); } finally { CloseableUtils.closeQuietly(node); } } @Test public void testSetDataWhenDisconnected() throws Exception { CuratorFramework curator = newCurator(); byte[] initialData = "Hello World".getBytes(); byte[] updatedData = "Updated".getBytes(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, initialData); try { node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), initialData)); server.stop(); final CountDownLatch dataUpdateLatch = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if ( event.getType() == EventType.NodeDataChanged ) { dataUpdateLatch.countDown(); } } }; curator.getData().usingWatcher(watcher).inBackground().forPath(node.getActualPath()); node.setData(updatedData); server.restart(); assertTrue(timing.awaitLatch(dataUpdateLatch)); assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), updatedData)); } finally { CloseableUtils.closeQuietly(node); } } @Test public void testSetUpdatedDataWhenReconnected() throws Exception { CuratorFramework curator = newCurator(); byte[] initialData = "Hello World".getBytes(); byte[] updatedData = "Updated".getBytes(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, initialData); try { node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), initialData)); node.setData(updatedData); assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), updatedData)); server.restart(); final CountDownLatch dataUpdateLatch = new CountDownLatch(1); curator.getData().inBackground(new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { dataUpdateLatch.countDown(); } }).forPath(node.getActualPath()); assertTrue(timing.awaitLatch(dataUpdateLatch)); assertTrue(Arrays.equals(curator.getData().forPath(node.getActualPath()), updatedData)); } finally { CloseableUtils.closeQuietly(node); } } /** * See CURATOR-190 * For protected nodes on reconnect the current protected name was passed to the create builder meaning that it got * appended to the new protected node name. This meant that a new node got created on each reconnect. * @throws Exception */ @Test public void testProtected() throws Exception { CuratorFramework curator = newCurator(); PersistentEphemeralNode node = new PersistentEphemeralNode(curator, PersistentEphemeralNode.Mode.PROTECTED_EPHEMERAL, PATH, new byte[0]); try { node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); node.waitForInitialCreate(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNodeExists(curator, node.getActualPath()); server.restart(); curator.blockUntilConnected(5, TimeUnit.SECONDS); assertNodeExists(curator, node.getActualPath()); //There should only be a single child, the persisted ephemeral node List children = curator.getChildren().forPath(DIR); assertFalse(children == null); assertEquals(children.size(), 1); } finally { CloseableUtils.closeQuietly(node); } } @Test public void testNoCreatePermission() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder .connectString(server.getConnectString()) .authorization("digest", "me1:pass1".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); PersistentEphemeralNode node = null; try { client.start(); ACL acl = new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.AUTH_IDS); List aclList = Lists.newArrayList(acl); client.create().withACL(aclList).forPath(DIR, new byte[0]); client.close(); //New client without authentication client = newCurator(); node = new PersistentEphemeralNode(client, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); node.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); node.start(); node.waitForInitialCreate(timing.seconds(), TimeUnit.SECONDS); assertNodeDoesNotExist(client, PATH); assertTrue(node.isAuthFailure()); } finally { CloseableUtils.closeQuietly(node); CloseableUtils.closeQuietly(client); } } @Test public void testNoWritePermission() throws Exception { final ACLProvider aclProvider = new ACLProvider() { final ACL acl = new ACL(ZooDefs.Perms.READ | ZooDefs.Perms.CREATE | ZooDefs.Perms.DELETE, ZooDefs.Ids.ANYONE_ID_UNSAFE); final List aclList = Collections.singletonList(acl); @Override public List getDefaultAcl() { return aclList; } @Override public List getAclForPath(String path) { return aclList; } }; CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder .connectString(server.getConnectString()) .aclProvider(aclProvider) .retryPolicy(new RetryOneTime(1)) .build(); PersistentEphemeralNode node = null; try { client.start(); node = new PersistentEphemeralNode(client, PersistentEphemeralNode.Mode.EPHEMERAL, PATH, new byte[0]); node.start(); assertTrue(node.waitForInitialCreate(timing.seconds(), TimeUnit.SECONDS), "Node not created"); assertNodeExists(client, PATH); assertFalse(node.isAuthFailure(), "AuthFailure when creating node."); byte[] NEW_DATA = "NEW_DATA".getBytes(); node.setData(NEW_DATA); timing.sleepABit(); byte[] read_data = client.getData().forPath(PATH); assertNotEquals(read_data, NEW_DATA, "Data matches - write went through."); assertTrue(node.isAuthFailure(), "AuthFailure response not received."); } finally { CloseableUtils.closeQuietly(node); CloseableUtils.closeQuietly(client); } } private void assertNodeExists(CuratorFramework curator, String path) throws Exception { assertNotNull(path); assertTrue(curator.checkExists().forPath(path) != null); } private void assertNodeDoesNotExist(CuratorFramework curator, String path) throws Exception { assertTrue(curator.checkExists().forPath(path) == null); } private CuratorFramework newCurator() throws IOException { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); curatorInstances.add(client); return client; } private static final class Trigger implements Watcher { private final Set types; private final CountDownLatch latch; public Trigger(Event.EventType... types) { assertNotNull(types); this.types = ImmutableSet.copyOf(types); this.latch = new CountDownLatch(1); } @Override public void process(WatchedEvent event) { if ( types.contains(event.getType()) ) { latch.countDown(); } else if ( event.getType() != EventType.None ) { log.warn("Unexpected watcher event: " + event); } } public boolean firedWithin(long duration, TimeUnit unit) { try { return latch.await(duration, unit); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); throw Throwables.propagate(e); } } private static Trigger created() { return new Trigger(Event.EventType.NodeCreated); } private static Trigger deletedOrSetData() { return new Trigger(Event.EventType.NodeDeleted, EventType.NodeDataChanged); } private static Trigger dataChanged() { return new Trigger(EventType.NodeDataChanged); } } } TestPersistentEphemeralNodeListener.java000066400000000000000000000072631442004423600440520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @SuppressWarnings("deprecation") public class TestPersistentEphemeralNodeListener extends BaseClassForTests { @Test public void testListenersReconnectedIsOK() throws Exception { server.stop(); Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); PersistentEphemeralNode node = new PersistentEphemeralNode(client, PersistentEphemeralNode.Mode.EPHEMERAL, "/abc/node", "hello".getBytes()); node.start(); final CountDownLatch connectedLatch = new CountDownLatch(1); final CountDownLatch reconnectedLatch = new CountDownLatch(1); final AtomicReference lastState = new AtomicReference(); ConnectionStateListener listener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { lastState.set(newState); if ( newState == ConnectionState.CONNECTED ) { connectedLatch.countDown(); } if ( newState == ConnectionState.RECONNECTED ) { reconnectedLatch.countDown(); } } }; client.getConnectionStateListenable().addListener(listener); timing.sleepABit(); server.restart(); assertTrue(timing.awaitLatch(connectedLatch)); timing.sleepABit(); assertTrue(node.waitForInitialCreate(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS)); server.restart(); timing.sleepABit(); assertTrue(timing.awaitLatch(reconnectedLatch)); timing.sleepABit(); assertEquals(lastState.get(), ConnectionState.RECONNECTED); } finally { CloseableUtils.closeQuietly(client); } } }TestPersistentNode.java000066400000000000000000000223471442004423600405210ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.CreateMode; import org.junit.jupiter.api.Test; import java.util.List; import java.util.concurrent.TimeUnit; public class TestPersistentNode extends BaseClassForTests { @Test public void testQuickSetData() throws Exception { final byte[] TEST_DATA = "hey".getBytes(); final byte[] ALT_TEST_DATA = "there".getBytes(); Timing timing = new Timing(); PersistentNode pen = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); pen = new PersistentNode(client, CreateMode.PERSISTENT, false, "/test", TEST_DATA); pen.start(); try { pen.setData(ALT_TEST_DATA); fail("IllegalStateException should have been thrown"); } catch ( IllegalStateException dummy ) { // expected } } finally { CloseableUtils.closeQuietly(pen); CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { final byte[] TEST_DATA = "hey".getBytes(); Timing2 timing = new Timing2(); PersistentNode pen = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); pen = new PersistentNode(client, CreateMode.PERSISTENT, false, "/test", TEST_DATA); pen.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); pen.start(); assertTrue(pen.waitForInitialCreate(timing.milliseconds(), TimeUnit.MILLISECONDS)); client.close(); // cause session to end - force checks that node is persistent client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); byte[] bytes = client.getData().forPath("/test"); assertArrayEquals(bytes, TEST_DATA); } finally { CloseableUtils.closeQuietly(pen); CloseableUtils.closeQuietly(client); } } @Test public void testCreationWithParentCreationOff() throws Exception { Timing2 timing = new Timing2(); PersistentNode pen = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); pen = new PersistentNode(client, CreateMode.PERSISTENT, false, "/test/one/two", new byte[0], false); pen.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); pen.start(); assertFalse(pen.waitForInitialCreate(timing.milliseconds(), TimeUnit.MILLISECONDS)); assertTrue(pen.isParentCreationFailure()); } finally { CloseableUtils.closeQuietly(pen); CloseableUtils.closeQuietly(client); } } @Test public void testRecreationWithParentCreationOff() throws Exception { final byte[] TEST_DATA = "hey".getBytes(); Timing2 timing = new Timing2(); PersistentNode pen = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().creatingParentsIfNeeded().forPath("/test/one"); pen = new PersistentNode(client, CreateMode.EPHEMERAL, false, "/test/one/two", TEST_DATA, false); pen.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds()); pen.start(); assertTrue(pen.waitForInitialCreate(timing.milliseconds(), TimeUnit.MILLISECONDS)); assertFalse(pen.isParentCreationFailure()); client.delete().deletingChildrenIfNeeded().forPath("/test/one"); timing.sleepABit(); // persistent node should not be able to recreate itself as the lazy parent creation is disabled assertNull(client.checkExists().forPath("/test/one/two")); assertTrue(pen.isParentCreationFailure()); PersistentNode finalPen = pen; assertThrows(IllegalStateException.class, () -> finalPen.setData(new byte[0])); // The persistent node data should still be the initial one assertArrayEquals(TEST_DATA, pen.getData()); } finally { CloseableUtils.closeQuietly(pen); CloseableUtils.closeQuietly(client); } } @Test public void testQuickClose() throws Exception { Timing timing = new Timing(); PersistentNode pen = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); pen = new PersistentNode(client, CreateMode.PERSISTENT, false, "/test/one/two", new byte[0]); pen.start(); pen.close(); timing.sleepABit(); assertNull(client.checkExists().forPath("/test/one/two")); } finally { CloseableUtils.closeQuietly(pen); CloseableUtils.closeQuietly(client); } } @Test public void testQuickCloseNodeExists() throws Exception { Timing timing = new Timing(); PersistentNode pen = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().creatingParentsIfNeeded().forPath("/test/one/two"); pen = new PersistentNode(client, CreateMode.PERSISTENT, false, "/test/one/two", new byte[0]); pen.start(); pen.close(); timing.sleepABit(); assertNull(client.checkExists().forPath("/test/one/two")); } finally { CloseableUtils.closeQuietly(pen); CloseableUtils.closeQuietly(client); } } @Test public void testEphemeralSequentialWithProtectionReconnection() throws Exception { Timing timing = new Timing(); PersistentNode pen = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().creatingParentsIfNeeded().forPath("/test/one"); pen = new PersistentNode(client, CreateMode.EPHEMERAL_SEQUENTIAL, true, "/test/one/two", new byte[0]); pen.start(); List children = client.getChildren().forPath("/test/one"); System.out.println("children before restart: "+children); assertEquals(1, children.size()); server.stop(); timing.sleepABit(); server.restart(); timing.sleepABit(); List childrenAfter = client.getChildren().forPath("/test/one"); System.out.println("children after restart: "+childrenAfter); assertEquals(children, childrenAfter, "unexpected znodes: "+childrenAfter); } finally { CloseableUtils.closeQuietly(pen); CloseableUtils.closeQuietly(client); } } } TestPersistentTtlNode.java000066400000000000000000000201541442004423600411770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.nodes; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.ZKPaths; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import static org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode.BUILD_INITIAL_CACHE; public class TestPersistentTtlNode extends CuratorTestBase { private final Timing timing = new Timing(); private final long ttlMs = timing.multiple(.10).milliseconds(); // a small number @BeforeAll public static void setUpClass() { System.setProperty("zookeeper.extendedTypesEnabled", "true"); } @BeforeEach @Override public void setup() throws Exception { System.setProperty("znode.container.checkIntervalMs", "1"); super.setup(); } @AfterEach @Override public void teardown() throws Exception { System.clearProperty("znode.container.checkIntervalMs"); super.teardown(); } @Test public void testBasic() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) { client.start(); try (PersistentTtlNode node = new PersistentTtlNode(client, "/test", ttlMs, new byte[0])) { node.start(); assertTrue(node.waitForInitialCreate(timing.session(), TimeUnit.MILLISECONDS)); for ( int i = 0; i < 5; ++i ) { Thread.sleep(ttlMs + (ttlMs / 2)); // sleep a bit more than the TTL assertNotNull(client.checkExists().forPath("/test")); } } assertNotNull(client.checkExists().forPath("/test")); timing.sleepABit(); assertNull(client.checkExists().forPath("/test")); } } @Test public void testForcedDeleteOfTouchNode() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) { client.start(); try (PersistentTtlNode node = new PersistentTtlNode(client, "/test", ttlMs, new byte[0])) { node.start(); assertTrue(node.waitForInitialCreate(timing.session(), TimeUnit.MILLISECONDS)); for ( int i = 0; i < 5; ++i ) { Thread.sleep(ttlMs); client.delete().quietly().forPath(ZKPaths.makePath("test", PersistentTtlNode.DEFAULT_CHILD_NODE_NAME)); } timing.sleepABit(); assertNotNull(client.checkExists().forPath("/test")); } } } @Test public void testRecreationOfParentNodeWithParentCreationOff() throws Exception { final byte[] TEST_DATA = "hey".getBytes(); Timing2 timing = new Timing2(); String containerPath = ZKPaths.makePath("test", "one", "two"); String childPath = ZKPaths.makePath("test", "one", "two", PersistentTtlNode.DEFAULT_CHILD_NODE_NAME); try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) { client.start(); client.create().creatingParentsIfNeeded().forPath("/test/one"); try (PersistentTtlNode node = new PersistentTtlNode(client, containerPath, ttlMs, TEST_DATA, false)) { node.start(); assertTrue(node.waitForInitialCreate(timing.milliseconds(), TimeUnit.MILLISECONDS)); Thread.sleep(ttlMs + (ttlMs / 2)); assertNotNull(client.checkExists().forPath(containerPath)); assertNotNull(client.checkExists().forPath(childPath)); client.delete().deletingChildrenIfNeeded().forPath("/test/one"); timing.sleepABit(); // The underlying persistent node should not be able to recreate itself as the lazy parent creation is disabled assertNull(client.checkExists().forPath(containerPath)); assertNull(client.checkExists().forPath(childPath)); assertThrows(IllegalStateException.class, () -> node.setData(new byte[0])); // The underlying persistent node data should still be the initial one assertArrayEquals(TEST_DATA, node.getData()); } } } @Test public void testEventsOnParent() throws Exception { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) { client.start(); try (PersistentTtlNode node = new PersistentTtlNode(client, "/test", ttlMs, new byte[0])) { try(PathChildrenCache cache = new PathChildrenCache(client, "/", true)) { final Semaphore changes = new Semaphore(0); PathChildrenCacheListener listener = new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if ( (event.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED) && "/test".equals(event.getData().getPath()) ) { changes.release(); } } }; cache.getListenable().addListener(listener); node.start(); assertTrue(node.waitForInitialCreate(timing.session(), TimeUnit.MILLISECONDS)); cache.start(BUILD_INITIAL_CACHE); assertEquals(changes.availablePermits(), 0); timing.sleepABit(); assertEquals(changes.availablePermits(), 0); client.setData().forPath("/test", "changed".getBytes()); assertTrue(timing.acquireSemaphore(changes)); timing.sleepABit(); assertEquals(changes.availablePermits(), 0); } } timing.sleepABit(); assertNull(client.checkExists().forPath("/test")); } } } queue/000077500000000000000000000000001442004423600340545ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipesQueueItemSerializer.java000066400000000000000000000022271442004423600406570ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; class QueueItemSerializer implements QueueSerializer { @Override public byte[] serialize(TestQueueItem item) { return item.str.getBytes(); } @Override public TestQueueItem deserialize(byte[] bytes) { return new TestQueueItem(new String(bytes)); } } QueueTestProducer.java000066400000000000000000000031071442004423600403500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import java.util.concurrent.Callable; public class QueueTestProducer implements Callable { private final DistributedQueue queue; private final int itemQty; private final int startIndex; public QueueTestProducer(DistributedQueue queue, int itemQty, int startIndex) { this.queue = queue; this.itemQty = itemQty; this.startIndex = startIndex; } @Override public Void call() throws Exception { int count = 0; while ( !Thread.currentThread().isInterrupted() && (count < itemQty) ) { queue.put(new TestQueueItem(Integer.toString(count + startIndex))); ++count; } return null; } } TestBoundedDistributedQueue.java000066400000000000000000000243211442004423600423510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class TestBoundedDistributedQueue extends BaseClassForTests { private static final QueueSerializer serializer = new QueueSerializer() { @Override public byte[] serialize(String item) { return item.getBytes(); } @Override public String deserialize(byte[] bytes) { return new String(bytes); } }; @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") @Test public void testMulti() throws Exception { final String PATH = "/queue"; final int CLIENT_QTY = 4; final int MAX_ITEMS = 10; final int ADD_ITEMS = MAX_ITEMS * 100; final QueueConsumer consumer = new QueueConsumer() { @Override public void consumeMessage(String message) throws Exception { Thread.sleep(10); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; final Timing timing = new Timing(); final ExecutorService executor = Executors.newCachedThreadPool(); ExecutorCompletionService completionService = new ExecutorCompletionService(executor); final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); client.create().forPath(PATH); final CountDownLatch isWaitingLatch = new CountDownLatch(1); final AtomicBoolean isDone = new AtomicBoolean(false); final List counts = new CopyOnWriteArrayList(); final Object lock = new Object(); executor.submit ( new Callable() { @Override public Void call() throws Exception { Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { synchronized(lock) { lock.notifyAll(); } } }; while ( !Thread.currentThread().isInterrupted() && client.getState() == CuratorFrameworkState.STARTED && !isDone.get() ) { synchronized(lock) { int size = client.getChildren().usingWatcher(watcher).forPath(PATH).size(); counts.add(size); isWaitingLatch.countDown(); lock.wait(); } } return null; } } ); isWaitingLatch.await(); for ( int i = 0; i < CLIENT_QTY; ++i ) { final int index = i; completionService.submit ( new Callable() { @Override public Void call() throws Exception { CuratorFramework client = null; DistributedQueue queue = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); queue = QueueBuilder.builder(client, consumer, serializer, PATH).executor(executor).maxItems(MAX_ITEMS).putInBackground(false).lockPath("/locks").buildQueue(); queue.start(); for ( int i = 0; i < ADD_ITEMS; ++i ) { queue.put("" + index + "-" + i); } } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } return null; } } ); } for ( int i = 0; i < CLIENT_QTY; ++i ) { completionService.take().get(); } isDone.set(true); synchronized(lock) { lock.notifyAll(); } for ( int count : counts ) { assertTrue(count <= (MAX_ITEMS * CLIENT_QTY), counts.toString()); } } finally { executor.shutdownNow(); CloseableUtils.closeQuietly(client); } } @Test public void testSimple() throws Exception { Timing timing = new Timing(); DistributedQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); try { client.start(); final List messages = new CopyOnWriteArrayList(); final CountDownLatch latch = new CountDownLatch(2); final Semaphore semaphore = new Semaphore(0); QueueConsumer consumer = new QueueConsumer() { @Override public void consumeMessage(String message) throws Exception { messages.add(message); semaphore.acquire(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; queue = QueueBuilder.builder(client, consumer, serializer, "/queue").executor(Executors.newSingleThreadExecutor()).maxItems(1).buildQueue(); queue.start(); QueuePutListener listener = new QueuePutListener() { @Override public void putCompleted(String item) { latch.countDown(); } @Override public void putMultiCompleted(MultiItem items) { } }; queue.getPutListenerContainer().addListener(listener); assertTrue(queue.put("1", timing.milliseconds(), TimeUnit.MILLISECONDS)); // should end up in consumer assertTrue(queue.put("2", timing.milliseconds(), TimeUnit.MILLISECONDS)); // should sit blocking in DistributedQueue assertTrue(timing.awaitLatch(latch)); timing.sleepABit(); assertFalse(queue.put("3", timing.multiple(.5).milliseconds(), TimeUnit.MILLISECONDS)); semaphore.release(100); assertTrue(queue.put("3", timing.milliseconds(), TimeUnit.MILLISECONDS)); assertTrue(queue.put("4", timing.milliseconds(), TimeUnit.MILLISECONDS)); assertTrue(queue.put("5", timing.milliseconds(), TimeUnit.MILLISECONDS)); for ( int i = 0; i < 5; ++i ) { if ( messages.size() == 3 ) { break; } timing.sleepABit(); } timing.sleepABit(); assertEquals(messages, Arrays.asList("1", "2", "3", "4", "5")); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } } TestDistributedDelayQueue.java000066400000000000000000000210451442004423600420270ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.TimeUnit; public class TestDistributedDelayQueue extends BaseClassForTests { @Test public void testLateAddition() throws Exception { Timing timing = new Timing(); DistributedDelayQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, new LongSerializer(), "/test").buildDelayQueue(); queue.start(); queue.put(1L, System.currentTimeMillis() + Integer.MAX_VALUE); // never come out Long value = consumer.take(1, TimeUnit.SECONDS); assertNull(value); queue.put(2L, System.currentTimeMillis()); value = consumer.take(timing.seconds(), TimeUnit.SECONDS); assertEquals(value, Long.valueOf(2)); value = consumer.take(1, TimeUnit.SECONDS); assertNull(value); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { Timing timing = new Timing(); DistributedDelayQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, new LongSerializer(), "/test").buildDelayQueue(); queue.start(); queue.put(1L, System.currentTimeMillis() + 1000); Thread.sleep(100); assertEquals(consumer.size(), 0); // delay hasn't been reached Long value = consumer.take(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertEquals(value, Long.valueOf(1)); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testSimple() throws Exception { final int QTY = 10; Timing timing = new Timing(); DistributedDelayQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, new LongSerializer(), "/test").buildDelayQueue(); queue.start(); Random random = new Random(); for ( int i = 0; i < QTY; ++i ) { long delay = System.currentTimeMillis() + random.nextInt(100); queue.put(delay, delay); } long lastValue = -1; for ( int i = 0; i < QTY; ++i ) { Long value = consumer.take(timing.forWaiting().seconds(), TimeUnit.SECONDS); assertNotNull(value); assertTrue(value >= lastValue); lastValue = value; } } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testSorting() throws Exception { Timing timing = new Timing(); //Need to use a fairly large number to ensure that sorting can take some time. final int QTY = 1000; final int DELAY_MS = timing.multiple(.1).milliseconds(); DistributedDelayQueue putQueue = null; DistributedDelayQueue getQueue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { putQueue = QueueBuilder.builder(client, null, new LongSerializer(), "/test2").putInBackground(false).buildDelayQueue(); putQueue.start(); Map data = new HashMap(); //Make the earliest a second into the future, so we can ensure that everything's //been added prior to the consumption starting. Otherwise it's possible to start //processing entries before they've all been added so the ordering will be //incorrect. long delay = System.currentTimeMillis() + DELAY_MS; for ( long i = 0; i < QTY; ++i ) { data.put(delay, i); //We want to make the elements close together but not exactly the same MS. delay += 1; } //Randomly sort the list List keys = new ArrayList(data.keySet()); Collections.shuffle(keys); //Put the messages onto the queue in random order, but with the appropriate //delay and value for ( Long key : keys ) { putQueue.put(data.get(key), key); } BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); getQueue = QueueBuilder.builder(client, consumer, new LongSerializer(), "/test2").putInBackground(false).buildDelayQueue(); getQueue.start(); long lastValue = -1; for ( int i = 0; i < QTY; ++i ) { Long value = consumer.take(DELAY_MS * 2, TimeUnit.MILLISECONDS); assertNotNull(value); assertEquals(value, new Long(lastValue + 1)); lastValue = value; } } finally { CloseableUtils.closeQuietly(putQueue); CloseableUtils.closeQuietly(getQueue); CloseableUtils.closeQuietly(client); } } private static class LongSerializer implements QueueSerializer { @Override public byte[] serialize(Long item) { return Long.toString(item).getBytes(); } @Override public Long deserialize(byte[] bytes) { return Long.parseLong(new String(bytes)); } } } TestDistributedIdQueue.java000066400000000000000000000143301442004423600413240ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"}) public class TestDistributedIdQueue extends BaseClassForTests { private static final String QUEUE_PATH = "/a/queue"; private static final QueueSerializer serializer = new QueueItemSerializer(); @Test public void testDeletingWithLock() throws Exception { DistributedIdQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final CountDownLatch consumingLatch = new CountDownLatch(1); final CountDownLatch waitLatch = new CountDownLatch(1); QueueConsumer consumer = new QueueConsumer() { @Override public void consumeMessage(TestQueueItem message) throws Exception { consumingLatch.countDown(); waitLatch.await(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH).lockPath("/locks").buildIdQueue(); queue.start(); queue.put(new TestQueueItem("test"), "id"); assertTrue(consumingLatch.await(10, TimeUnit.SECONDS)); // wait until consumer has it assertEquals(queue.remove("id"), 0); waitLatch.countDown(); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testOrdering() throws Exception { final int ITEM_QTY = 100; DistributedIdQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH).buildIdQueue(); queue.start(); List ids = Lists.newArrayList(); for ( int i = 0; i < ITEM_QTY; ++i ) { String id = Double.toString(Math.random()); ids.add(id); queue.put(new TestQueueItem(id), id); } int iteration = 0; while ( consumer.size() < ITEM_QTY ) { assertTrue(++iteration < ITEM_QTY); Thread.sleep(1000); } int i = 0; for ( TestQueueItem item : consumer.getItems() ) { assertEquals(item.str, ids.get(i++)); } } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testRequeuingWithLock() throws Exception { DistributedIdQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final CountDownLatch consumingLatch = new CountDownLatch(1); QueueConsumer consumer = new QueueConsumer() { @Override public void consumeMessage(TestQueueItem message) throws Exception { consumingLatch.countDown(); // Throw an exception so requeuing occurs throw new Exception("Consumer failed"); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH).lockPath("/locks").buildIdQueue(); queue.start(); queue.put(new TestQueueItem("test"), "id"); assertTrue(consumingLatch.await(10, TimeUnit.SECONDS)); // wait until consumer has it // Sleep one more second Thread.sleep(1000); assertTrue(queue.debugIsQueued("id")); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } } TestDistributedPriorityQueue.java000066400000000000000000000242511442004423600426140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; public class TestDistributedPriorityQueue extends BaseClassForTests { @Test public void testMinItemsBeforeRefresh() throws Exception { DistributedPriorityQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final int minItemsBeforeRefresh = 3; BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, new IntSerializer(), "/test").buildPriorityQueue(minItemsBeforeRefresh); queue.start(); for ( int i = 0; i < 10; ++i ) { queue.put(i, 10 + i); } assertEquals(consumer.take(1, TimeUnit.SECONDS), new Integer(0)); queue.put(1000, 1); // lower priority int count = 0; while ( consumer.take(1, TimeUnit.SECONDS) < 1000 ) { ++count; } assertTrue(Math.abs(minItemsBeforeRefresh - count) < minItemsBeforeRefresh, String.format("Diff: %d - min: %d", Math.abs(minItemsBeforeRefresh - count), minItemsBeforeRefresh)); // allows for some slack - testing that within a slop value the newly inserted item with lower priority comes out } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testSortingWhileTaking() throws Exception { Timing timing = new Timing(); DistributedPriorityQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final BlockingQueue blockingQueue = new SynchronousQueue(); QueueConsumer consumer = new QueueConsumer() { @Override public void consumeMessage(Integer message) throws Exception { blockingQueue.put(message); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; queue = QueueBuilder.builder(client, consumer, new IntSerializer(), "/test").buildPriorityQueue(0); queue.start(); for ( int i = 0; i < 10; ++i ) { queue.put(i, 10); } assertEquals(blockingQueue.poll(timing.seconds(), TimeUnit.SECONDS), new Integer(0)); timing.sleepABit(); queue.put(1000, 1); // lower priority timing.sleepABit(); assertEquals(blockingQueue.poll(timing.seconds(), TimeUnit.SECONDS), new Integer(1)); // is in consumer already assertEquals(blockingQueue.poll(timing.seconds(), TimeUnit.SECONDS), new Integer(1000)); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testAdditions() throws Exception { DistributedPriorityQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final CountDownLatch latch = new CountDownLatch(1); QueueSerializer serializer = new IntSerializer() { @Override public Integer deserialize(byte[] bytes) { // gets called in the Queue's event processing thread try { latch.await(); } catch ( InterruptedException e ) { // ignore } return super.deserialize(bytes); } }; BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, serializer, "/test").buildPriorityQueue(1); queue.start(); for ( int i = 0; i < 10; ++i ) { queue.put(10, 10); if ( i == 0 ) { queue.put(1, 1); latch.countDown(); } } assertOrdering(consumer, 10); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testSimple() throws Exception { List nums = new ArrayList(); Timing timing = new Timing(); DistributedPriorityQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final CountDownLatch hasConsumedLatch = new CountDownLatch(1); final CountDownLatch okToConsumeLatch = new CountDownLatch(1); BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)) { @Override public void consumeMessage(Integer message) throws Exception { hasConsumedLatch.countDown(); okToConsumeLatch.await(); super.consumeMessage(message); } }; queue = QueueBuilder.builder(client, consumer, new IntSerializer(), "/test").buildPriorityQueue(0); queue.start(); nums.add(Integer.MIN_VALUE); queue.put(Integer.MIN_VALUE, Integer.MIN_VALUE); // the queue background thread will be blocking with the first item - make sure it's the lowest value assertTrue(timing.awaitLatch(hasConsumedLatch)); Random random = new Random(); for ( int i = 0; i < 100; ++i ) { int priority = random.nextInt(); nums.add(priority); queue.put(priority, priority); } while ( queue.getCache().getData().children.size() < (nums.size() - 1) ) // -1 because the first message has already been consumed { timing.sleepABit(); // wait for the cache to catch up } okToConsumeLatch.countDown(); assertOrdering(consumer, nums.size()); } catch ( AssertionError e ) { StringBuilder message = new StringBuilder(e.getMessage()); for ( int i : nums ) { message.append(i).append("\t").append(DistributedPriorityQueue.priorityToString(i)).append("\n"); } fail(message.toString()); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } private void assertOrdering(BlockingQueueConsumer consumer, int qty) throws Exception { int previous = 0; for ( int i = 0; i < qty; ++i ) { Integer value = consumer.take(10, TimeUnit.SECONDS); assertNotNull(value); if ( i > 0 ) { assertTrue(value >= previous, String.format("Value: (%d:%s) Previous: (%d:%s)", value, DistributedPriorityQueue.priorityToString(value), previous, DistributedPriorityQueue.priorityToString(previous))); } previous = value; } } private static class IntSerializer implements QueueSerializer { @Override public byte[] serialize(Integer item) { return Integer.toString(item).getBytes(); } @Override public Integer deserialize(byte[] bytes) { return Integer.parseInt(new String(bytes)); } } } TestDistributedQueue.java000066400000000000000000000727011442004423600410550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.utils.CloseableUtils; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.apache.zookeeper.CreateMode; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"}) public class TestDistributedQueue extends BaseClassForTests { private static final String QUEUE_PATH = "/a/queue"; private static final QueueSerializer serializer = new QueueItemSerializer(); @Test public void testRetryAfterFailure_Curator56() throws Exception { /* https://issues.apache.org/jira/browse/CURATOR-56 This tests against ever growing node name bug */ DistributedQueue queue = null; final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final int RETRY_COUNT = 1; final CountDownLatch retryCounter = new CountDownLatch(RETRY_COUNT + 1); final List names = new ArrayList(); QueueConsumer consumer = new QueueConsumer() { @Override public void consumeMessage(TestQueueItem messsage) throws Exception { List queueItems = client.getChildren().forPath(QUEUE_PATH); names.add(queueItems.get(0)); if (retryCounter.getCount() > 1) { retryCounter.countDown(); throw new Exception("Something went wrong"); } else { retryCounter.countDown(); } } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH) .lockPath("/lock") .buildQueue(); queue.start(); queue.put(new TestQueueItem("test")); retryCounter.await(10, TimeUnit.SECONDS); assertEquals(retryCounter.getCount(), 0, "Queue item was not consumed. Retry counter is " + retryCounter.getCount()); assertEquals(names.size(), 2); assertEquals(names.get(0).length(), names.get(1).length(), "name1: " + names.get(0) + " - " + "name2: " + names.get(1)); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testCustomExecutor() throws Exception { final int ITERATIONS = 1000; Timing timing = new Timing(); DistributedQueue queue = null; final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final CountDownLatch latch = new CountDownLatch(ITERATIONS); QueueConsumer consumer = new QueueConsumer() { @Override public void consumeMessage(String message) throws Exception { latch.countDown(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; QueueSerializer serializer = new QueueSerializer() { @Override public byte[] serialize(String item) { return item.getBytes(); } @Override public String deserialize(byte[] bytes) { return new String(bytes); } }; Executor executor = Executors.newCachedThreadPool(); final Set used = Sets.newHashSet(); final Set doubleUsed = Sets.newHashSet(); queue = new DistributedQueue ( client, consumer, serializer, QUEUE_PATH, QueueBuilder.defaultThreadFactory, executor, Integer.MAX_VALUE, false, "/lock", QueueBuilder.NOT_SET, true, timing.milliseconds() ) { @SuppressWarnings("SimplifiableConditionalExpression") @Override protected boolean processWithLockSafety(String itemNode, DistributedQueue.ProcessType type) throws Exception { if ( used.contains(itemNode) ) { doubleUsed.add(itemNode); } else { used.add(itemNode); } return (client.getState() == CuratorFrameworkState.STARTED) ? super.processWithLockSafety(itemNode, type) : false; } }; queue.start(); for ( int i = 0; i < ITERATIONS; ++i ) { queue.put(Integer.toString(i)); } assertTrue(timing.awaitLatch(latch)); assertTrue(doubleUsed.size() == 0, doubleUsed.toString()); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testPutListener() throws Exception { final int itemQty = 10; DistributedQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH).buildQueue(); queue.start(); QueueTestProducer producer = new QueueTestProducer(queue, itemQty, 0); final AtomicInteger listenerCalls = new AtomicInteger(0); QueuePutListener listener = new QueuePutListener() { @Override public void putCompleted(TestQueueItem item) { listenerCalls.incrementAndGet(); } @Override public void putMultiCompleted(MultiItem items) { } }; queue.getPutListenerContainer().addListener(listener); ExecutorService service = Executors.newCachedThreadPool(); service.submit(producer); int iteration = 0; while ( consumer.size() < itemQty ) { assertTrue(++iteration < 10); Thread.sleep(1000); } int i = 0; for ( TestQueueItem item : consumer.getItems() ) { assertEquals(item.str, Integer.toString(i++)); } assertEquals(listenerCalls.get(), itemQty); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testErrorMode() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final AtomicReference latch = new AtomicReference(new CountDownLatch(1)); final AtomicInteger count = new AtomicInteger(0); QueueConsumer consumer = new QueueConsumer() { @Override public void consumeMessage(TestQueueItem message) throws Exception { if ( count.incrementAndGet() < 2 ) { throw new Exception(); } latch.get().countDown(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; DistributedQueue queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH).lockPath("/locks").buildQueue(); try { queue.start(); TestQueueItem item = new TestQueueItem("1"); queue.put(item); assertTrue(timing.awaitLatch(latch.get())); assertEquals(count.get(), 2); queue.setErrorMode(ErrorMode.DELETE); count.set(0); latch.set(new CountDownLatch(1)); item = new TestQueueItem("1"); queue.put(item); assertFalse(latch.get().await(5, TimeUnit.SECONDS)); // consumer should get called only once assertEquals(count.get(), 1); } finally { queue.close(); } } finally { client.close(); } } @Test public void testNoDuplicateProcessing() throws Exception { final int itemQty = 1000; final int consumerQty = 4; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); client.start(); try { DistributedQueue producerQueue = QueueBuilder.builder(client, null, serializer, QUEUE_PATH).buildQueue(); try { producerQueue.start(); for ( int i = 0; i < itemQty; ++i ) { TestQueueItem item = new TestQueueItem(Integer.toString(i)); producerQueue.put(item); } producerQueue.flushPuts(timing.multiple(2).seconds(), TimeUnit.SECONDS); } finally { producerQueue.close(); } } finally { client.close(); } final Set consumedMessages = Sets.newHashSet(); final Set duplicateMessages = Sets.newHashSet(); final CountDownLatch latch = new CountDownLatch(itemQty); List> consumers = Lists.newArrayList(); List consumerClients = Lists.newArrayList(); try { final QueueConsumer ourQueue = new QueueConsumer() { @Override public void consumeMessage(TestQueueItem message) { synchronized(consumedMessages) { if ( consumedMessages.contains(message.str) ) { duplicateMessages.add(message.str); } consumedMessages.add(message.str); } latch.countDown(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; for ( int i = 0; i < consumerQty; ++i ) { CuratorFramework thisClient = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); consumerClients.add(thisClient); thisClient.start(); DistributedQueue thisConsumer = QueueBuilder.builder(thisClient, ourQueue, serializer, QUEUE_PATH). lockPath("/a/locks"). buildQueue(); consumers.add(thisConsumer); } for ( DistributedQueue consumer : consumers ) { consumer.start(); } timing.awaitLatch(latch); assertTrue(duplicateMessages.size() == 0, duplicateMessages.toString()); } finally { for ( DistributedQueue consumer : consumers ) { CloseableUtils.closeQuietly(consumer); } for ( CuratorFramework curatorFramework : consumerClients ) { CloseableUtils.closeQuietly(curatorFramework); } } } @Test public void testSafetyWithCrash() throws Exception { final int itemQty = 100; DistributedQueue producerQueue = null; DistributedQueue consumerQueue1 = null; DistributedQueue consumerQueue2 = null; CuratorFramework producerClient = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); CuratorFramework consumerClient1 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); CuratorFramework consumerClient2 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { producerClient.start(); consumerClient1.start(); consumerClient2.start(); ExecutorService service = Executors.newCachedThreadPool(); // make the producer queue { producerQueue = QueueBuilder.builder(producerClient, null, serializer, QUEUE_PATH).buildQueue(); producerQueue.start(); QueueTestProducer producer = new QueueTestProducer(producerQueue, itemQty, 0); service.submit(producer); } final Set takenItems = Sets.newTreeSet(); final Set takenItemsForConsumer1 = Sets.newTreeSet(); final Set takenItemsForConsumer2 = Sets.newTreeSet(); final AtomicReference thrownItemFromConsumer1 = new AtomicReference(null); // make the first consumer queue { final QueueConsumer ourQueue = new QueueConsumer() { @Override public void consumeMessage(TestQueueItem message) throws Exception { synchronized(takenItems) { if ( takenItems.size() > 10 ) { thrownItemFromConsumer1.set(message); throw new Exception("dummy"); // simulate a crash } } addToTakenItems(message, takenItems, itemQty); synchronized(takenItemsForConsumer1) { takenItemsForConsumer1.add(message); } Thread.sleep((long)(Math.random() * 5)); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; consumerQueue1 = QueueBuilder.builder(consumerClient1, ourQueue, serializer, QUEUE_PATH). lockPath("/a/locks"). buildQueue(); consumerQueue1.start(); } // make the second consumer queue { final QueueConsumer ourQueue = new QueueConsumer() { @Override public void consumeMessage(TestQueueItem message) throws Exception { addToTakenItems(message, takenItems, itemQty); synchronized(takenItemsForConsumer2) { takenItemsForConsumer2.add(message); } Thread.sleep((long)(Math.random() * 5)); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; consumerQueue2 = QueueBuilder.builder(consumerClient2, ourQueue, serializer, QUEUE_PATH). lockPath("/a/locks"). buildQueue(); consumerQueue2.start(); } synchronized(takenItems) { while ( takenItems.size() < itemQty ) { takenItems.wait(1000); } } int i = 0; for ( TestQueueItem item : takenItems ) { assertEquals(item.str, Integer.toString(i++)); } assertNotNull(thrownItemFromConsumer1.get()); assertTrue((takenItemsForConsumer2.contains(thrownItemFromConsumer1.get()))); assertTrue(Sets.intersection(takenItemsForConsumer1, takenItemsForConsumer2).size() == 0); } finally { CloseableUtils.closeQuietly(producerQueue); CloseableUtils.closeQuietly(consumerQueue1); CloseableUtils.closeQuietly(consumerQueue2); CloseableUtils.closeQuietly(producerClient); CloseableUtils.closeQuietly(consumerClient1); CloseableUtils.closeQuietly(consumerClient2); } } private void addToTakenItems(TestQueueItem message, Set takenItems, int itemQty) { synchronized(takenItems) { takenItems.add(message); if ( takenItems.size() > itemQty ) { takenItems.notifyAll(); } } } @Test public void testSafetyBasic() throws Exception { final int itemQty = 10; DistributedQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { final BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH). lockPath("/a/locks"). buildQueue(); queue.start(); QueueTestProducer producer = new QueueTestProducer(queue, itemQty, 0); ExecutorService service = Executors.newCachedThreadPool(); service.submit(producer); final CountDownLatch latch = new CountDownLatch(1); service.submit ( new Callable() { @Override public Object call() throws Exception { for ( int i = 0; i < itemQty; ++i ) { TestQueueItem item = consumer.take(); assertEquals(item.str, Integer.toString(i)); } latch.countDown(); return null; } } ); assertTrue(latch.await(10, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testPutMulti() throws Exception { final int itemQty = 100; DistributedQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH).buildQueue(); queue.start(); MultiItem items = new MultiItem() { private int index = 0; @Override public TestQueueItem nextItem() throws Exception { if ( index >= itemQty ) { return null; } return new TestQueueItem(Integer.toString(index++)); } }; queue.putMulti(items); for ( int i = 0; i < itemQty; ++i ) { TestQueueItem queueItem = consumer.take(1, TimeUnit.SECONDS); assertNotNull(queueItem); assertEquals(queueItem, new TestQueueItem(Integer.toString(i))); } } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testMultiPutterSingleGetter() throws Exception { final int itemQty = 100; DistributedQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH).buildQueue(); queue.start(); QueueTestProducer producer1 = new QueueTestProducer(queue, itemQty / 2, 0); QueueTestProducer producer2 = new QueueTestProducer(queue, ((itemQty + 1) / 2), itemQty / 2); ExecutorService service = Executors.newCachedThreadPool(); service.submit(producer1); service.submit(producer2); int iteration = 0; while ( consumer.size() < itemQty ) { assertTrue(++iteration < 10); Thread.sleep(1000); } List items = consumer.getItems(); assertEquals(com.google.common.collect.Sets.newHashSet(items).size(), items.size()); // check no duplicates } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testFlush() throws Exception { final Timing timing = new Timing(); final CountDownLatch latch = new CountDownLatch(1); DistributedQueue queue = null; final CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); try { final AtomicBoolean firstTime = new AtomicBoolean(true); queue = new DistributedQueue(client, null, serializer, "/test", new ThreadFactoryBuilder().build(), MoreExecutors.directExecutor(), 10, true, null, QueueBuilder.NOT_SET, true, 0) { @Override void internalCreateNode(final String path, final byte[] bytes, final BackgroundCallback callback) throws Exception { if ( firstTime.compareAndSet(true, false) ) { Executors.newSingleThreadExecutor().submit ( new Callable() { @Override public Object call() throws Exception { latch.await(); timing.sleepABit(); client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).inBackground(callback).forPath(path, bytes); return null; } } ); } else { super.internalCreateNode(path, bytes, callback); } } }; queue.start(); queue.put(new TestQueueItem("1")); assertFalse(queue.flushPuts(timing.forWaiting().seconds(), TimeUnit.SECONDS)); latch.countDown(); assertTrue(queue.flushPuts(timing.forWaiting().seconds(), TimeUnit.SECONDS)); } finally { if ( latch.getCount() > 0 ) { latch.countDown(); } CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } @Test public void testSimple() throws Exception { final int itemQty = 10; DistributedQueue queue = null; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { BlockingQueueConsumer consumer = new BlockingQueueConsumer(Mockito.mock(ConnectionStateListener.class)); queue = QueueBuilder.builder(client, consumer, serializer, QUEUE_PATH).buildQueue(); queue.start(); QueueTestProducer producer = new QueueTestProducer(queue, itemQty, 0); ExecutorService service = Executors.newCachedThreadPool(); service.submit(producer); int iteration = 0; while ( consumer.size() < itemQty ) { assertTrue(++iteration < 10); Thread.sleep(1000); } int i = 0; for ( TestQueueItem item : consumer.getItems() ) { assertEquals(item.str, Integer.toString(i++)); } } finally { CloseableUtils.closeQuietly(queue); CloseableUtils.closeQuietly(client); } } } TestLongNetworkPartition.java000066400000000000000000000102161442004423600417220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import org.apache.curator.ensemble.fixed.FixedEnsembleProvider; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.compatibility.Timing2; import org.junit.jupiter.api.Test; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; public class TestLongNetworkPartition { private static final Timing2 timing = new Timing2(); // test for https://issues.apache.org/jira/browse/CURATOR-623 @Test public void testLongNetworkPartition() throws Exception { final CompletableFuture done = new CompletableFuture<>(); try (final TestingCluster testingCluster = started(new TestingCluster(1)); final CuratorFramework dyingCuratorFramework = getCuratorFramework(testingCluster.getConnectString()); final DistributedQueue dyingQueue = newQueue(dyingCuratorFramework, item -> { if ( item.equals("0") ) { done.complete(null); } })) { dyingQueue.start(); testingCluster.killServer(testingCluster.getInstances().iterator().next()); timing.forSessionSleep().multiple(2).sleep(); testingCluster.restartServer(testingCluster.getInstances().iterator().next()); try (final CuratorFramework aliveCuratorFramework = getCuratorFramework(testingCluster.getConnectString()); final DistributedQueue aliveQueue = newQueue(aliveCuratorFramework, null)) { aliveQueue.start(); aliveQueue.put("0"); done.get(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); } } } private static DistributedQueue newQueue(CuratorFramework curatorFramework, Consumer consumer) { curatorFramework.start(); return QueueBuilder.builder(curatorFramework, consumer == null ? null : new QueueConsumer() { @Override public void consumeMessage(String o) { consumer.accept(o); } @Override public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) { } }, new QueueSerializer() { @Override public byte[] serialize(String item) { return item.getBytes(); } @Override public String deserialize(byte[] bytes) { return new String(bytes); } }, "/MyChildrenCacheTest/queue").buildQueue(); } private static TestingCluster started(TestingCluster testingCluster) throws Exception { testingCluster.start(); return testingCluster; } private static CuratorFramework getCuratorFramework(String connectString) { return CuratorFrameworkFactory.builder() .ensembleProvider(new FixedEnsembleProvider(connectString, true)) .sessionTimeoutMs(timing.session()) .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build(); } }TestQueueItem.java000066400000000000000000000033421442004423600374640ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; class TestQueueItem implements Comparable { final String str; TestQueueItem(String str) { this.str = str; } @Override public int compareTo(TestQueueItem rhs) { if ( this == rhs ) { return 0; } int val = Integer.parseInt(str); int rhsVal = Integer.parseInt(rhs.str); int diff = val - rhsVal; return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0); } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } TestQueueItem that = (TestQueueItem)o; return str.equals(that.str); } @Override public int hashCode() { return str.hashCode(); } } TestQueueSharder.java000066400000000000000000000232111442004423600401530ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Sets; import org.apache.commons.math.stat.descriptive.SummaryStatistics; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.junit.jupiter.api.Test; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestQueueSharder extends BaseClassForTests { @Test public void testDistribution() throws Exception { final int threshold = 100; final int factor = 10; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); QueueSharder> sharder = null; try { client.start(); final CountDownLatch latch = new CountDownLatch(1); QueueConsumer consumer = new QueueConsumer() { @Override public void consumeMessage(String message) throws Exception { latch.await(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; QueueAllocator> distributedQueueAllocator = makeAllocator(consumer); QueueSharderPolicies policies = QueueSharderPolicies.builder().newQueueThreshold(threshold).thresholdCheckMs(1).build(); sharder = new QueueSharder>(client, distributedQueueAllocator, "/queues", "/leader", policies); sharder.start(); for ( int i = 0; i < (factor * threshold); ++i ) { sharder.getQueue().put(Integer.toString(i)); Thread.sleep(5); } timing.forWaiting().sleepABit(); SummaryStatistics statistics = new SummaryStatistics(); for ( String path : sharder.getQueuePaths() ) { int numChildren = client.checkExists().forPath(path).getNumChildren(); assertTrue(numChildren > 0); assertTrue(numChildren >= (threshold * .1)); statistics.addValue(numChildren); } latch.countDown(); assertTrue(statistics.getMean() >= (threshold * .9)); } finally { timing.sleepABit(); // let queue clear CloseableUtils.closeQuietly(sharder); CloseableUtils.closeQuietly(client); } } @Test public void testSharderWatchSync() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); final BlockingQueueConsumer consumer = makeConsumer(null); QueueAllocator> distributedQueueAllocator = makeAllocator(consumer); QueueSharderPolicies policies = QueueSharderPolicies.builder().newQueueThreshold(2).thresholdCheckMs(1).build(); QueueSharder> sharder1 = new QueueSharder>(client, distributedQueueAllocator, "/queues", "/leader", policies); QueueSharder> sharder2 = new QueueSharder>(client, distributedQueueAllocator, "/queues", "/leader", policies); try { client.start(); sharder1.start(); sharder2.start(); for ( int i = 0; i < 20; ++i ) { sharder1.getQueue().put(Integer.toString(i)); } timing.sleepABit(); assertTrue((sharder1.getShardQty() > 1) || (sharder2.getShardQty() > 1)); timing.forWaiting().sleepABit(); assertEquals(sharder1.getShardQty(), sharder2.getShardQty()); } finally { timing.sleepABit(); // let queues clear CloseableUtils.closeQuietly(sharder1); CloseableUtils.closeQuietly(sharder2); CloseableUtils.closeQuietly(client); } } @Test public void testSimpleDistributedQueue() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); final CountDownLatch latch = new CountDownLatch(1); final BlockingQueueConsumer consumer = makeConsumer(latch); QueueAllocator> distributedQueueAllocator = makeAllocator(consumer); QueueSharderPolicies policies = QueueSharderPolicies.builder().newQueueThreshold(2).thresholdCheckMs(1).build(); QueueSharder> sharder = new QueueSharder>(client, distributedQueueAllocator, "/queues", "/leader", policies); try { client.start(); sharder.start(); sharder.getQueue().put("one"); sharder.getQueue().put("two"); sharder.getQueue().put("three"); sharder.getQueue().put("four"); latch.countDown(); timing.sleepABit(); sharder.getQueue().put("five"); sharder.getQueue().put("six"); sharder.getQueue().put("seven"); sharder.getQueue().put("eight"); timing.sleepABit(); assertTrue(sharder.getShardQty() > 1); Set consumed = Sets.newHashSet(); for ( int i = 0; i < 8; ++i ) { String s = consumer.take(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); assertNotNull(s); consumed.add(s); } assertEquals(consumed, Sets.newHashSet("one", "two", "three", "four", "five", "six", "seven", "eight")); int shardQty = sharder.getShardQty(); sharder.close(); // check re-open sharder = new QueueSharder>(client, distributedQueueAllocator, "/queues", "/leader", policies); sharder.start(); assertEquals(sharder.getShardQty(), shardQty); } finally { CloseableUtils.closeQuietly(sharder); CloseableUtils.closeQuietly(client); } } private QueueAllocator> makeAllocator(final QueueConsumer consumer) { final QueueSerializer serializer = new QueueSerializer() { @Override public byte[] serialize(String item) { return item.getBytes(); } @Override public String deserialize(byte[] bytes) { return new String(bytes); } }; return new QueueAllocator>() { @Override public DistributedQueue allocateQueue(CuratorFramework client, String queuePath) { return QueueBuilder.builder(client, consumer, serializer, queuePath).buildQueue(); } }; } private BlockingQueueConsumer makeConsumer(final CountDownLatch latch) { ConnectionStateListener connectionStateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; return new BlockingQueueConsumer(connectionStateListener) { @Override public void consumeMessage(String message) throws Exception { if ( latch != null ) { latch.await(); } super.consumeMessage(message); } }; } } TestSimpleDistributedQueue.java000066400000000000000000000401171442004423600422230ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/queue/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.queue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.TestingServer; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.junit.jupiter.api.Test; import java.util.NoSuchElementException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class TestSimpleDistributedQueue extends BaseClassForTests { private static abstract class QueueUser implements Runnable { private static final String QUEUE_PATH = "/queue"; private static final int ITEM_COUNT = 10; protected final SimpleDistributedQueue queue; private final int sleepMillis; public QueueUser(CuratorFramework curator, int sleepMillis) { this.queue = new SimpleDistributedQueue(curator, QUEUE_PATH); this.sleepMillis = sleepMillis; } @Override public void run() { try { for ( int i = 0; i < ITEM_COUNT; i++ ) { processItem(i); Thread.sleep(sleepMillis); } } catch ( Exception e ) { throw new RuntimeException(e); } } protected abstract void processItem(int itemNumber) throws Exception; } @Test public void testHangFromContainerLoss() throws Exception { // for CURATOR-308 server.close(); System.setProperty("znode.container.checkIntervalMs", "100"); server = new TestingServer(); Timing timing = new Timing().multiple(.1); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(new QueueUser(client, timing.milliseconds()) { @Override protected void processItem(int itemNumber) throws Exception { System.out.println("Offering item"); queue.offer(new byte[]{(byte)itemNumber}); } }); executor.execute(new QueueUser(client, timing.multiple(.5).milliseconds()) { @Override protected void processItem(int itemNumber) throws Exception { System.out.println("Taking item " + itemNumber); byte[] item = queue.take(); if ( item == null ) { throw new IllegalStateException("Null result for item " + itemNumber); } System.out.println("Got item " + item[0]); } }); executor.shutdown(); assertTrue(executor.awaitTermination((QueueUser.ITEM_COUNT * 2) * timing.milliseconds(), TimeUnit.MILLISECONDS)); } finally { CloseableUtils.closeQuietly(client); System.clearProperty("znode.container.checkIntervalMs"); } } @Test public void testPollWithTimeout() throws Exception { CuratorFramework clients[] = null; try { String dir = "/testOffer1"; final int num_clients = 1; clients = new CuratorFramework[num_clients]; SimpleDistributedQueue queueHandles[] = new SimpleDistributedQueue[num_clients]; for ( int i = 0; i < clients.length; i++ ) { clients[i] = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients[i].start(); queueHandles[i] = new SimpleDistributedQueue(clients[i], dir); } assertNull(queueHandles[0].poll(3, TimeUnit.SECONDS)); } finally { closeAll(clients); } } @Test public void testOffer1() throws Exception { CuratorFramework clients[] = null; try { String dir = "/testOffer1"; String testString = "Hello World"; final int num_clients = 1; clients = new CuratorFramework[num_clients]; SimpleDistributedQueue queueHandles[] = new SimpleDistributedQueue[num_clients]; for ( int i = 0; i < clients.length; i++ ) { clients[i] = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients[i].start(); queueHandles[i] = new SimpleDistributedQueue(clients[i], dir); } queueHandles[0].offer(testString.getBytes()); byte dequeuedBytes[] = queueHandles[0].remove(); assertEquals(new String(dequeuedBytes), testString); } finally { closeAll(clients); } } @Test public void testOffer2() throws Exception { CuratorFramework clients[] = null; try { String dir = "/testOffer2"; String testString = "Hello World"; final int num_clients = 2; clients = new CuratorFramework[num_clients]; SimpleDistributedQueue queueHandles[] = new SimpleDistributedQueue[num_clients]; for ( int i = 0; i < clients.length; i++ ) { clients[i] = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients[i].start(); queueHandles[i] = new SimpleDistributedQueue(clients[i], dir); } queueHandles[0].offer(testString.getBytes()); byte dequeuedBytes[] = queueHandles[1].remove(); assertEquals(new String(dequeuedBytes), testString); } finally { closeAll(clients); } } @Test public void testTake1() throws Exception { CuratorFramework clients[] = null; try { String dir = "/testTake1"; String testString = "Hello World"; final int num_clients = 1; clients = new CuratorFramework[num_clients]; SimpleDistributedQueue queueHandles[] = new SimpleDistributedQueue[num_clients]; for ( int i = 0; i < clients.length; i++ ) { clients[i] = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients[i].start(); queueHandles[i] = new SimpleDistributedQueue(clients[i], dir); } queueHandles[0].offer(testString.getBytes()); byte dequeuedBytes[] = queueHandles[0].take(); assertEquals(new String(dequeuedBytes), testString); } finally { closeAll(clients); } } @Test public void testRemova1() throws Exception { CuratorFramework clients[] = null; try { String dir = "/testRemove1"; final int num_clients = 1; clients = new CuratorFramework[num_clients]; SimpleDistributedQueue queueHandles[] = new SimpleDistributedQueue[num_clients]; for ( int i = 0; i < clients.length; i++ ) { clients[i] = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients[i].start(); queueHandles[i] = new SimpleDistributedQueue(clients[i], dir); } try { queueHandles[0].remove(); } catch ( NoSuchElementException e ) { return; } assertTrue(false); } finally { closeAll(clients); } } public void createNremoveMtest(String dir, int n, int m) throws Exception { CuratorFramework clients[] = null; try { String testString = "Hello World"; final int num_clients = 2; clients = new CuratorFramework[num_clients]; SimpleDistributedQueue queueHandles[] = new SimpleDistributedQueue[num_clients]; for ( int i = 0; i < clients.length; i++ ) { clients[i] = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients[i].start(); queueHandles[i] = new SimpleDistributedQueue(clients[i], dir); } for ( int i = 0; i < n; i++ ) { String offerString = testString + i; queueHandles[0].offer(offerString.getBytes()); } byte data[] = null; for ( int i = 0; i < m; i++ ) { data = queueHandles[1].remove(); } assertEquals(new String(data), testString + (m - 1)); } finally { closeAll(clients); } } @Test public void testRemove2() throws Exception { createNremoveMtest("/testRemove2", 10, 2); } @Test public void testRemove3() throws Exception { createNremoveMtest("/testRemove3", 1000, 1000); } public void createNremoveMelementTest(String dir, int n, int m) throws Exception { CuratorFramework clients[] = null; try { String testString = "Hello World"; final int num_clients = 2; clients = new CuratorFramework[num_clients]; SimpleDistributedQueue queueHandles[] = new SimpleDistributedQueue[num_clients]; for ( int i = 0; i < clients.length; i++ ) { clients[i] = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients[i].start(); queueHandles[i] = new SimpleDistributedQueue(clients[i], dir); } for ( int i = 0; i < n; i++ ) { String offerString = testString + i; queueHandles[0].offer(offerString.getBytes()); } for ( int i = 0; i < m; i++ ) { queueHandles[1].remove(); } assertEquals(new String(queueHandles[1].element()), testString + m); } finally { closeAll(clients); } } @Test public void testElement1() throws Exception { createNremoveMelementTest("/testElement1", 1, 0); } @Test public void testElement2() throws Exception { createNremoveMelementTest("/testElement2", 10, 2); } @Test public void testElement3() throws Exception { createNremoveMelementTest("/testElement3", 1000, 500); } @Test public void testElement4() throws Exception { createNremoveMelementTest("/testElement4", 1000, 1000 - 1); } @Test public void testTakeWait1() throws Exception { CuratorFramework clients[] = null; try { String dir = "/testTakeWait1"; final String testString = "Hello World"; final int num_clients = 1; clients = new CuratorFramework[num_clients]; final SimpleDistributedQueue queueHandles[] = new SimpleDistributedQueue[num_clients]; for ( int i = 0; i < clients.length; i++ ) { clients[i] = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients[i].start(); queueHandles[i] = new SimpleDistributedQueue(clients[i], dir); } final byte[] takeResult[] = new byte[1][]; Thread takeThread = new Thread() { public void run() { try { takeResult[0] = queueHandles[0].take(); } catch ( Exception e ) { // ignore } } }; takeThread.start(); Thread.sleep(1000); Thread offerThread = new Thread() { public void run() { try { queueHandles[0].offer(testString.getBytes()); } catch ( Exception e ) { // ignore } } }; offerThread.start(); offerThread.join(); takeThread.join(); assertTrue(takeResult[0] != null); assertEquals(new String(takeResult[0]), testString); } finally { closeAll(clients); } } @Test public void testTakeWait2() throws Exception { String dir = "/testTakeWait2"; final String testString = "Hello World"; final int num_clients = 1; final CuratorFramework clients[] = new CuratorFramework[num_clients]; final SimpleDistributedQueue queueHandles[] = new SimpleDistributedQueue[num_clients]; for ( int i = 0; i < clients.length; i++ ) { clients[i] = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients[i].start(); queueHandles[i] = new SimpleDistributedQueue(clients[i], dir); } int num_attempts = 2; for ( int i = 0; i < num_attempts; i++ ) { final byte[] takeResult[] = new byte[1][]; final String threadTestString = testString + i; Thread takeThread = new Thread() { public void run() { try { takeResult[0] = queueHandles[0].take(); } catch ( Exception e ) { // ignore } } }; takeThread.start(); Thread.sleep(1000); Thread offerThread = new Thread() { public void run() { try { queueHandles[0].offer(threadTestString.getBytes()); } catch ( Exception e ) { // ignore } } }; offerThread.start(); offerThread.join(); takeThread.join(); assertTrue(takeResult[0] != null); assertEquals(new String(takeResult[0]), threadTestString); } } private void closeAll(CuratorFramework[] clients) { if ( clients != null ) { for ( CuratorFramework c : clients ) { CloseableUtils.closeQuietly(c); } } } } shared/000077500000000000000000000000001442004423600341765ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipesTestSharedCount.java000066400000000000000000000532741442004423600401330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/shared/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.shared; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.WatchedEvent; import org.junit.jupiter.api.Test; import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.Phaser; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class TestSharedCount extends CuratorTestBase { @Test public void testMultiClients() throws Exception { final int CLIENT_QTY = 5; List>> futures = Lists.newArrayList(); final List clients = new CopyOnWriteArrayList(); final List counts = new CopyOnWriteArrayList(); try { final CountDownLatch startLatch = new CountDownLatch(CLIENT_QTY); final Semaphore semaphore = new Semaphore(0); ExecutorService service = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("Test-%d").build()); for ( int i = 0; i < CLIENT_QTY; ++i ) { Future> future = service.submit ( new Callable>() { @Override public List call() throws Exception { final List countList = Lists.newArrayList(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients.add(client); client.start(); client.checkExists().forPath("/"); // clear initial connect event SharedCount count = new SharedCount(client, "/count", 10); counts.add(count); final CountDownLatch latch = new CountDownLatch(1); count.addListener ( new SharedCountListener() { @Override public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception { if ( newCount < 0 ) { latch.countDown(); } else { countList.add(newCount); } semaphore.release(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } } ); count.start(); startLatch.countDown(); latch.await(); return countList; } } ); futures.add(future); } CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); clients.add(client); client.start(); client.checkExists().forPath("/"); // clear initial connect event assertTrue(startLatch.await(10, TimeUnit.SECONDS)); SharedCount count = new SharedCount(client, "/count", 10); counts.add(count); count.start(); List countList = Lists.newArrayList(); Random random = new Random(); for ( int i = 0; i < 100; ++i ) { Thread.sleep(random.nextInt(10)); int next = random.nextInt(100); countList.add(next); count.setCount(next); assertTrue(semaphore.tryAcquire(CLIENT_QTY, 10, TimeUnit.SECONDS)); } count.setCount(-1); for ( Future> future : futures ) { List thisCountList = future.get(); assertEquals(thisCountList, countList); } } finally { for ( SharedCount count : counts ) { CloseableUtils.closeQuietly(count); } for ( CuratorFramework client : clients ) { CloseableUtils.closeQuietly(client); } } } @Test public void testSimple() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); SharedCount count = new SharedCount(client, "/count", 0); try { client.start(); count.start(); final CountDownLatch setLatch = new CountDownLatch(3); SharedCountListener listener = new SharedCountListener() { @Override public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception { setLatch.countDown(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { // nop } }; count.addListener(listener); assertTrue(count.trySetCount(1)); timing.sleepABit(); assertTrue(count.trySetCount(2)); timing.sleepABit(); assertTrue(count.trySetCount(10)); timing.sleepABit(); assertEquals(count.getCount(), 10); assertTrue(new Timing().awaitLatch(setLatch)); } finally { CloseableUtils.closeQuietly(count); CloseableUtils.closeQuietly(client); } } @Test public void testSimpleVersioned() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); SharedCount count = new SharedCount(client, "/count", 0); client.start(); try { count.start(); VersionedValue current = count.getVersionedValue(); assertEquals(current.getVersion(), 0); assertTrue(count.trySetCount(current, 1)); current = count.getVersionedValue(); assertEquals(current.getVersion(), 1); assertEquals(count.getCount(), 1); assertTrue(count.trySetCount(current, 5)); current = count.getVersionedValue(); assertEquals(current.getVersion(), 2); assertEquals(count.getCount(), 5); assertTrue(count.trySetCount(current, 10)); current = count.getVersionedValue(); assertEquals(current.getVersion(), 3); assertEquals(count.getCount(), 10); // Wrong value assertFalse(count.trySetCount(new VersionedValue(3, 20), 7)); // Wrong version assertFalse(count.trySetCount(new VersionedValue(10, 10), 7)); // Server changed client.setData().forPath("/count", SharedCount.toBytes(88)); assertFalse(count.trySetCount(current, 234)); } finally { CloseableUtils.closeQuietly(count); CloseableUtils.closeQuietly(client); } } @Test public void testMultiClientVersioned() throws Exception { Timing timing = new Timing(); CuratorFramework client1 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); CuratorFramework client2 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); SharedCount count1 = new SharedCount(client1, "/count", 0); SharedCount count2 = new SharedCount(client2, "/count", 0); try { client1.start(); client2.start(); count1.start(); count2.start(); VersionedValue versionedValue = count1.getVersionedValue(); assertTrue(count1.trySetCount(versionedValue, 10)); timing.sleepABit(); versionedValue = count2.getVersionedValue(); assertTrue(count2.trySetCount(versionedValue, 20)); timing.sleepABit(); final CountDownLatch setLatch = new CountDownLatch(2); SharedCountListener listener = new SharedCountListener() { @Override public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception { setLatch.countDown(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { // nop } }; count1.addListener(listener); VersionedValue versionedValue1 = count1.getVersionedValue(); VersionedValue versionedValue2 = count2.getVersionedValue(); assertTrue(count2.trySetCount(versionedValue2, 30)); assertFalse(count1.trySetCount(versionedValue1, 40)); versionedValue1 = count1.getVersionedValue(); assertTrue(count1.trySetCount(versionedValue1, 40)); assertTrue(timing.awaitLatch(setLatch)); } finally { CloseableUtils.closeQuietly(count2); CloseableUtils.closeQuietly(count1); CloseableUtils.closeQuietly(client2); CloseableUtils.closeQuietly(client1); } } @Test public void testMultiClientDifferentSeed() throws Exception { CuratorFramework client1 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); CuratorFramework client2 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); SharedCount count1 = new SharedCount(client1, "/count", 10); SharedCount count2 = new SharedCount(client2, "/count", 20); try { client1.start(); client2.start(); count1.start(); count2.start(); assertEquals(count1.getCount(), 10); assertEquals(count2.getCount(), 10); } finally { CloseableUtils.closeQuietly(count2); CloseableUtils.closeQuietly(count1); CloseableUtils.closeQuietly(client2); CloseableUtils.closeQuietly(client1); } } @Test public void testDisconnectEventOnWatcherDoesNotRetry() throws Exception { final CountDownLatch gotSuspendEvent = new CountDownLatch(1); CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryNTimes(10, 1000)); curatorFramework.start(); curatorFramework.blockUntilConnected(); SharedCount sharedCount = new SharedCount(curatorFramework, "/count", 10); sharedCount.start(); curatorFramework.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if (newState == ConnectionState.SUSPENDED) { gotSuspendEvent.countDown(); } } }); try { server.stop(); // if watcher goes into 10second retry loop we won't get timely notification assertTrue(gotSuspendEvent.await(5, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(sharedCount); CloseableUtils.closeQuietly(curatorFramework); } } @Test public void testDisconnectReconnectEventDoesNotFireValueWatcher() throws Exception { final CountDownLatch gotSuspendEvent = new CountDownLatch(1); final CountDownLatch gotChangeEvent = new CountDownLatch(1); final CountDownLatch getReconnectEvent = new CountDownLatch(1); final AtomicInteger numChangeEvents = new AtomicInteger(0); CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryNTimes(10, 500)); curatorFramework.start(); curatorFramework.blockUntilConnected(); SharedCount sharedCount = new SharedCount(curatorFramework, "/count", 10); sharedCount.addListener(new SharedCountListener() { @Override public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception { numChangeEvents.incrementAndGet(); gotChangeEvent.countDown(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if (newState == ConnectionState.SUSPENDED) { gotSuspendEvent.countDown(); } else if (newState == ConnectionState.RECONNECTED) { getReconnectEvent.countDown(); } } }); sharedCount.start(); try { sharedCount.setCount(11); assertTrue(gotChangeEvent.await(2, TimeUnit.SECONDS)); server.stop(); assertTrue(gotSuspendEvent.await(2, TimeUnit.SECONDS)); server.restart(); assertTrue(getReconnectEvent.await(2, TimeUnit.SECONDS)); assertEquals(numChangeEvents.get(), 1); sharedCount.trySetCount(sharedCount.getVersionedValue(), 12); // flush background task queue final CountDownLatch flushDone = new CountDownLatch(1); curatorFramework.getData().inBackground(new BackgroundCallback() { @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { flushDone.countDown(); } }).forPath("/count"); flushDone.await(5, TimeUnit.SECONDS); // CURATOR-311: when a Curator client's state became RECONNECTED, countHasChanged method is called back // because the Curator client calls readValueAndNotifyListenersInBackground in SharedValue#ConnectionStateListener#stateChanged. assertTrue(numChangeEvents.get() > 2); } finally { CloseableUtils.closeQuietly(sharedCount); CloseableUtils.closeQuietly(curatorFramework); } } @Test public void testDisconnectReconnectWithMultipleClients() throws Exception { Timing timing = new Timing(); CuratorFramework curatorFramework1 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryNTimes(10, 500)); CuratorFramework curatorFramework2 = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryNTimes(10, 500)); curatorFramework1.start(); curatorFramework1.checkExists().forPath("/"); // clear initial connect events curatorFramework2.start(); curatorFramework2.checkExists().forPath("/"); // clear initial connect events final String sharedCountPath = "/count"; final int initialCount = 10; SharedCount sharedCount1 = new SharedCount(curatorFramework1, sharedCountPath, initialCount); SharedCount sharedCountWithFaultyWatcher = createSharedCountWithFaultyWatcher(curatorFramework2, sharedCountPath, initialCount); class MySharedCountListener implements SharedCountListener { final public Phaser gotSuspendEvent = new Phaser(1); final public Phaser gotChangeEvent = new Phaser(1); final public Phaser getReconnectEvent = new Phaser(1); final public AtomicInteger numChangeEvents = new AtomicInteger(0); @Override public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception { numChangeEvents.incrementAndGet(); gotChangeEvent.arrive(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if (newState == ConnectionState.SUSPENDED) { gotSuspendEvent.arrive(); } else if (newState == ConnectionState.RECONNECTED) { getReconnectEvent.arrive(); } } } MySharedCountListener listener1 = new MySharedCountListener(); sharedCount1.addListener(listener1); sharedCount1.start(); MySharedCountListener listener2 = new MySharedCountListener(); sharedCountWithFaultyWatcher.addListener(listener2); try { sharedCount1.setCount(12); assertEquals(listener1.gotChangeEvent.awaitAdvanceInterruptibly(0, timing.seconds(), TimeUnit.SECONDS), 1); assertEquals(sharedCount1.getCount(), 12); assertEquals(sharedCountWithFaultyWatcher.getCount(), 10); // new counter with faultyWatcher start sharedCountWithFaultyWatcher.start(); for (int i = 0; i < 10; i++) { sharedCount1.setCount(13 + i); assertEquals(sharedCount1.getCount(), 13 + i); server.restart(); assertEquals(listener2.getReconnectEvent.awaitAdvanceInterruptibly(i, timing.forWaiting().seconds(), TimeUnit.SECONDS), i + 1); // CURATOR-311 introduces to Curator's client reading server's shared count value // when client's state gets ConnectionState.RECONNECTED. Following tests ensures that. assertEquals(listener2.gotChangeEvent.awaitAdvanceInterruptibly(i, timing.forWaiting().seconds(), TimeUnit.SECONDS), i + 1); assertEquals(sharedCountWithFaultyWatcher.getCount(), 13 + i); } } finally { CloseableUtils.closeQuietly(sharedCount1); CloseableUtils.closeQuietly(curatorFramework1); CloseableUtils.closeQuietly(sharedCountWithFaultyWatcher); CloseableUtils.closeQuietly(curatorFramework2); } } private SharedCount createSharedCountWithFaultyWatcher(CuratorFramework curatorFramework, String path, int val) { final CuratorWatcher faultyWatcher = new CuratorWatcher() { @Override public void process(WatchedEvent event) throws Exception { // everything will be ignored } }; class FaultySharedValue extends SharedValue { public FaultySharedValue(CuratorFramework client, String path, byte[] seedValue) { super(client.newWatcherRemoveCuratorFramework(), path, seedValue, faultyWatcher); } }; final SharedValue faultySharedValue = new FaultySharedValue(curatorFramework, path, SharedCount.toBytes(val)); class FaultySharedCount extends SharedCount { public FaultySharedCount(CuratorFramework client, String path, int val) { super(client, path, faultySharedValue); } }; return new FaultySharedCount(curatorFramework, path, val); } } watch/000077500000000000000000000000001442004423600340365ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipesTestPersistentWatcher.java000066400000000000000000000103541442004423600412220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/java/org/apache/curator/framework/recipes/watch/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.recipes.watch; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; @Tag(CuratorTestBase.zk36Group) public class TestPersistentWatcher extends CuratorTestBase { @Test public void testConnectionLostRecursive() throws Exception { internalTest(true); } @Test public void testConnectionLost() throws Exception { internalTest(false); } private void internalTest(boolean recursive) throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)) ) { CountDownLatch lostLatch = new CountDownLatch(1); CountDownLatch reconnectedLatch = new CountDownLatch(1); client.start(); client.getConnectionStateListenable().addListener((__, newState) -> { if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } else if ( newState == ConnectionState.RECONNECTED ) { reconnectedLatch.countDown(); } }); try ( PersistentWatcher persistentWatcher = new PersistentWatcher(client, "/top/main", recursive) ) { persistentWatcher.start(); BlockingQueue events = new LinkedBlockingQueue<>(); persistentWatcher.getListenable().addListener(events::add); client.create().creatingParentsIfNeeded().forPath("/top/main/a"); assertEquals(timing.takeFromQueue(events).getPath(), "/top/main"); if ( recursive ) { assertEquals(timing.takeFromQueue(events).getPath(), "/top/main/a"); } else { assertEquals(timing.takeFromQueue(events).getPath(), "/top/main"); // child added } server.stop(); assertEquals(timing.takeFromQueue(events).getState(), Watcher.Event.KeeperState.Disconnected); assertTrue(timing.awaitLatch(lostLatch)); server.restart(); assertTrue(timing.awaitLatch(reconnectedLatch)); timing.sleepABit(); // time to allow watcher to get reset events.clear(); if ( recursive ) { client.setData().forPath("/top/main/a", "foo".getBytes()); assertEquals(timing.takeFromQueue(events).getType(), Watcher.Event.EventType.NodeDataChanged); } client.setData().forPath("/top/main", "bar".getBytes()); assertEquals(timing.takeFromQueue(events).getPath(), "/top/main"); } } } }curator-apache-curator-5.5.0/curator-recipes/src/test/resources/000077500000000000000000000000001442004423600247625ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-recipes/src/test/resources/log4j.properties000066400000000000000000000021071442004423600301170ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. log4j.rootLogger=ERROR, console log4j.logger.org.apache.curator=DEBUG, console log4j.additivity.org.apache.curator=false log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n curator-apache-curator-5.5.0/curator-test-zk35/000077500000000000000000000000001442004423600213215ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/pom.xml000066400000000000000000000164231442004423600226440ustar00rootroot00000000000000 org.apache.curator apache-curator 5.5.0 4.0.0 curator-test-zk35 3.5.7 org.apache.curator curator-framework org.apache.zookeeper zookeeper org.apache.curator curator-x-async org.apache.zookeeper zookeeper org.apache.curator curator-recipes org.apache.zookeeper zookeeper org.apache.zookeeper zookeeper ${zookeeper-35-version} com.sun.jmx jmxri com.sun.jdmk jmxtools javax.jms jms junit junit org.slf4j slf4j-log4j12 org.apache.curator curator-test org.apache.zookeeper zookeeper test org.apache.curator curator-recipes org.apache.zookeeper zookeeper test-jar test org.apache.curator curator-framework org.apache.zookeeper zookeeper test-jar test org.apache.commons commons-math test com.fasterxml.jackson.core jackson-core test com.fasterxml.jackson.core jackson-databind test com.fasterxml.jackson.dataformat jackson-dataformat-yaml ${jackson-version} test org.junit.jupiter junit-jupiter-api test org.slf4j slf4j-log4j12 test org.awaitility awaitility test org.apache.maven.plugins maven-deploy-plugin deploy true deploy org.apache.maven.plugins maven-surefire-plugin org.apache.curator:curator-framework org.apache.curator:curator-recipes zk35TestCompatibility zk36,zk37 curator-apache-curator-5.5.0/curator-test-zk35/src/000077500000000000000000000000001442004423600221105ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/src/test/000077500000000000000000000000001442004423600230675ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/src/test/java/000077500000000000000000000000001442004423600240105ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/src/test/java/org/000077500000000000000000000000001442004423600245775ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/src/test/java/org/apache/000077500000000000000000000000001442004423600260205ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/src/test/java/org/apache/curator/000077500000000000000000000000001442004423600274775ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/src/test/java/org/apache/curator/framework/000077500000000000000000000000001442004423600314745ustar00rootroot00000000000000TestCompatibility.java000066400000000000000000000041101442004423600357250ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/src/test/java/org/apache/curator/framework/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework; import static org.junit.jupiter.api.Assertions.assertThrows; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.x.async.AsyncCuratorFramework; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; public class TestCompatibility extends CuratorTestBase { @Test @Tag(zk35TestCompatibilityGroup) public void testPersistentWatchesNotAvailable() { assertThrows(IllegalStateException.class, ()-> { try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1))) { client.start(); client.watchers().add().forPath("/foo"); } }); } @Test @Tag(zk35TestCompatibilityGroup) public void testPersistentWatchesNotAvailableAsync() { assertThrows(IllegalStateException.class, ()->{ try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.addWatch().forPath("/foo"); } }); } } curator-apache-curator-5.5.0/curator-test-zk35/src/test/java/org/apache/curator/zk35/000077500000000000000000000000001442004423600302735ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/src/test/java/org/apache/curator/zk35/TestIs35.java000066400000000000000000000027731442004423600325320ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.zk35; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.Compatibility; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; public class TestIs35 extends CuratorTestBase { @Test @Tag(zk35TestCompatibilityGroup) public void testIsZk35() { assertFalse(Compatibility.hasGetReachableOrOneMethod()); assertTrue(Compatibility.hasAddrField()); assertFalse(Compatibility.hasPersistentWatchers()); } @Override protected void createServer() { // NOP } } curator-apache-curator-5.5.0/curator-test-zk35/src/test/resources/000077500000000000000000000000001442004423600251015ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk35/src/test/resources/log4j.properties000066400000000000000000000021071442004423600302360ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. log4j.rootLogger=ERROR, console log4j.logger.org.apache.curator=DEBUG, console log4j.additivity.org.apache.curator=false log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n curator-apache-curator-5.5.0/curator-test-zk36/000077500000000000000000000000001442004423600213225ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk36/pom.xml000066400000000000000000000177021442004423600226460ustar00rootroot00000000000000 org.apache.curator apache-curator 5.5.0 4.0.0 curator-test-zk36 3.6.3 org.apache.curator curator-framework org.apache.zookeeper zookeeper org.apache.curator curator-x-async org.apache.zookeeper zookeeper org.apache.curator curator-recipes org.apache.zookeeper zookeeper org.apache.zookeeper zookeeper ${zookeeper-36-version} com.sun.jmx jmxri com.sun.jdmk jmxtools javax.jms jms junit junit org.slf4j slf4j-log4j12 org.apache.curator curator-test org.apache.zookeeper zookeeper test org.apache.curator curator-recipes org.apache.zookeeper zookeeper test-jar test org.apache.curator curator-framework org.apache.zookeeper zookeeper test-jar test org.apache.curator curator-client org.apache.zookeeper zookeeper test-jar test org.apache.commons commons-math test com.fasterxml.jackson.core jackson-core test com.fasterxml.jackson.core jackson-databind test com.fasterxml.jackson.dataformat jackson-dataformat-yaml ${jackson-version} test org.junit.jupiter junit-jupiter-api test org.mockito mockito-core test org.awaitility awaitility test org.slf4j slf4j-log4j12 test org.apache.maven.plugins maven-deploy-plugin deploy true deploy org.apache.maven.plugins maven-surefire-plugin org.apache.curator:curator-framework org.apache.curator:curator-recipes org.apache.curator:curator-client zk36,zk35TestCompatibility zk37 curator-apache-curator-5.5.0/curator-test-zk36/src/000077500000000000000000000000001442004423600221115ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk36/src/test/000077500000000000000000000000001442004423600230705ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk36/src/test/java/000077500000000000000000000000001442004423600240115ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk36/src/test/java/org/000077500000000000000000000000001442004423600246005ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk36/src/test/java/org/apache/000077500000000000000000000000001442004423600260215ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk36/src/test/java/org/apache/curator/000077500000000000000000000000001442004423600275005ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk36/src/test/java/org/apache/curator/zk36/000077500000000000000000000000001442004423600302755ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk36/src/test/java/org/apache/curator/zk36/TestIs36.java000066400000000000000000000032601442004423600325250ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.zk36; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.Compatibility; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; public class TestIs36 extends CuratorTestBase { @Test @Tag(zk36Group) public void testIsZk36() { assertTrue(Compatibility.hasGetReachableOrOneMethod()); assertTrue(Compatibility.hasAddrField()); assertTrue(Compatibility.hasPersistentWatchers()); try { Class.forName("org.apache.zookeeper.proto.WhoAmIResponse"); fail("WhoAmIResponse is introduced after ZooKeeper 3.7"); } catch (ClassNotFoundException ignore) {} } @Override protected void createServer() { // NOP } } curator-apache-curator-5.5.0/curator-test-zk36/src/test/resources/000077500000000000000000000000001442004423600251025ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test-zk36/src/test/resources/log4j.properties000066400000000000000000000021071442004423600302370ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. log4j.rootLogger=ERROR, console log4j.logger.org.apache.curator=DEBUG, console log4j.additivity.org.apache.curator=false log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n curator-apache-curator-5.5.0/curator-test/000077500000000000000000000000001442004423600205275ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/LICENSE000066400000000000000000000261361442004423600215440ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. curator-apache-curator-5.5.0/curator-test/NOTICE000066400000000000000000000002501442004423600214300ustar00rootroot00000000000000Apache Curator Copyright 2013-2023 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). curator-apache-curator-5.5.0/curator-test/pom.xml000066400000000000000000000114051442004423600220450ustar00rootroot00000000000000 4.0.0 org.apache.curator apache-curator 5.5.0 curator-test 5.5.0 Curator Testing Unit testing utilities. 2011 org.apache.zookeeper zookeeper io.dropwizard.metrics metrics-core org.xerial.snappy snappy-java com.google.guava guava org.junit.jupiter junit-jupiter-api org.slf4j slf4j-log4j12 test org.apache.maven.plugins maven-shade-plugin apache-curator-guava-shader shade package com.google.guava:guava com.google.guava:listenablefuture com.google.guava:failureaccess com.google org.apache.curator-test.shaded.com.google com.google org.apache.curator-test.shaded.com.google com.google.common.base.Function com.google.common.base.Predicate com.google.common.reflect.TypeToken org.apache.maven.plugins maven-surefire-plugin 1 false true curator-apache-curator-5.5.0/curator-test/src/000077500000000000000000000000001442004423600213165ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/000077500000000000000000000000001442004423600222425ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/000077500000000000000000000000001442004423600231635ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/000077500000000000000000000000001442004423600237525ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/000077500000000000000000000000001442004423600251735ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/000077500000000000000000000000001442004423600266525ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/000077500000000000000000000000001442004423600276315ustar00rootroot00000000000000BaseClassForTests.java000066400000000000000000000125731442004423600337570ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.BindException; import java.util.concurrent.atomic.AtomicBoolean; public class BaseClassForTests { protected volatile TestingServer server; private final Logger log = LoggerFactory.getLogger(getClass()); private final AtomicBoolean isRetrying = new AtomicBoolean(false); private static final String INTERNAL_PROPERTY_DONT_LOG_CONNECTION_ISSUES; private static final String INTERNAL_PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND; private static final String INTERNAL_PROPERTY_VALIDATE_NAMESPACE_WATCHER_MAP_EMPTY; static { String logConnectionIssues = null; try { // use reflection to avoid adding a circular dependency in the pom Class debugUtilsClazz = Class.forName("org.apache.curator.utils.DebugUtils"); logConnectionIssues = (String)debugUtilsClazz.getField("PROPERTY_DONT_LOG_CONNECTION_ISSUES").get(null); } catch ( Exception e ) { e.printStackTrace(); } INTERNAL_PROPERTY_DONT_LOG_CONNECTION_ISSUES = logConnectionIssues; String s = null; try { // use reflection to avoid adding a circular dependency in the pom s = (String)Class.forName("org.apache.curator.utils.DebugUtils").getField("PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND").get(null); } catch ( Exception e ) { e.printStackTrace(); } INTERNAL_PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND = s; s = null; try { // use reflection to avoid adding a circular dependency in the pom s = (String)Class.forName("org.apache.curator.utils.DebugUtils").getField("PROPERTY_VALIDATE_NAMESPACE_WATCHER_MAP_EMPTY").get(null); } catch ( Exception e ) { e.printStackTrace(); } INTERNAL_PROPERTY_VALIDATE_NAMESPACE_WATCHER_MAP_EMPTY = s; } @BeforeEach public void setup() throws Exception { if ( INTERNAL_PROPERTY_DONT_LOG_CONNECTION_ISSUES != null ) { System.setProperty(INTERNAL_PROPERTY_DONT_LOG_CONNECTION_ISSUES, "true"); } System.setProperty(INTERNAL_PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND, "true"); System.setProperty(INTERNAL_PROPERTY_VALIDATE_NAMESPACE_WATCHER_MAP_EMPTY, "true"); try { createServer(); } catch ( FailedServerStartException ignore ) { log.warn("Failed to start server - retrying 1 more time"); // server creation failed - we've sometime seen this with re-used addresses, etc. - retry one more time closeServer(); createServer(); } } public TestingCluster createAndStartCluster(int qty) throws Exception { TestingCluster cluster = new TestingCluster(qty); try { cluster.start(); } catch ( FailedServerStartException e ) { log.warn("Failed to start cluster - retrying 1 more time"); // cluster creation failed - we've sometime seen this with re-used addresses, etc. - retry one more time try { cluster.close(); } catch ( Exception ex ) { // ignore } cluster = new TestingCluster(qty); cluster.start(); } return cluster; } protected void createServer() throws Exception { while ( server == null ) { try { server = new TestingServer(); } catch ( BindException e ) { server = null; throw new FailedServerStartException("Getting bind exception - retrying to allocate server"); } } } @AfterEach public void teardown() throws Exception { System.clearProperty(INTERNAL_PROPERTY_VALIDATE_NAMESPACE_WATCHER_MAP_EMPTY); System.clearProperty(INTERNAL_PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND); closeServer(); } private void closeServer() { if ( server != null ) { try { server.close(); } catch ( IOException e ) { e.printStackTrace(); } finally { server = null; } } } } curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/Compatibility.java000066400000000000000000000100201442004423600332760ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import java.lang.reflect.Method; @SuppressWarnings({"unchecked", "rawtypes"}) public class Compatibility { private static final Method closeAllWithReasonMethod; private static final Method closeAllMethod; private static final Method closeWithReasonMethod; private static final Method closeMethod; private static final Object disconnectReasonObj; static { Object localDisconnectReasonObj; Method localCloseAllWithReasonMethod; Method localCloseAllMethod; Method localCloseWithReasonMethod; Method localCloseMethod; try { Class disconnectReasonClass = Class.forName("org.apache.zookeeper.server.ServerCnxn$DisconnectReason"); localDisconnectReasonObj = Enum.valueOf(disconnectReasonClass, "UNKNOWN"); localCloseAllWithReasonMethod = ServerCnxnFactory.class.getDeclaredMethod("closeAll", disconnectReasonClass); localCloseWithReasonMethod = ServerCnxn.class.getDeclaredMethod("close", disconnectReasonClass); localCloseAllMethod = null; localCloseMethod = null; localCloseAllWithReasonMethod.setAccessible(true); localCloseWithReasonMethod.setAccessible(true); } catch ( Throwable e ) { localDisconnectReasonObj = null; localCloseAllWithReasonMethod = null; localCloseWithReasonMethod = null; try { localCloseAllMethod = ServerCnxnFactory.class.getDeclaredMethod("closeAll"); localCloseMethod = ServerCnxn.class.getDeclaredMethod("close"); localCloseAllMethod.setAccessible(true); localCloseMethod.setAccessible(true); } catch ( Throwable ex ) { throw new IllegalStateException("Could not reflectively find ServerCnxnFactory/ServerCnxn close methods"); } } disconnectReasonObj = localDisconnectReasonObj; closeAllWithReasonMethod = localCloseAllWithReasonMethod; closeAllMethod = localCloseAllMethod; closeMethod = localCloseMethod; closeWithReasonMethod = localCloseWithReasonMethod; } public static void serverCnxnFactoryCloseAll(ServerCnxnFactory factory) { try { if ( closeAllMethod != null ) { closeAllMethod.invoke(factory); } else { closeAllWithReasonMethod.invoke(factory, disconnectReasonObj); } } catch ( Exception e ) { throw new RuntimeException("Could not close factory", e); } } public static void serverCnxnClose(ServerCnxn cnxn) { try { if ( closeMethod != null ) { closeMethod.invoke(cnxn); } else { closeWithReasonMethod.invoke(cnxn, disconnectReasonObj); } } catch ( Exception e ) { throw new RuntimeException("Could not close connection", e); } } } DelegatingExecutorService.java000066400000000000000000000060151442004423600355220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.util.Collection; import java.util.List; import java.util.concurrent.*; public class DelegatingExecutorService implements ExecutorService { private final ExecutorService delegate; public DelegatingExecutorService( ExecutorService delegate ) { this.delegate = delegate; } @Override public void shutdown() { delegate.shutdown(); } @Override public List shutdownNow() { return delegate.shutdownNow(); } @Override public boolean isShutdown() { return delegate.isShutdown(); } @Override public boolean isTerminated() { return delegate.isTerminated(); } @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return delegate.awaitTermination(timeout, unit); } @Override public Future submit(Callable task) { return delegate.submit(task); } @Override public Future submit(Runnable task, T result) { return delegate.submit(task, result); } @Override public Future submit(Runnable task) { return delegate.submit(task); } @Override public List> invokeAll(Collection> tasks) throws InterruptedException { return delegate.invokeAll(tasks); } @Override public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { return delegate.invokeAll(tasks, timeout, unit); } @Override public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { return delegate.invokeAny(tasks); } @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return delegate.invokeAny(tasks, timeout, unit); } @Override public void execute(Runnable command) { delegate.execute(command); } } curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/DirectoryUtils.java000066400000000000000000000046541442004423600334720ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; // copied from Google Guava as these methods are now deprecated // NOTE: removed the line of code documented: Symbolic links will have different canonical and absolute paths // Update May 28, 2017 - change exception into logs public class DirectoryUtils { private static final Logger log = LoggerFactory.getLogger(DirectoryUtils.class); public static File createTempDirectory() { try { final Path tempDirectory = Files.createTempDirectory(DirectoryUtils.class.getSimpleName()); return tempDirectory.toFile(); } catch (final IOException e) { throw new UncheckedIOException(e); } } public static void deleteRecursively(File file) throws IOException { if (file.isDirectory()) { deleteDirectoryContents(file); } if (!file.delete()) { log.error("Failed to delete " + file); } } public static void deleteDirectoryContents(File directory) throws IOException { Preconditions.checkArgument(directory.isDirectory(), "Not a directory: %s", directory); File[] files = directory.listFiles(); if (files == null) { log.warn("directory.listFiles() returned null for: " + directory); return; } for (File file : files) { deleteRecursively(file); } } } ExecuteCalledWatchingExecutorService.java000066400000000000000000000027171442004423600376600ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.util.concurrent.ExecutorService; public class ExecuteCalledWatchingExecutorService extends DelegatingExecutorService { boolean executeCalled = false; public ExecuteCalledWatchingExecutorService(ExecutorService delegate) { super(delegate); } @Override public synchronized void execute(Runnable command) { executeCalled = true; super.execute(command); } public synchronized boolean isExecuteCalled() { return executeCalled; } public synchronized void setExecuteCalled(boolean executeCalled) { this.executeCalled = executeCalled; } } FailedServerStartException.java000066400000000000000000000020751442004423600356710ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; public class FailedServerStartException extends IllegalStateException { public FailedServerStartException(Throwable cause) { super(cause); } public FailedServerStartException(String s) { super(s); } } curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/InstanceSpec.java000066400000000000000000000261641442004423600330640ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.UnknownHostException; import java.util.Collections; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * Abstracts one of the servers in the ensemble */ public class InstanceSpec { private static final AtomicInteger nextServerId = new AtomicInteger(1); private static final String localhost; static { String address = "localhost"; try { // This is a workaround for people using OS X Lion. On Lion when a process tries to connect to a link-local // address it takes 5 seconds to establish the connection for some reason. So instead of using 'localhost' // which could return the link-local address randomly, we'll manually resolve it and look for an address to // return that isn't link-local. If for some reason we can't find an address that isn't link-local then // we'll fall back to the default lof just looking up 'localhost'. for ( InetAddress a : InetAddress.getAllByName("localhost") ) { if ( !a.isLinkLocalAddress() ) { address = a.getHostAddress(); break; } } } catch ( UnknownHostException e ) { // Something went wrong, just default to the existing approach of using 'localhost'. } localhost = address; } private final File dataDirectory; private final int port; private final int electionPort; private final int quorumPort; private final boolean deleteDataDirectoryOnClose; private final int serverId; private final int tickTime; private final int maxClientCnxns; private final Map customProperties; private final String hostname; public static void reset() { nextServerId.set(1); } public static InstanceSpec newInstanceSpec() { return new InstanceSpec(null, -1, -1, -1, true, -1, -1, -1); } public static int getRandomPort() { ServerSocket server = null; try { server = new ServerSocket(0); server.setReuseAddress(true); return server.getLocalPort(); } catch ( IOException e ) { throw new Error(e); } finally { if ( server != null ) { try { server.close(); } catch ( IOException ignore ) { // ignore } } } } /** * @param dataDirectory where to store data/logs/etc. * @param port the port to listen on - each server in the ensemble must use a unique port * @param electionPort the electionPort to listen on - each server in the ensemble must use a unique electionPort * @param quorumPort the quorumPort to listen on - each server in the ensemble must use a unique quorumPort * @param deleteDataDirectoryOnClose if true, the data directory will be deleted when {@link TestingCluster#close()} is called * @param serverId the server ID for the instance */ public InstanceSpec(File dataDirectory, int port, int electionPort, int quorumPort, boolean deleteDataDirectoryOnClose, int serverId) { this(dataDirectory, port, electionPort, quorumPort, deleteDataDirectoryOnClose, serverId, -1, -1, null, null); } /** * @param dataDirectory where to store data/logs/etc. * @param port the port to listen on - each server in the ensemble must use a unique port * @param electionPort the electionPort to listen on - each server in the ensemble must use a unique electionPort * @param quorumPort the quorumPort to listen on - each server in the ensemble must use a unique quorumPort * @param deleteDataDirectoryOnClose if true, the data directory will be deleted when {@link TestingCluster#close()} is called * @param serverId the server ID for the instance * @param tickTime tickTime. Set -1 to used fault server configuration * @param maxClientCnxns max number of client connections from the same IP. Set -1 to use default server configuration */ public InstanceSpec(File dataDirectory, int port, int electionPort, int quorumPort, boolean deleteDataDirectoryOnClose, int serverId, int tickTime, int maxClientCnxns) { this(dataDirectory, port, electionPort, quorumPort, deleteDataDirectoryOnClose, serverId, tickTime, maxClientCnxns, null, null); } /** * @param dataDirectory where to store data/logs/etc. * @param port the port to listen on - each server in the ensemble must use a unique port * @param electionPort the electionPort to listen on - each server in the ensemble must use a unique electionPort * @param quorumPort the quorumPort to listen on - each server in the ensemble must use a unique quorumPort * @param deleteDataDirectoryOnClose if true, the data directory will be deleted when {@link TestingCluster#close()} is called * @param serverId the server ID for the instance * @param tickTime tickTime. Set -1 to used fault server configuration * @param maxClientCnxns max number of client connections from the same IP. Set -1 to use default server configuration * @param customProperties other properties to be passed to the server */ public InstanceSpec(File dataDirectory, int port, int electionPort, int quorumPort, boolean deleteDataDirectoryOnClose, int serverId, int tickTime, int maxClientCnxns, Map customProperties) { this(dataDirectory, port, electionPort, quorumPort, deleteDataDirectoryOnClose, serverId, tickTime, maxClientCnxns, customProperties, null); } /** * @param dataDirectory where to store data/logs/etc. * @param port the port to listen on - each server in the ensemble must use a unique port * @param electionPort the electionPort to listen on - each server in the ensemble must use a unique electionPort * @param quorumPort the quorumPort to listen on - each server in the ensemble must use a unique quorumPort * @param deleteDataDirectoryOnClose if true, the data directory will be deleted when {@link TestingCluster#close()} is called * @param serverId the server ID for the instance * @param tickTime tickTime. Set -1 to used fault server configuration * @param maxClientCnxns max number of client connections from the same IP. Set -1 to use default server configuration * @param customProperties other properties to be passed to the server * @param hostname Hostname or IP if the cluster is intending to be bounded into external interfaces */ public InstanceSpec(File dataDirectory, int port, int electionPort, int quorumPort, boolean deleteDataDirectoryOnClose, int serverId, int tickTime, int maxClientCnxns, Map customProperties,String hostname) { this.dataDirectory = (dataDirectory != null) ? dataDirectory : DirectoryUtils.createTempDirectory(); this.port = (port >= 0) ? port : getRandomPort(); this.electionPort = (electionPort >= 0) ? electionPort : getRandomPort(); this.quorumPort = (quorumPort >= 0) ? quorumPort : getRandomPort(); this.deleteDataDirectoryOnClose = deleteDataDirectoryOnClose; this.serverId = (serverId >= 0) ? serverId : nextServerId.getAndIncrement(); this.tickTime = (tickTime > 0 ? tickTime : -1); // -1 to set default value this.maxClientCnxns = (maxClientCnxns >= 0 ? maxClientCnxns : -1); // -1 to set default value this.customProperties = customProperties != null ? Collections.unmodifiableMap(customProperties) : Collections.emptyMap(); this.hostname = hostname == null ? localhost : hostname; } public int getServerId() { return serverId; } public File getDataDirectory() { return dataDirectory; } public int getPort() { return port; } public int getElectionPort() { return electionPort; } public int getQuorumPort() { return quorumPort; } /** * @deprecated use {@link TestingServer#getConnectString()} or {@link TestingCluster#getConnectString()} instead */ @Deprecated public String getConnectString() { return hostname + ":" + port; } public int getTickTime() { return tickTime; } public int getMaxClientCnxns() { return maxClientCnxns; } public boolean deleteDataDirectoryOnClose() { return deleteDataDirectoryOnClose; } public Map getCustomProperties() { return customProperties; } public String getHostname() { return hostname; } @Override public String toString() { return "InstanceSpec{" + "dataDirectory=" + dataDirectory + ", port=" + port + ", electionPort=" + electionPort + ", quorumPort=" + quorumPort + ", deleteDataDirectoryOnClose=" + deleteDataDirectoryOnClose + ", serverId=" + serverId + ", tickTime=" + tickTime + ", maxClientCnxns=" + maxClientCnxns + ", customProperties=" + customProperties + ", hostname=" + hostname + "} " + super.toString(); } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } InstanceSpec that = (InstanceSpec)o; return hostname.equals(that.getHostname()) && port == that.port; } @Override public int hashCode() { return hostname.hashCode() + port; } } curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/KillSession.java000066400000000000000000000037161442004423600327420ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import org.apache.zookeeper.ZooKeeper; /** *

* Utility to simulate a ZK session dying. *

*/ public class KillSession { /** * Kill the given ZK session * * @param client the client to kill * @since 3.0.0 */ public static void kill(ZooKeeper client) { client.getTestable().injectSessionExpiration(); } /** * Kill the given ZK session * * @param client the client to kill * @param connectString server connection string * @throws Exception errors * @deprecated use {@link #kill(ZooKeeper)} instead */ public static void kill(ZooKeeper client, String connectString) throws Exception { kill(client); } /** * Kill the given ZK session * * @param client the client to kill * @param connectString server connection string * @param maxMs max time ms to wait for kill * @throws Exception errors * @deprecated use {@link #kill(ZooKeeper)} instead */ public static void kill(ZooKeeper client, String connectString, int maxMs) throws Exception { kill(client); } } QuorumConfigBuilder.java000066400000000000000000000134621442004423600343500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.io.Files; import org.apache.curator.test.compatibility.Timing2; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; @SuppressWarnings("UnusedDeclaration") public class QuorumConfigBuilder implements Closeable { private final ImmutableList instanceSpecs; private final boolean fromRandom; private final File fakeConfigFile; public QuorumConfigBuilder(Collection specs) { this(specs.toArray(new InstanceSpec[0])); } public QuorumConfigBuilder(InstanceSpec... specs) { fromRandom = (specs == null) || (specs.length == 0); instanceSpecs = fromRandom ? ImmutableList.of(InstanceSpec.newInstanceSpec()) : ImmutableList.copyOf(specs); File fakeConfigFile = null; try { fakeConfigFile = File.createTempFile("temp", "temp"); } catch ( IOException e ) { Throwables.propagate(e); } this.fakeConfigFile = fakeConfigFile; } public boolean isFromRandom() { return fromRandom; } public QuorumPeerConfig buildConfig() throws Exception { return buildConfig(0); } public InstanceSpec getInstanceSpec(int index) { return instanceSpecs.get(index); } public List getInstanceSpecs() { return instanceSpecs; } public int size() { return instanceSpecs.size(); } @Override public void close() { if ( fakeConfigFile != null ) { //noinspection ResultOfMethodCallIgnored fakeConfigFile.delete(); } } public QuorumPeerConfig buildConfig(int instanceIndex) throws Exception { InstanceSpec spec = instanceSpecs.get(instanceIndex); return buildConfig(instanceIndex, spec.getPort()); } public QuorumPeerConfig buildConfig(int instanceIndex, int instancePort) throws Exception { Properties properties = buildConfigProperties(instanceIndex, instancePort); QuorumPeerConfig config = new QuorumPeerConfig() { { if ( fakeConfigFile != null ) { configFileStr = fakeConfigFile.getPath(); } } }; config.parseProperties(properties); return config; } public Properties buildConfigProperties(int instanceIndex) throws Exception { InstanceSpec spec = instanceSpecs.get(instanceIndex); return buildConfigProperties(instanceIndex, spec.getPort()); } public Properties buildConfigProperties(int instanceIndex, int instancePort) throws Exception { boolean isCluster = (instanceSpecs.size() > 1); InstanceSpec spec = instanceSpecs.get(instanceIndex); if ( isCluster ) { Files.write(Integer.toString(spec.getServerId()).getBytes(), new File(spec.getDataDirectory(), "myid")); } Properties properties = new Properties(); String localSessionsEnabled = System.getProperty("readonlymode.enabled", "false"); properties.setProperty("localSessionsEnabled", localSessionsEnabled); properties.setProperty("localSessionsUpgradingEnabled", localSessionsEnabled); properties.setProperty("initLimit", "10"); properties.setProperty("syncLimit", "5"); properties.setProperty("dataDir", spec.getDataDirectory().getCanonicalPath()); properties.setProperty("clientPort", Integer.toString(instancePort)); String tickTime = Integer.toString((spec.getTickTime() >= 0) ? spec.getTickTime() : new Timing2().tickTime()); properties.setProperty("tickTime", tickTime); properties.setProperty("minSessionTimeout", tickTime); int maxClientCnxns = spec.getMaxClientCnxns(); if ( maxClientCnxns >= 0 ) { properties.setProperty("maxClientCnxns", Integer.toString(maxClientCnxns)); } if ( isCluster ) { for ( InstanceSpec thisSpec : instanceSpecs ) { int clientPort = thisSpec == spec ? instancePort : thisSpec.getPort(); properties.setProperty("server." + thisSpec.getServerId(), String.format("%s:%d:%d;%s:%d", thisSpec.getHostname(), thisSpec.getQuorumPort(), thisSpec.getElectionPort(), thisSpec.getHostname(), clientPort)); } } Map customProperties = spec.getCustomProperties(); if (customProperties != null) { properties.putAll(customProperties); } return properties; } public QuorumPeerConfigBuilder bindInstance(int instanceIndex, int instancePort) { return new QuorumPeerConfigBuilder(this, instanceIndex, instancePort); } } QuorumPeerConfigBuilder.java000066400000000000000000000034421442004423600351610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import java.util.Properties; public class QuorumPeerConfigBuilder { private final QuorumConfigBuilder configBuilder; private final int instanceIndex; private final int instancePort; QuorumPeerConfigBuilder(QuorumConfigBuilder configBuilder, int instanceIndex, int instancePort) { this.configBuilder = configBuilder; this.instanceIndex = instanceIndex; this.instancePort = instancePort; } public boolean isFromRandom() { return configBuilder.isFromRandom(); } public InstanceSpec getInstanceSpec() { return configBuilder.getInstanceSpec(instanceIndex); } public QuorumPeerConfig buildConfig() throws Exception { return configBuilder.buildConfig(instanceIndex, instancePort); } public Properties buildProperties() throws Exception { return configBuilder.buildConfigProperties(instanceIndex, instancePort); } } curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/ServerHelper.java000066400000000000000000000125731442004423600331120ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import org.apache.zookeeper.server.ZooKeeperServer; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.InetSocketAddress; class ServerHelper { private static class ServerCnxnFactoryMethods { private final Constructor constructor; private final Method configureMethod; private final Method startupMethod; private final Method shutdownMethod; private ServerCnxnFactoryMethods(Constructor constructor, Method configureMethod, Method startupMethod, Method shutdownMethod) { this.constructor = constructor; this.configureMethod = configureMethod; this.startupMethod = startupMethod; this.shutdownMethod = shutdownMethod; } } private static class NioServerCnxnMethods { private final Constructor constructor; private final Method startupMethod; private final Method shutdownMethod; private NioServerCnxnMethods(Constructor constructor, Method startupMethod, Method shutdownMethod) { this.constructor = constructor; this.startupMethod = startupMethod; this.shutdownMethod = shutdownMethod; } } private static final ServerCnxnFactoryMethods serverCnxnFactoryMethods; private static final NioServerCnxnMethods nioServerCnxn; static { Class serverCnxnFactoryClass = null; Class nioServerCnxnFactoryClass = null; try { serverCnxnFactoryClass = Class.forName("org.apache.zookeeper.server.NIOServerCnxnFactory"); } catch ( ClassNotFoundException ignore ) { // ignore } try { nioServerCnxnFactoryClass = Class.forName("org.apache.zookeeper.server.NIOServerCnxn$Factory"); } catch ( ClassNotFoundException ignore ) { // ignore } ServerCnxnFactoryMethods localServerCnxnFactoryMethods = null; NioServerCnxnMethods localNioServerCnxn = null; try { if ( serverCnxnFactoryClass != null ) { localServerCnxnFactoryMethods = new ServerCnxnFactoryMethods ( serverCnxnFactoryClass.getConstructor(), serverCnxnFactoryClass.getDeclaredMethod("configure", InetSocketAddress.class, Integer.TYPE), serverCnxnFactoryClass.getDeclaredMethod("startup", ZooKeeperServer.class), serverCnxnFactoryClass.getDeclaredMethod("shutdown") ); } else if ( nioServerCnxnFactoryClass != null ) { localNioServerCnxn = new NioServerCnxnMethods ( nioServerCnxnFactoryClass.getConstructor(InetSocketAddress.class), nioServerCnxnFactoryClass.getDeclaredMethod("startup", ZooKeeperServer.class), nioServerCnxnFactoryClass.getDeclaredMethod("shutdown") ); } } catch ( Exception e ) { e.printStackTrace(); throw new Error(e); } serverCnxnFactoryMethods = localServerCnxnFactoryMethods; nioServerCnxn = localNioServerCnxn; } static Object makeFactory(ZooKeeperServer server, int port) throws Exception { Object factory; if ( nioServerCnxn != null ) { factory = nioServerCnxn.constructor.newInstance(new InetSocketAddress(port)); if ( server != null ) { nioServerCnxn.startupMethod.invoke(factory, server); } } else { factory = serverCnxnFactoryMethods.constructor.newInstance(); serverCnxnFactoryMethods.configureMethod.invoke(factory, new InetSocketAddress(port), 0); if ( server != null ) { serverCnxnFactoryMethods.startupMethod.invoke(factory, server); } } return factory; } static void shutdownFactory(Object factory) { try { if ( nioServerCnxn != null ) { nioServerCnxn.shutdownMethod.invoke(factory); } else { serverCnxnFactoryMethods.shutdownMethod.invoke(factory); } } catch ( Exception e ) { e.printStackTrace(); throw new Error(e); } } private ServerHelper() { } } curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/TestingCluster.java000066400000000000000000000201601442004423600334520ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.zookeeper.ZooKeeper; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.util.Collection; import java.util.List; import java.util.Map; /** * manages an internally running ensemble of ZooKeeper servers. FOR TESTING PURPOSES ONLY */ public class TestingCluster implements Closeable { private final List servers; /** * Creates an ensemble comprised of n servers. Each server will use * a temp directory and random ports * * @param instanceQty number of servers to create in the ensemble */ public TestingCluster(int instanceQty) { this(makeSpecs(instanceQty)); } /** * Creates an ensemble using the given server specs * * @param specs the server specs */ public TestingCluster(InstanceSpec... specs) { this(listToMap(ImmutableList.copyOf(specs))); } /** * Creates an ensemble using the given server specs * * @param specs the server specs */ public TestingCluster(Collection specs) { this(listToMap(specs)); } /** * Creates an ensemble using the given server specs * * @param specs map of an instance spec to its set of quorum instances. Allows simulation of an ensemble with instances * having different config peers */ public TestingCluster(Map> specs) { ImmutableList.Builder serverBuilder = ImmutableList.builder(); for ( Map.Entry> entry : specs.entrySet() ) { List instanceSpecs = Lists.newArrayList(entry.getValue()); int index = instanceSpecs.indexOf(entry.getKey()); Preconditions.checkState(index >= 0, entry.getKey() + " not found in specs"); QuorumConfigBuilder builder = new QuorumConfigBuilder(instanceSpecs); serverBuilder.add(new TestingZooKeeperServer(builder, index)); } servers = serverBuilder.build(); } /** * Returns the set of servers in the ensemble * * @return set of servers */ public Collection getInstances() { Iterable transformed = Iterables.transform ( servers, new Function() { @Override public InstanceSpec apply(TestingZooKeeperServer server) { return server.getInstanceSpec(); } } ); return Lists.newArrayList(transformed); } public List getServers() { return Lists.newArrayList(servers); } /** * Returns the connection string to pass to the ZooKeeper constructor * * @return connection string */ public String getConnectString() { StringBuilder str = new StringBuilder(); for ( InstanceSpec spec : getInstances() ) { if ( str.length() > 0 ) { str.append(","); } str.append(spec.getConnectString()); } return str.toString(); } /** * Start the ensemble. The cluster must be started before use. * * @throws Exception errors */ public void start() throws Exception { for ( TestingZooKeeperServer server : servers ) { server.start(); } } /** * Shutdown the ensemble WITHOUT freeing resources, etc. */ public void stop() throws IOException { for ( TestingZooKeeperServer server : servers ) { server.stop(); } } /** * Shutdown the ensemble, free resources, etc. If temp directories were used, they * are deleted. You should call this in a finally block. * * @throws IOException errors */ @Override public void close() throws IOException { for ( TestingZooKeeperServer server : servers ) { server.close(); } } /** * Kills the given server. This simulates the server unexpectedly crashing * * @param instance server to kill * @return true if the instance was found * @throws Exception errors */ public boolean killServer(InstanceSpec instance) throws Exception { for ( TestingZooKeeperServer server : servers ) { if ( server.getInstanceSpec().equals(instance) ) { server.kill(); return true; } } return false; } /** * Restart the given server of the cluster * * @param instance server instance * @return true of the server was found * @throws Exception errors */ public boolean restartServer(InstanceSpec instance) throws Exception { for ( TestingZooKeeperServer server : servers ) { if ( server.getInstanceSpec().equals(instance) ) { server.restart(); return true; } } return false; } /** * Given a ZooKeeper instance, returns which server it is connected to * * @param client ZK instance * @return the server * @throws Exception errors */ public InstanceSpec findConnectionInstance(ZooKeeper client) throws Exception { Method m = ZooKeeper.class.getDeclaredMethod("testableRemoteSocketAddress"); m.setAccessible(true); InetSocketAddress address = (InetSocketAddress)m.invoke(client); if ( address != null ) { for ( TestingZooKeeperServer server : servers ) { if ( server.getInstanceSpec().getPort() == address.getPort() ) { return server.getInstanceSpec(); } } } return null; } public static Map> makeSpecs(int instanceQty) { return makeSpecs(instanceQty, true); } public static Map> makeSpecs(int instanceQty, boolean resetServerIds) { if ( resetServerIds ) { InstanceSpec.reset(); } ImmutableList.Builder builder = ImmutableList.builder(); for ( int i = 0; i < instanceQty; ++i ) { builder.add(InstanceSpec.newInstanceSpec()); } return listToMap(builder.build()); } private static Map> listToMap(Collection list) { ImmutableMap.Builder> mapBuilder = ImmutableMap.builder(); for ( InstanceSpec spec : list ) { mapBuilder.put(spec, list); } return mapBuilder.build(); } } TestingQuorumPeerMain.java000066400000000000000000000067451442004423600347000ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.lang.reflect.Field; import java.nio.channels.ServerSocketChannel; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerMain; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class TestingQuorumPeerMain extends QuorumPeerMain implements ZooKeeperMainFace { private static final Logger log = LoggerFactory.getLogger(TestingQuorumPeerMain.class); private volatile boolean isClosed = false; @Override public void kill() { try { if ( quorumPeer != null ) { Field cnxnFactoryField = QuorumPeer.class.getDeclaredField("cnxnFactory"); cnxnFactoryField.setAccessible(true); ServerCnxnFactory cnxnFactory = (ServerCnxnFactory)cnxnFactoryField.get(quorumPeer); Compatibility.serverCnxnFactoryCloseAll(cnxnFactory); Field ssField = cnxnFactory.getClass().getDeclaredField("ss"); ssField.setAccessible(true); ServerSocketChannel ss = (ServerSocketChannel)ssField.get(cnxnFactory); ss.close(); } close(); } catch ( Exception e ) { e.printStackTrace(); } } @Override public void close() { if ( (quorumPeer != null) && !isClosed ) { isClosed = true; quorumPeer.shutdown(); } } private void blockUntilStarted() { long startTime = System.currentTimeMillis(); while ( (quorumPeer == null) && ((System.currentTimeMillis() - startTime) <= TestingZooKeeperMain.MAX_WAIT_MS) ) { try { Thread.sleep(10); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); break; } } if ( quorumPeer == null ) { throw new FailedServerStartException("quorumPeer never got set"); } } @Override public void start(QuorumPeerConfigBuilder configBuilder) { new Thread(() -> { try { runFromConfig(configBuilder.buildConfig()); } catch (Exception e) { log.error("From testing server (random state: {}) for instance: {}", configBuilder.isFromRandom(), configBuilder.getInstanceSpec(), e); } }).start(); blockUntilStarted(); } @Override public int getClientPort() { return quorumPeer == null ? -1 : quorumPeer.getClientPort(); } } curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/TestingServer.java000066400000000000000000000123361442004423600333050ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.io.Closeable; import java.io.File; import java.io.IOException; /** * manages an internally running ZooKeeper server. FOR TESTING PURPOSES ONLY */ public class TestingServer implements Closeable { private final TestingZooKeeperServer testingZooKeeperServer; private final InstanceSpec spec; TestingZooKeeperServer getTestingZooKeeperServer() { return testingZooKeeperServer; } /** * Create the server using a random port * * @throws Exception errors */ public TestingServer() throws Exception { this(-1, null, true); } /** * Create the server using a random port * * @param start True if the server should be started, false otherwise * @throws Exception errors */ public TestingServer(boolean start) throws Exception { this(-1, null, start); } /** * Create and start the server using the given port * * @param port the port * @throws Exception errors */ public TestingServer(int port) throws Exception { this(port, null, true); } /** * Create the server using the given port * * @param port the port * @param start True if the server should be started, false otherwise * @throws Exception errors */ public TestingServer(int port, boolean start) throws Exception { this(port, null, start); } /** * Create and start the server using the given port * * @param port the port * @param tempDirectory directory to use * @throws Exception errors */ public TestingServer(int port, File tempDirectory) throws Exception { this(port, tempDirectory, true); } /** * Create the server using the given port * * @param port the port * @param tempDirectory directory to use * @param start True if the server should be started, false otherwise * @throws Exception errors */ public TestingServer(int port, File tempDirectory, boolean start) throws Exception { this(new InstanceSpec(tempDirectory, Math.max(0, port), -1, -1, true, -1), start); } /** * Create the server using the given port * * @param spec instance details * @param start True if the server should be started, false otherwise * @throws Exception errors */ public TestingServer(InstanceSpec spec, boolean start) throws Exception { this.spec = spec; testingZooKeeperServer = new TestingZooKeeperServer(new QuorumConfigBuilder(spec)); if ( start ) { testingZooKeeperServer.start(); } } /** * Return the port being used or will be used. * * @return port * @throws IllegalStateException if server is configured to bind to port 0 but not started */ public int getPort() { int port = spec.getPort(); if (port > 0) { return port; } return testingZooKeeperServer.getLocalPort(); } /** * Returns the temp directory being used * * @return directory */ public File getTempDirectory() { return spec.getDataDirectory(); } /** * Start the server * * @throws Exception */ public void start() throws Exception { testingZooKeeperServer.start(); } /** * Stop the server without deleting the temp directory */ public void stop() throws IOException { testingZooKeeperServer.stop(); } /** * Restart the server. If the server is currently running it will be stopped * and restarted. If it's not currently running then it will be started. If * it has been closed (had close() called on it) then an exception will be * thrown. * * @throws Exception */ public void restart() throws Exception { testingZooKeeperServer.restart(); } /** * Close the server and any open clients and delete the temp directory */ @Override public void close() throws IOException { testingZooKeeperServer.close(); } /** * Returns the connection string to use * * @return connection string * @throws IllegalStateException if server is configured to bind to port 0 but not started */ public String getConnectString() { return spec.getHostname() + ":" + getPort(); } }TestingZooKeeperMain.java000066400000000000000000000255421442004423600344730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.io.IOException; import java.lang.reflect.Field; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.channels.ServerSocketChannel; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.jmx.ZKMBeanInfo; import org.apache.zookeeper.server.ContainerManager; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ServerConfig; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestingZooKeeperMain implements ZooKeeperMainFace { private static final Logger log = LoggerFactory.getLogger(TestingZooKeeperMain.class); private final CountDownLatch latch = new CountDownLatch(1); private final AtomicReference startingException = new AtomicReference<>(null); private volatile ServerCnxnFactory cnxnFactory; private volatile TestZooKeeperServer zkServer; private volatile ContainerManager containerManager; private static final Timing timing = new Timing(); static final int MAX_WAIT_MS; static { long startMs = System.currentTimeMillis(); try { // this can take forever and fails tests - ZK calls it internally so there's nothing we can do // pre flight it and use it to calculate max wait //noinspection ResultOfMethodCallIgnored InetAddress.getLocalHost().getCanonicalHostName(); } catch ( UnknownHostException e ) { // ignore } long elapsed = System.currentTimeMillis() - startMs; MAX_WAIT_MS = Math.max((int)elapsed * 2, 1000); } @Override public void kill() { try { if ( cnxnFactory != null ) { Compatibility.serverCnxnFactoryCloseAll(cnxnFactory); Field ssField = cnxnFactory.getClass().getDeclaredField("ss"); ssField.setAccessible(true); ServerSocketChannel ss = (ServerSocketChannel)ssField.get(cnxnFactory); ss.close(); } close(); } catch ( Exception e ) { e.printStackTrace(); // just ignore - this class is only for testing } } TestZooKeeperServer getZkServer() { return zkServer; } private void runFromConfig(QuorumPeerConfig config) throws Exception { try { Field instance = MBeanRegistry.class.getDeclaredField("instance"); instance.setAccessible(true); MBeanRegistry nopMBeanRegistry = new MBeanRegistry() { @Override public void register(ZKMBeanInfo bean, ZKMBeanInfo parent) { // NOP } @Override public void unregister(ZKMBeanInfo bean) { // NOP } }; instance.set(null, nopMBeanRegistry); } catch ( Exception e ) { log.error("Could not fix MBeanRegistry"); } ServerConfig serverConfig = new ServerConfig(); serverConfig.readFrom(config); try { internalRunFromConfig(serverConfig); } catch ( IOException e ) { startingException.set(e); throw e; } } private void blockUntilStarted() { if (!timing.awaitLatch(latch)) { throw new FailedServerStartException("Timed out waiting for server startup"); } if ( zkServer != null ) { //noinspection SynchronizeOnNonFinalField synchronized(zkServer) { while ( !zkServer.isRunning() ) { try { zkServer.wait(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); throw new FailedServerStartException("Server start interrupted"); } } } } else { throw new FailedServerStartException("No zkServer."); } Exception exception = startingException.get(); if ( exception != null ) { throw new FailedServerStartException(exception); } } @Override public void close() throws IOException { try { cnxnFactory.shutdown(); } catch ( Throwable e ) { e.printStackTrace(); // just ignore - this class is only for testing } finally { cnxnFactory = null; } if ( containerManager != null ) { containerManager.stop(); containerManager = null; } try { if ( zkServer != null ) { zkServer.shutdown(); ZKDatabase zkDb = zkServer.getZKDatabase(); if ( zkDb != null ) { // make ZK server close its log files zkDb.close(); } } } catch ( Throwable e ) { e.printStackTrace(); // just ignore - this class is only for testing } finally { zkServer = null; } } // copied from ZooKeeperServerMain.java private void internalRunFromConfig(ServerConfig config) throws IOException { log.info("Starting server"); try { // Note that this thread isn't going to be doing anything else, // so rather than spawning another thread, we will just call // run() in this thread. // create a file logger url from the command line args FileTxnSnapLog txnLog = new FileTxnSnapLog(config.getDataLogDir(), config.getDataDir()); zkServer = new TestZooKeeperServer(txnLog, config); try { cnxnFactory = ServerCnxnFactory.createFactory(); cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns()); } catch ( IOException e ) { log.info("Could not start server. Waiting and trying one more time.", e); timing.sleepABit(); cnxnFactory = ServerCnxnFactory.createFactory(); cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns()); } cnxnFactory.startup(zkServer); containerManager = new ContainerManager(zkServer.getZKDatabase(), zkServer.getFirstProcessor(), Integer.getInteger("znode.container.checkIntervalMs", (int)TimeUnit.MINUTES.toMillis(1L)), Integer.getInteger("znode.container.maxPerMinute", 10000)); containerManager.start(); latch.countDown(); cnxnFactory.join(); if ( (zkServer != null) && zkServer.isRunning()) { zkServer.shutdown(); } } catch (InterruptedException e) { // warn, but generally this is ok Thread.currentThread().interrupt(); log.warn("Server interrupted", e); } } @Override public void start(QuorumPeerConfigBuilder configBuilder) { new Thread(() -> { try { runFromConfig(configBuilder.buildConfig()); } catch ( Exception e ) { log.error(String.format("From testing server (random state: %s) for instance: %s", configBuilder.isFromRandom(), configBuilder.getInstanceSpec()), e); } }, "zk-main-thread").start(); blockUntilStarted(); } @Override public int getClientPort() { return cnxnFactory == null ? -1 : cnxnFactory.getLocalPort(); } public static class TestZooKeeperServer extends ZooKeeperServer { private final FileTxnSnapLog txnLog; public TestZooKeeperServer(FileTxnSnapLog txnLog, ServerConfig config) { this.txnLog = txnLog; this.setTxnLogFactory(txnLog); // tickTime would affect min and max session timeout: should be set first this.setTickTime(config.getTickTime()); this.setMinSessionTimeout(config.getMinSessionTimeout()); this.setMaxSessionTimeout(config.getMaxSessionTimeout()); } @Override public synchronized void shutdown(boolean fullyShutDown) { super.shutdown(fullyShutDown); try { txnLog.close(); } catch ( IOException e ) { // ignore } } private final AtomicBoolean isRunning = new AtomicBoolean(false); public RequestProcessor getFirstProcessor() { return firstProcessor; } @Override protected void setState(State state) { this.state = state; // avoid ZKShutdownHandler is not registered message } protected void registerJMX() { // NOP } @Override protected void unregisterJMX() { // NOP } @Override public boolean isRunning() { return isRunning.get() || super.isRunning(); } public void noteStartup() { synchronized (this) { isRunning.set(true); this.notifyAll(); } } } } TestingZooKeeperServer.java000066400000000000000000000125121442004423600350460ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.io.Closeable; import java.io.IOException; import java.util.Collection; import java.util.concurrent.atomic.AtomicReference; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Thanks to Jeremie BORDIER (ahfeel) for this code */ public class TestingZooKeeperServer implements Closeable { private static final Logger log = LoggerFactory.getLogger(TestingZooKeeperServer.class); static boolean hasZooKeeperServerEmbedded; private final AtomicReference state = new AtomicReference<>(State.LATENT); private final QuorumConfigBuilder configBuilder; private final int thisInstanceIndex; private int thisInstancePort; private volatile ZooKeeperMainFace main; static { boolean localHasZooKeeperServerEmbedded; try { Class.forName("org.apache.zookeeper.server.embedded.ZooKeeperServerEmbedded"); localHasZooKeeperServerEmbedded = true; } catch (Throwable t) { localHasZooKeeperServerEmbedded = false; log.info("ZooKeeperServerEmbedded is not available in the version of the ZooKeeper library being used"); } hasZooKeeperServerEmbedded = localHasZooKeeperServerEmbedded; } ZooKeeperMainFace getMain() { return main; } private enum State { LATENT, STARTED, STOPPED, CLOSED } public TestingZooKeeperServer(QuorumConfigBuilder configBuilder) { this(configBuilder, 0); } public TestingZooKeeperServer(QuorumConfigBuilder configBuilder, int thisInstanceIndex) { System.setProperty("zookeeper.jmx.log4j.disable", "true"); // disable JMX logging this.configBuilder = configBuilder; this.thisInstanceIndex = thisInstanceIndex; this.thisInstancePort = configBuilder.getInstanceSpec(thisInstanceIndex).getPort(); main = createServerMain(); } private ZooKeeperMainFace createServerMain() { if (hasZooKeeperServerEmbedded) { return new ZooKeeperServerEmbeddedAdapter(); } else if (isCluster()) { return new TestingQuorumPeerMain(); } else { return new TestingZooKeeperMain(); } } private boolean isCluster() { return configBuilder.size() > 1; } public Collection getInstanceSpecs() { return configBuilder.getInstanceSpecs(); } public void kill() { main.kill(); state.set(State.STOPPED); } /** * Restart the server. If the server is running it will be stopped and then * started again. If it is not running (in a LATENT or STOPPED state) then * it will be restarted. If it is in a CLOSED state then an exception will * be thrown. */ public void restart() throws Exception { // Can't restart from a closed state as all the temporary data is gone if ( state.get() == State.CLOSED ) { throw new IllegalStateException("Cannot restart a closed instance"); } // If the server's currently running then stop it. if ( state.get() == State.STARTED ) { stop(); } // Set to a LATENT state so we can restart state.set(State.LATENT); main = createServerMain(); start(); } public void stop() throws IOException { if ( state.compareAndSet(State.STARTED, State.STOPPED) ) { main.close(); } } public InstanceSpec getInstanceSpec() { return configBuilder.getInstanceSpec(thisInstanceIndex); } @Override public void close() throws IOException { stop(); if ( state.compareAndSet(State.STOPPED, State.CLOSED) ) { configBuilder.close(); InstanceSpec spec = getInstanceSpec(); if ( spec.deleteDataDirectoryOnClose() ) { DirectoryUtils.deleteRecursively(spec.getDataDirectory()); } } } public void start() throws Exception { if ( !state.compareAndSet(State.LATENT, State.STARTED) ) { return; } main.start(configBuilder.bindInstance(thisInstanceIndex, thisInstancePort)); thisInstancePort = main.getClientPort(); } public int getLocalPort() { if (thisInstancePort == 0) { throw new IllegalStateException("server is configured to bind to port 0 but not started"); } return thisInstancePort; } }curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/Timing.java000066400000000000000000000131341442004423600317250ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * Utility to get various testing times */ public class Timing { private final long value; private final TimeUnit unit; private final int waitingMultiple; private static final int DEFAULT_SECONDS = 10; private static final int DEFAULT_WAITING_MULTIPLE = 5; private static final double SESSION_MULTIPLE = .25; /** * Use the default base time */ public Timing() { this(Integer.getInteger("timing-multiple", 1), getWaitingMultiple()); } /** * Use a multiple of the default base time * * @param multiple the multiple */ public Timing(double multiple) { this((long)(DEFAULT_SECONDS * multiple), TimeUnit.SECONDS, getWaitingMultiple()); } /** * Use a multiple of the default base time * * @param multiple the multiple * @param waitingMultiple multiple of main timing to use when waiting */ public Timing(double multiple, int waitingMultiple) { this((long)(DEFAULT_SECONDS * multiple), TimeUnit.SECONDS, waitingMultiple); } /** * @param value base time * @param unit base time unit */ public Timing(long value, TimeUnit unit) { this(value, unit, getWaitingMultiple()); } /** * @param value base time * @param unit base time unit * @param waitingMultiple multiple of main timing to use when waiting */ public Timing(long value, TimeUnit unit, int waitingMultiple) { this.value = value; this.unit = unit; this.waitingMultiple = waitingMultiple; } /** * Return the base time in milliseconds * * @return time ms */ public int milliseconds() { return (int)TimeUnit.MILLISECONDS.convert(value, unit); } /** * Return the base time in seconds * * @return time secs */ public int seconds() { return (int)value; } /** * Wait on the given latch * * @param latch latch to wait on * @return result of {@link CountDownLatch#await(long, TimeUnit)} */ public boolean awaitLatch(CountDownLatch latch) { Timing m = forWaiting(); try { return latch.await(m.value, m.unit); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } return false; } /** * Wait on the given semaphore * * @param semaphore the semaphore * @return result of {@link Semaphore#tryAcquire()} */ public boolean acquireSemaphore(Semaphore semaphore) { Timing m = forWaiting(); try { return semaphore.tryAcquire(m.value, m.unit); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } return false; } /** * Wait on the given semaphore * * @param semaphore the semaphore * @param n number of permits to acquire * @return result of {@link Semaphore#tryAcquire(int, long, TimeUnit)} */ public boolean acquireSemaphore(Semaphore semaphore, int n) { Timing m = forWaiting(); try { return semaphore.tryAcquire(n, m.value, m.unit); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } return false; } /** * Return a new timing that is a multiple of the this timing * * @param n the multiple * @return this timing times the multiple */ public Timing multiple(double n) { return new Timing((int)(value * n), unit); } /** * Return a new timing with the standard multiple for waiting on latches, etc. * * @return this timing multiplied */ @SuppressWarnings("PointlessArithmeticExpression") public Timing forWaiting() { return multiple(waitingMultiple); } /** * Sleep for a small amount of time * * @throws InterruptedException if interrupted */ public void sleepABit() throws InterruptedException { unit.sleep(value / 4); } /** * Return the value to use for ZK session timeout * * @return session timeout */ public int session() { return multiple(SESSION_MULTIPLE).milliseconds(); } /** * Return the value to use for ZK connection timeout * * @return connection timeout */ public int connection() { return milliseconds(); } private static Integer getWaitingMultiple() { return Integer.getInteger("timing-waiting-multiple", DEFAULT_WAITING_MULTIPLE); } } curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/WatchersDebug.java000066400000000000000000000053401442004423600332250ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import org.apache.zookeeper.ZooKeeper; import java.lang.reflect.Method; import java.util.List; public class WatchersDebug { private static final Method getDataWatches; private static final Method getExistWatches; private static final Method getChildWatches; static { Method localGetDataWatches = null; Method localGetExistWatches = null; Method localGetChildWatches = null; try { localGetDataWatches = getMethod("getDataWatches"); localGetExistWatches = getMethod("getExistWatches"); localGetChildWatches = getMethod("getChildWatches"); } catch ( NoSuchMethodException e ) { e.printStackTrace(); } getDataWatches = localGetDataWatches; getExistWatches = localGetExistWatches; getChildWatches = localGetChildWatches; } public static List getDataWatches(ZooKeeper zooKeeper) { return callMethod(zooKeeper, WatchersDebug.getDataWatches); } public static List getExistWatches(ZooKeeper zooKeeper) { return callMethod(zooKeeper, getExistWatches); } public static List getChildWatches(ZooKeeper zooKeeper) { return callMethod(zooKeeper, getChildWatches); } private WatchersDebug() { } private static Method getMethod(String name) throws NoSuchMethodException { Method m = ZooKeeper.class.getDeclaredMethod(name); m.setAccessible(true); return m; } private static List callMethod(ZooKeeper zooKeeper, Method method) { if ( zooKeeper == null ) { return null; } try { //noinspection unchecked return (List)method.invoke(zooKeeper); } catch ( Exception e ) { throw new RuntimeException(e); } } } ZooKeeperMainFace.java000066400000000000000000000020141442004423600337010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.io.Closeable; interface ZooKeeperMainFace extends Closeable { void start(QuorumPeerConfigBuilder configBuilder); void kill(); int getClientPort() throws Exception; } ZooKeeperServerEmbeddedAdapter.java000066400000000000000000000146231442004423600364300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.Properties; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServerMain; import org.apache.zookeeper.server.embedded.ZooKeeperServerEmbedded; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.QuorumPeerMain; import org.apache.zookeeper.server.util.ConfigUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZooKeeperServerEmbeddedAdapter implements ZooKeeperMainFace { private static final Logger log = LoggerFactory.getLogger(ZooKeeperServerEmbeddedAdapter.class); private static final Duration DEFAULT_STARTUP_TIMEOUT = Duration.ofMinutes(1); private volatile ZooKeeperServerEmbedded zooKeeperEmbedded; @Override public void start(QuorumPeerConfigBuilder configBuilder) { try { final Properties properties = configBuilder.buildProperties(); properties.put("admin.enableServer", "false"); final Path dataDir = Paths.get(properties.getProperty("dataDir")); zooKeeperEmbedded = ZooKeeperServerEmbedded.builder() .configuration(properties) .baseDir(dataDir.getParent()) .build(); log.info("Configure ZooKeeperServerEmbeddedAdapter with properties: {}", properties); // Before ZOOKEEPER-4303, there are issues when setting "clientPort" to 0: // * It does not set "clientPortAddress" which causes ZooKeeper started with no // server cnxn factory to serve client requests. // * It uses "clientPortAddress" to construct connection string but not bound port. // // So here, we hijack start process to circumvent these if there is no fix applied. // * Setup "clientPortAddress" if it is null. // * Setup "clientPortAddress" with bound port after started if above step applied. if (hijackClientPort(0)) { zooKeeperEmbedded.start(DEFAULT_STARTUP_TIMEOUT.toMillis()); int port = getServerCnxnFactory().getLocalPort(); hijackClientPort(port); } else { zooKeeperEmbedded.start(DEFAULT_STARTUP_TIMEOUT.toMillis()); } } catch (Exception e) { throw new FailedServerStartException(e); } } @Override public int getClientPort() throws Exception { String address = zooKeeperEmbedded.getConnectionString(); try { String[] parts = ConfigUtils.getHostAndPort(address); return Integer.parseInt(parts[1], 10); } catch (Exception ex) { throw new IllegalStateException("invalid connection string: " + address); } } private boolean hijackClientPort(int port) { try { Class clazz = Class.forName("org.apache.zookeeper.server.embedded.ZooKeeperServerEmbeddedImpl"); Field configField = clazz.getDeclaredField("config"); configField.setAccessible(true); QuorumPeerConfig peerConfig = (QuorumPeerConfig) configField.get(zooKeeperEmbedded); if (peerConfig.getClientPortAddress() == null || port != 0) { Field addressField = QuorumPeerConfig.class.getDeclaredField("clientPortAddress"); addressField.setAccessible(true); addressField.set(peerConfig, new InetSocketAddress(port)); return true; } } catch (Exception ignored) { // swallow hijack failure to accommodate possible upstream changes } return false; } public ServerCnxnFactory getServerCnxnFactory() { try { Class clazz = Class.forName("org.apache.zookeeper.server.embedded.ZooKeeperServerEmbeddedImpl"); Field clusterField = clazz.getDeclaredField("maincluster"); clusterField.setAccessible(true); QuorumPeerMain quorumPeerMain = (QuorumPeerMain) clusterField.get(zooKeeperEmbedded); if (quorumPeerMain != null) { Field quorumPeerField = QuorumPeerMain.class.getDeclaredField("quorumPeer"); quorumPeerField.setAccessible(true); QuorumPeer quorumPeer = (QuorumPeer) quorumPeerField.get(quorumPeerMain); return getServerCnxnFactory(QuorumPeer.class, quorumPeer, "cnxnFactory"); } Field serverField = clazz.getDeclaredField("mainsingle"); serverField.setAccessible(true); ZooKeeperServerMain server = (ZooKeeperServerMain) serverField.get(zooKeeperEmbedded); return getServerCnxnFactory(ZooKeeperServerMain.class, server, "cnxnFactory"); } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException ex) { throw new IllegalStateException("zk server cnxn factory not found", ex); } } static ServerCnxnFactory getServerCnxnFactory(Class clazz, Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException { Field cnxnFactoryField = clazz.getDeclaredField(fieldName); cnxnFactoryField.setAccessible(true); return (ServerCnxnFactory) cnxnFactoryField.get(obj); } @Override public void kill() { close(); } @Override public void close() { if (zooKeeperEmbedded != null) { zooKeeperEmbedded.close(); } } } curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/compatibility/000077500000000000000000000000001442004423600325025ustar00rootroot00000000000000CuratorTestBase.java000066400000000000000000000022671442004423600363470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/compatibility/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test.compatibility; import org.apache.curator.test.BaseClassForTests; public class CuratorTestBase extends BaseClassForTests { public static final String zk36Group = "zk36"; public static final String zk37Group = "zk37"; public static final String zk35TestCompatibilityGroup = "zk35TestCompatibility"; protected final Timing2 timing = new Timing2(); } Timing2.java000066400000000000000000000200041442004423600345730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/main/java/org/apache/curator/test/compatibility/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test.compatibility; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Utility to get various testing times. * * Copied from the old Timing class which is now deprecated. Needed this to support ZK 3.4 compatibility */ public class Timing2 { private final long value; private final TimeUnit unit; private final int waitingMultiple; private static final double TICK_TIME_MULTIPLE = .10; private static final int DEFAULT_SECONDS = 10; private static final int DEFAULT_WAITING_MULTIPLE = 5; private static final double SESSION_MULTIPLE = 1.5; private static final double SESSION_SLEEP_MULTIPLE = SESSION_MULTIPLE * 1.75; // has to be at least session + 2/3 of a session to account for missed heartbeat then session expiration /** * Use the default base time */ public Timing2() { this(Integer.getInteger("timing-multiple", 1), getWaitingMultiple()); } /** * Use a multiple of the default base time * * @param multiple the multiple */ public Timing2(double multiple) { this((long)(DEFAULT_SECONDS * multiple), TimeUnit.SECONDS, getWaitingMultiple()); } /** * Use a multiple of the default base time * * @param multiple the multiple * @param waitingMultiple multiple of main timing to use when waiting */ public Timing2(double multiple, int waitingMultiple) { this((long)(DEFAULT_SECONDS * multiple), TimeUnit.SECONDS, waitingMultiple); } /** * @param value base time * @param unit base time unit */ public Timing2(long value, TimeUnit unit) { this(value, unit, getWaitingMultiple()); } /** * @param value base time * @param unit base time unit * @param waitingMultiple multiple of main timing to use when waiting */ public Timing2(long value, TimeUnit unit, int waitingMultiple) { this.value = value; this.unit = unit; this.waitingMultiple = waitingMultiple; } /** * Return the base time in milliseconds * * @return time ms */ public int milliseconds() { return (int)TimeUnit.MILLISECONDS.convert(value, unit); } /** * Return the base time in seconds * * @return time secs */ public int seconds() { return (int)value; } /** * Wait on the given latch * * @param latch latch to wait on * @return result of {@link java.util.concurrent.CountDownLatch#await(long, java.util.concurrent.TimeUnit)} */ public boolean awaitLatch(CountDownLatch latch) { Timing2 m = forWaiting(); try { return latch.await(m.value, m.unit); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } return false; } /** * Try to take an item from the given queue * * @param queue queue * @return item * @throws Exception interrupted or timed out */ public T takeFromQueue(BlockingQueue queue) throws Exception { Timing2 m = forWaiting(); try { T value = queue.poll(m.value, m.unit); if ( value == null ) { throw new TimeoutException("Timed out trying to take from queue"); } return value; } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); throw e; } } /** * Wait on the given semaphore * * @param semaphore the semaphore * @return result of {@link java.util.concurrent.Semaphore#tryAcquire()} */ public boolean acquireSemaphore(Semaphore semaphore) { Timing2 m = forWaiting(); try { return semaphore.tryAcquire(m.value, m.unit); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } return false; } /** * Wait on the given semaphore * * @param semaphore the semaphore * @param n number of permits to acquire * @return result of {@link java.util.concurrent.Semaphore#tryAcquire(int, long, java.util.concurrent.TimeUnit)} */ public boolean acquireSemaphore(Semaphore semaphore, int n) { Timing2 m = forWaiting(); try { return semaphore.tryAcquire(n, m.value, m.unit); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } return false; } /** * Return a new timing that is a multiple of the this timing * * @param n the multiple * @return this timing times the multiple */ public Timing2 multiple(double n) { return new Timing2((int)(value * n), unit); } /** * Return a new timing that is a multiple of the this timing * * @param n the multiple * @param waitingMultiple new waitingMultiple * @return this timing times the multiple */ public Timing2 multiple(double n, int waitingMultiple) { return new Timing2((int)(value * n), unit, waitingMultiple); } /** * Return a new timing with the standard multiple for waiting on latches, etc. * * @return this timing multiplied */ @SuppressWarnings("PointlessArithmeticExpression") public Timing2 forWaiting() { return multiple(waitingMultiple); } /** * Return a new timing with a multiple that ensures a ZK session timeout * * @return this timing multiplied */ public Timing2 forSessionSleep() { return multiple(SESSION_SLEEP_MULTIPLE, 1); } /** * Return a new timing with a multiple for sleeping a smaller amount of time * * @return this timing multiplied */ public Timing2 forSleepingABit() { return multiple(.25); } /** * Sleep for a small amount of time * * @throws InterruptedException if interrupted */ public void sleepABit() throws InterruptedException { forSleepingABit().sleep(); } /** * Sleep for a the full amount of time * * @throws InterruptedException if interrupted */ public void sleep() throws InterruptedException { unit.sleep(value); } /** * Return the value to use for ZK session timeout * * @return session timeout */ public int session() { return multiple(SESSION_MULTIPLE).milliseconds(); } /** * Return the value to use for ZK connection timeout * * @return connection timeout */ public int connection() { return milliseconds(); } /** * Value to use for server "tickTime" * * @return tick time */ public int tickTime() { return (int)Math.max(1, milliseconds() * TICK_TIME_MULTIPLE); } private static Integer getWaitingMultiple() { return Integer.getInteger("timing-waiting-multiple", DEFAULT_WAITING_MULTIPLE); } } curator-apache-curator-5.5.0/curator-test/src/site/000077500000000000000000000000001442004423600222625ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/site/confluence/000077500000000000000000000000001442004423600244035ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/site/confluence/index.confluence000066400000000000000000000007021442004423600275540ustar00rootroot00000000000000h1. Testing Utilities h2. Test Server In the curator\-test sub\-model the {{TestingServer}} class is provided. This class creates a local, in\-process ZooKeeper server that can be used for testing. h2. Test Cluster In the curator\-test sub\-model the {{TestingCluster}} class is provided. This class creates an internally running ensemble of ZooKeeper servers. h2. Usage Look at the various Curator unit tests to see how these utilities are used. curator-apache-curator-5.5.0/curator-test/src/site/site.xml000066400000000000000000000023421442004423600237510ustar00rootroot00000000000000 ]]> curator-apache-curator-5.5.0/curator-test/src/test/000077500000000000000000000000001442004423600222755ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/test/java/000077500000000000000000000000001442004423600232165ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/test/java/org/000077500000000000000000000000001442004423600240055ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/test/java/org/apache/000077500000000000000000000000001442004423600252265ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/test/java/org/apache/curator/000077500000000000000000000000001442004423600267055ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/test/java/org/apache/curator/test/000077500000000000000000000000001442004423600276645ustar00rootroot00000000000000TestQuorumConfigBuilder.java000066400000000000000000000037741442004423600352500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/test/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; /** * Test QuorumConfigBuilder */ public class TestQuorumConfigBuilder { @Test public void testCustomProperties() throws Exception { Map customProperties = new HashMap(); customProperties.put("authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); customProperties.put("kerberos.removeHostFromPrincipal", "true"); customProperties.put("kerberos.removeRealmFromPrincipal", "true"); InstanceSpec spec = new InstanceSpec(null, -1, -1, -1, true, 1,-1, -1,customProperties); TestingServer server = new TestingServer(spec, true); try { assertEquals("org.apache.zookeeper.server.auth.SASLAuthenticationProvider", System.getProperty("zookeeper.authProvider.1")); assertEquals("true", System.getProperty("zookeeper.kerberos.removeHostFromPrincipal")); assertEquals("true", System.getProperty("zookeeper.kerberos.removeRealmFromPrincipal")); } finally { server.close(); } } }TestTestingServer.java000066400000000000000000000036621442004423600341230ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-test/src/test/java/org/apache/curator/test/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.test; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class TestTestingServer { @TempDir File zkTmpDir; @Test public void setCustomTickTimeTest() throws Exception { TestingZooKeeperServer.hasZooKeeperServerEmbedded = false; final int defaultZkTickTime = ZooKeeperServer.DEFAULT_TICK_TIME; final int customTickMs; if (defaultZkTickTime > 0) { customTickMs = defaultZkTickTime + (defaultZkTickTime == Integer.MAX_VALUE ? -1 : +1); } else { customTickMs = 100; } final InstanceSpec spec = new InstanceSpec(zkTmpDir, -1, -1, -1, true, -1, customTickMs, -1); final int zkTickTime; try (TestingServer testingServer = new TestingServer(spec, true)) { TestingZooKeeperMain main = (TestingZooKeeperMain) testingServer.getTestingZooKeeperServer().getMain(); zkTickTime = main.getZkServer().getTickTime(); } assertEquals(customTickMs, zkTickTime); } } curator-apache-curator-5.5.0/curator-x-async/000077500000000000000000000000001442004423600211325ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/pom.xml000066400000000000000000000047331442004423600224560ustar00rootroot00000000000000 org.apache.curator apache-curator 5.5.0 4.0.0 curator-x-async Curator Async Java 8 Async DSL 2017 org.apache.curator curator-framework org.apache.curator curator-recipes provided com.fasterxml.jackson.core jackson-databind provided org.apache.curator curator-test test org.junit.jupiter junit-jupiter-api test org.slf4j slf4j-log4j12 test curator-apache-curator-5.5.0/curator-x-async/src/000077500000000000000000000000001442004423600217215ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/000077500000000000000000000000001442004423600226455ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/000077500000000000000000000000001442004423600235665ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/000077500000000000000000000000001442004423600243555ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/000077500000000000000000000000001442004423600255765ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/000077500000000000000000000000001442004423600272555ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/000077500000000000000000000000001442004423600275245ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/000077500000000000000000000000001442004423600306415ustar00rootroot00000000000000AsyncCuratorFramework.java000066400000000000000000000113141442004423600357200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.x.async.api.AsyncCuratorFrameworkDsl; import org.apache.curator.x.async.details.AsyncCuratorFrameworkImpl; import org.apache.zookeeper.WatchedEvent; import java.util.function.UnaryOperator; /** * Zookeeper framework-style client that returns composable async operations * that implement {@link java.util.concurrent.CompletionStage} */ public interface AsyncCuratorFramework extends AsyncCuratorFrameworkDsl { /** * Takes an old-style Curator instance and returns a new async instance that * wraps it. Note: the instance must have been created through a chain that * leads back to {@link org.apache.curator.framework.CuratorFrameworkFactory}. i.e. * you can have derived instances such as {@link org.apache.curator.framework.WatcherRemoveCuratorFramework} * etc. but the original client must have been created by the Factory. * * @param client instance to wrap * @return wrapped instance */ static AsyncCuratorFramework wrap(CuratorFramework client) { return new AsyncCuratorFrameworkImpl(client); } /** * Returns the client that was originally passed to {@link #wrap(org.apache.curator.framework.CuratorFramework)} * * @return original client */ CuratorFramework unwrap(); /** * Returns a facade that changes how watchers are set when {@link #watched()} is called * * @param mode watch mode to use for subsequent calls to {@link #watched()} * @return facade */ AsyncCuratorFrameworkDsl with(WatchMode mode); /** * Returns a facade that adds the given UnhandledErrorListener to all background operations * * @param listener lister to use * @return facade */ AsyncCuratorFrameworkDsl with(UnhandledErrorListener listener); /** * Returns a facade that adds the the given filters to all background operations and watchers. * resultFilter will get called for every background callback. watcherFilter * will get called for every watcher. The filters can return new versions or unchanged versions * of the arguments. * * @param resultFilter filter to use or null * @param watcherFilter filter to use or null * @return facade */ AsyncCuratorFrameworkDsl with(UnaryOperator resultFilter, UnaryOperator watcherFilter); /** * Set any combination of listener or filters * * @param listener lister to use or null * @param resultFilter filter to use or null * @param watcherFilter filter to use or null * @see #with(java.util.function.UnaryOperator, java.util.function.UnaryOperator) * @see #with(org.apache.curator.framework.api.UnhandledErrorListener) * @return facade */ AsyncCuratorFrameworkDsl with(UnhandledErrorListener listener, UnaryOperator resultFilter, UnaryOperator watcherFilter); /** * Set any combination of listener, filters or watch mode * * @param mode watch mode to use for subsequent calls to {@link #watched()} (cannot be null) * @param listener lister to use or null * @param resultFilter filter to use or null * @param watcherFilter filter to use or null * @see #with(WatchMode) * @see #with(java.util.function.UnaryOperator, java.util.function.UnaryOperator) * @see #with(org.apache.curator.framework.api.UnhandledErrorListener) * @return facade */ AsyncCuratorFrameworkDsl with(WatchMode mode, UnhandledErrorListener listener, UnaryOperator resultFilter, UnaryOperator watcherFilter); } AsyncEventException.java000066400000000000000000000033241442004423600353650ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import java.util.concurrent.CompletionStage; /** * The exception type set for async watchers */ public abstract class AsyncEventException extends Exception { /** * Returns the error condition that temporarily triggered the watcher. NOTE: the watcher * will most likely still be set. Use {@link #reset()} to stage on the successful trigger * * @return state */ public abstract Watcher.Event.KeeperState getKeeperState(); /** * ZooKeeper temporarily triggers watchers when there is a connection event. However, the watcher * stays set for the original operation. Use this method to reset with a new completion stage * that will allow waiting for a successful trigger. * * @return new stage */ public abstract CompletionStage reset(); } AsyncResult.java000066400000000000000000000104141442004423600337010ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async; import org.apache.curator.x.async.details.AsyncResultImpl; import org.apache.zookeeper.KeeperException; import java.util.Optional; import java.util.concurrent.CompletionStage; /** *

* Utility that combines the value, the ZooKeeper result code and the exception in one object * allowing you to not worry about exceptional completions. i.e. the {@link java.util.concurrent.CompletionStage} * returned by {@link org.apache.curator.x.async.AsyncResult#of(AsyncStage)} always completes successfully with an * {@link org.apache.curator.x.async.AsyncResult} object. *

* *

* All three possible results from a ZooKeeper method are encapsulated in this object. If the ZooKeeper * method succeeds, the internal value will be set. If there was a standard ZooKeeper error code * ({@link org.apache.zookeeper.KeeperException.Code#NODEEXISTS}, etc.), that code is set and the * value is null. If there was a general exception, that exception is set, the value will be null * and the code will be {@link org.apache.zookeeper.KeeperException.Code#SYSTEMERROR}. *

* @param value type */ public interface AsyncResult { /** * Return a new stage that wraps an async stage into a result-style completion stage. The returned * CompletionStage will always complete successfully. * * @param stage the stage to wrap * @param value type * @return completion stage that resolves to a result */ static CompletionStage> of(AsyncStage stage) { return stage.handle((value, ex) -> { if ( ex != null ) { if ( ex instanceof KeeperException ) { return new AsyncResultImpl(((KeeperException)ex).code()); } return new AsyncResultImpl(ex); } return new AsyncResultImpl(value); }); } /** * Returns the raw result of the ZooKeeper method or null * * @return result or null */ T getRawValue(); /** * An optional wrapper around the ZooKeeper method result * * @return wrapped result */ Optional getValue(); /** * Return the ZooKeeper result code. If the method was successful, * {@link org.apache.zookeeper.KeeperException.Code#OK} is returned. If there was a general * exception {@link org.apache.zookeeper.KeeperException.Code#SYSTEMERROR} is returned. * * @return result code */ KeeperException.Code getCode(); /** * Return any general exception or null * * @return exception or null */ Throwable getRawException(); /** * An optional wrapper around any general exception * * @return wrapped exception */ Optional getException(); /** * If there was a general exception (but not a {@link org.apache.zookeeper.KeeperException}) * a {@link java.lang.RuntimeException} is thrown that wraps the exception. Otherwise, the method returns * without any action being performed. */ void checkException(); /** * If there was a general exception or a {@link org.apache.zookeeper.KeeperException} * a {@link java.lang.RuntimeException} is thrown that wraps the exception. Otherwise, the method returns * without any action being performed. */ void checkError(); } AsyncStage.java000066400000000000000000000035151442004423600334720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async; import org.apache.zookeeper.WatchedEvent; import java.util.concurrent.CompletionStage; /** * A {@link java.util.concurrent.CompletionStage} that is the result of most operations. */ public interface AsyncStage extends CompletionStage { /** *

* If the {@link org.apache.curator.x.async.api.WatchableAsyncCuratorFramework} facade is * used (via {@link AsyncCuratorFramework#watched()}), this returns the completion * stage used when the watcher is triggered *

* *

* Also, applies to {@link org.apache.curator.x.async.modeled.ModeledFramework} * when {@link org.apache.curator.x.async.modeled.ModeledFrameworkBuilder#watched(WatchMode)} * or {@link org.apache.curator.x.async.modeled.ModeledFrameworkBuilder#watched(WatchMode, java.util.function.UnaryOperator)} * is used. *

* * @return CompletionStage for the set watcher or null */ CompletionStage event(); } AsyncWrappers.java000066400000000000000000000332521442004423600342330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async; import com.google.common.base.Throwables; import com.google.common.collect.Maps; import org.apache.curator.framework.recipes.locks.InterProcessLock; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.curator.x.async.api.ExistsOption; import org.apache.zookeeper.KeeperException; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; /** *

* Utility for adding asynchronous behavior *

* *

* E.g. locks: *

 *     InterProcessMutex mutex = new InterProcessMutex(...) // or any InterProcessLock
 *     AsyncWrappers.lockAsync(mutex, executor).thenAccept(dummy -> {
 *         try
 *         {
 *             // do work while holding the lock
 *         }
 *         finally
 *         {
 *             AsyncWrappers.release(mutex);
 *         }
 *     }).exceptionally(e -> {
 *         if ( e instanceOf TimeoutException ) {
 *             // timed out trying to acquire the lock
 *         }
 *         // handle the error
 *         return null;
 *     });
 * 
*

* *

* E.g. EnsureContainers *

 *     AsyncWrappers.(client, path, executor).thenAccept(dummy -> {
 *         // execute after ensuring containers
 *     });
 * 
*

*/ public class AsyncWrappers { /** *

* Return the children of the given path (keyed by the full path) and the data for each node. * IMPORTANT: this results in a ZooKeeper query * for each child node returned. i.e. if the initial children() call returns * 10 nodes an additional 10 ZooKeeper queries are made to get the data. *

* *

* Note: if the any of the nodes in the path do not exist yet, {@link org.apache.zookeeper.KeeperException.NoNodeException} * is NOT set. Instead the stage is completed with an empty map. *

* * @return CompletionStage */ public static CompletionStage> childrenWithData(AsyncCuratorFramework client, String path) { return childrenWithData(client, path, false); } /** *

* Return the children of the given path (keyed by the full path) and the data for each node. * IMPORTANT: this results in a ZooKeeper query * for each child node returned. i.e. if the initial children() call returns * 10 nodes an additional 10 ZooKeeper queries are made to get the data. *

* *

* Note: if the any of the nodes in the path do not exist yet, {@link org.apache.zookeeper.KeeperException.NoNodeException} * is NOT set. Instead the stage is completed with an empty map. *

* * @param isCompressed pass true if data is compressed * @return CompletionStage */ public static CompletionStage> childrenWithData(AsyncCuratorFramework client, String path, boolean isCompressed) { CompletableFuture> future = new CompletableFuture<>(); client.getChildren().forPath(path).handle((children, e) -> { if ( e != null ) { if ( Throwables.getRootCause(e) instanceof KeeperException.NoNodeException ) { future.complete(Maps.newHashMap()); } else { future.completeExceptionally(e); } } else { completeChildren(client, future, path, children, isCompressed); } return null; }); return future; } /** * Asynchronously ensure that the parents of the given path are created * * @param client client * @param path path to ensure * @return stage */ public static CompletionStage asyncEnsureParents(AsyncCuratorFramework client, String path) { return ensure(client, path, ExistsOption.createParentsIfNeeded); } /** * Asynchronously ensure that the parents of the given path are created as containers * * @param client client * @param path path to ensure * @return stage */ public static CompletionStage asyncEnsureContainers(AsyncCuratorFramework client, String path) { return ensure(client, path, ExistsOption.createParentsAsContainers); } /** * Set as the completion stage's exception when trying to acquire a lock * times out */ public static class TimeoutException extends RuntimeException { } /** * Attempt to acquire the given lock asynchronously using the given timeout and executor. If the lock * is not acquired within the timeout stage is completedExceptionally with {@link AsyncWrappers.TimeoutException} * * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex}, * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.) * @param timeout max timeout to acquire lock * @param unit time unit of timeout * @param executor executor to use to asynchronously acquire * @return stage */ public static CompletionStage lockAsync(InterProcessLock lock, long timeout, TimeUnit unit, Executor executor) { CompletableFuture future = new CompletableFuture<>(); if ( executor == null ) { CompletableFuture.runAsync(() -> lock(future, lock, timeout, unit)); } else { CompletableFuture.runAsync(() -> lock(future, lock, timeout, unit), executor); } return future; } /** * Attempt to acquire the given lock asynchronously using the given timeout and executor. The stage * is completed with a Boolean that indicates whether or not the lock was acquired. * * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex}, * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.) * @param timeout max timeout to acquire lock * @param unit time unit of timeout * @param executor executor to use to asynchronously acquire * @return stage */ public static CompletionStage lockAsyncIf(InterProcessLock lock, long timeout, TimeUnit unit, Executor executor) { CompletableFuture future = new CompletableFuture<>(); if ( executor == null ) { CompletableFuture.runAsync(() -> lockIf(future, lock, timeout, unit)); } else { CompletableFuture.runAsync(() -> lockIf(future, lock, timeout, unit), executor); } return future; } /** * Attempt to acquire the given lock asynchronously using the given executor and without a timeout. * * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex}, * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.) * @param executor executor to use to asynchronously acquire * @return stage */ public static CompletionStage lockAsync(InterProcessLock lock, Executor executor) { return lockAsync(lock, 0, null, executor); } /** * Attempt to acquire the given lock asynchronously using the given timeout using the {@link java.util.concurrent.ForkJoinPool#commonPool()}. * If the lock is not acquired within the timeout stage is completedExceptionally with {@link AsyncWrappers.TimeoutException} * * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex}, * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.) * @param timeout max timeout to acquire lock * @param unit time unit of timeout * @return stage */ public static CompletionStage lockAsync(InterProcessLock lock, long timeout, TimeUnit unit) { return lockAsync(lock, timeout, unit, null); } /** * Attempt to acquire the given lock asynchronously using the given timeout using the {@link java.util.concurrent.ForkJoinPool#commonPool()}. * The stage is completed with a Boolean that indicates whether or not the lock was acquired. * * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex}, * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.) * @param timeout max timeout to acquire lock * @param unit time unit of timeout * @return stage */ public static CompletionStage lockAsyncIf(InterProcessLock lock, long timeout, TimeUnit unit) { return lockAsyncIf(lock, timeout, unit, null); } /** * Attempt to acquire the given lock asynchronously without timeout using the {@link java.util.concurrent.ForkJoinPool#commonPool()}. * * @param lock a lock implementation (e.g. {@link org.apache.curator.framework.recipes.locks.InterProcessMutex}, * {@link org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2}, etc.) * @return stage */ public static CompletionStage lockAsync(InterProcessLock lock) { return lockAsync(lock, 0, null, null); } /** * Release the lock and wrap any exception in RuntimeException * * @param lock lock to release */ public static void release(InterProcessLock lock) { release(lock, true); } /** * Release the lock and wrap any exception in RuntimeException * * @param lock lock to release * @param ignoreNoLockExceptions if true {@link java.lang.IllegalStateException} is ignored */ public static void release(InterProcessLock lock, boolean ignoreNoLockExceptions) { try { lock.release(); } catch ( IllegalStateException e ) { if ( !ignoreNoLockExceptions ) { throw new RuntimeException(e); } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new RuntimeException(e); } } private static void lockIf(CompletableFuture future, InterProcessLock lock, long timeout, TimeUnit unit) { try { future.complete(lock.acquire(timeout, unit)); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); future.completeExceptionally(e); } } private static void lock(CompletableFuture future, InterProcessLock lock, long timeout, TimeUnit unit) { try { if ( unit != null ) { if ( lock.acquire(timeout, unit) ) { future.complete(null); } else { future.completeExceptionally(new TimeoutException()); } } else { lock.acquire(); future.complete(null); } } catch ( Throwable e ) { ThreadUtils.checkInterrupted(e); future.completeExceptionally(e); } } private static void completeChildren(AsyncCuratorFramework client, CompletableFuture> future, String parentPath, List children, boolean isCompressed) { Map nodes = Maps.newHashMap(); if ( children.size() == 0 ) { future.complete(nodes); return; } children.forEach(node -> { String path = ZKPaths.makePath(parentPath, node); AsyncStage stage = isCompressed ? client.getData().decompressed().forPath(path) : client.getData().forPath(path); stage.handle((data, e) -> { if ( e != null ) { future.completeExceptionally(e); } else { nodes.put(path, data); if ( nodes.size() == children.size() ) { future.complete(nodes); } } return null; }); }); } private static CompletionStage ensure(AsyncCuratorFramework client, String path, ExistsOption option) { String localPath = ZKPaths.makePath(path, "foo"); return client .checkExists() .withOptions(Collections.singleton(option)) .forPath(localPath) .thenApply(__ -> null) ; } private AsyncWrappers() { } } curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/WatchMode.java000066400000000000000000000033251442004423600333620ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async; public enum WatchMode { /** * The {@link java.util.concurrent.CompletionStage}<org.apache.zookeeper.WatchedEvent> will only * complete on successful trigger. i.e. connection issues are ignored */ successOnly, /** * The {@link java.util.concurrent.CompletionStage}<org.apache.zookeeper.WatchedEvent> will only * completeExceptionally. Successful trigger is ignored. Connection exceptions are * of type: {@link org.apache.curator.x.async.AsyncEventException}. */ stateChangeOnly, /** * The {@link java.util.concurrent.CompletionStage}<org.apache.zookeeper.WatchedEvent> will * complete for both successful trigger and connection exceptions. Connection exceptions are * of type: {@link org.apache.curator.x.async.AsyncEventException}. Note: this is the default * watch mode. */ stateChangeAndSuccess } curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/000077500000000000000000000000001442004423600314125ustar00rootroot00000000000000AsyncCreateBuilder.java000066400000000000000000000140201442004423600357030ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.Set; /** * Builder for ZNode creates */ public interface AsyncCreateBuilder extends AsyncPathAndBytesable> { /** * Have the operation fill the provided stat object * * @param stat the stat to have filled in * @return this */ AsyncPathAndBytesable> storingStatIn(Stat stat); /** * Use the given create mode. The default is {@link org.apache.zookeeper.CreateMode#PERSISTENT} * * @param createMode mode to use * @return this */ AsyncPathAndBytesable> withMode(CreateMode createMode); /** * Set an ACL list (default is {@link org.apache.zookeeper.ZooDefs.Ids#OPEN_ACL_UNSAFE}) * * @param aclList the ACL list to use * @return this */ AsyncPathAndBytesable> withACL(List aclList); /** * Specify a TTL when mode is {@link org.apache.zookeeper.CreateMode#PERSISTENT_WITH_TTL} or * {@link org.apache.zookeeper.CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If * the znode has not been modified within the given TTL, it will be deleted once it has no * children. The TTL unit is milliseconds and must be greater than 0 and less than or equal to * EphemeralType.MAX_TTL. * * @param ttl the ttl * @return this for chaining */ AsyncPathAndBytesable> withTtl(long ttl); /** * Specify the setData expected matching version when using option * {@link org.apache.curator.x.async.api.CreateOption#setDataIfExists}. By default -1 is used. * * @param version setData expected matching version * @return this for chaining */ AsyncPathAndBytesable> withSetDataVersion(int version); /** * Options to change how the ZNode is created * * @param options options * @return this */ AsyncPathAndBytesable> withOptions(Set options); /** * set options and ACLs * * @param options options * @param aclList the ACL list to use * @see #withOptions(java.util.Set) * @see #withACL(java.util.List) * @return this */ AsyncPathAndBytesable> withOptions(Set options, List aclList); /** * set options, mode and ACLs * * @param options options * @param createMode mode to use * @param aclList the ACL list to use * @see #withACL(java.util.List) * @see #withOptions(java.util.Set) * @see #withMode(org.apache.zookeeper.CreateMode) * @see #withACL(java.util.List) * @return this */ AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode, List aclList); /** * set options and mode * * @param options options * @param createMode mode to use * @see #withOptions(java.util.Set) * @see #withMode(org.apache.zookeeper.CreateMode) * @return this */ AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode); /** * set options, mode, ACLs, and stat * * @param options options * @param createMode mode to use * @param aclList the ACL list to use * @param stat the stat to have filled in * @see #withOptions(java.util.Set) * @see #withMode(org.apache.zookeeper.CreateMode) * @see #withACL(java.util.List) * @see #storingStatIn(org.apache.zookeeper.data.Stat) * @return this */ AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode, List aclList, Stat stat); /** * set options, mode, ACLs, and stat * * @param options options * @param createMode mode to use * @param aclList the ACL list to use * @param stat the stat to have filled in * @param ttl the ttl or 0 * @see #withOptions(java.util.Set) * @see #withMode(org.apache.zookeeper.CreateMode) * @see #withACL(java.util.List) * @see #storingStatIn(org.apache.zookeeper.data.Stat) * @see #withTtl(long) * @return this */ AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode, List aclList, Stat stat, long ttl); /** * set options, mode, ACLs, and stat * * @param options options * @param createMode mode to use * @param aclList the ACL list to use * @param stat the stat to have filled in * @param ttl the ttl or 0 * @param setDataVersion the setData matching version or -1 * @see #withOptions(java.util.Set) * @see #withMode(org.apache.zookeeper.CreateMode) * @see #withACL(java.util.List) * @see #storingStatIn(org.apache.zookeeper.data.Stat) * @see #withTtl(long) * @see #withSetDataVersion(long) * @return this */ AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode, List aclList, Stat stat, long ttl, int setDataVersion); } AsyncCuratorFrameworkDsl.java000066400000000000000000000064321442004423600371410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.framework.api.transaction.CuratorOp; /** * Zookeeper framework-style client */ public interface AsyncCuratorFrameworkDsl extends WatchableAsyncCuratorFramework { /** *

* Returns a facade that adds watching to any of the subsequently created builders. i.e. all * operations on the WatchableAsyncCuratorFramework facade will have watchers set. Also, * the {@link org.apache.curator.x.async.AsyncStage} returned from these builders will * have a loaded staged watcher that is accessed from {@link org.apache.curator.x.async.AsyncStage#event()} *

* *

* {@link org.apache.curator.x.async.WatchMode#stateChangeAndSuccess} is used *

* * @return watcher facade */ WatchableAsyncCuratorFramework watched(); /** * Start a create builder * * @return builder object */ AsyncCreateBuilder create(); /** * Start a delete builder * * @return builder object */ AsyncDeleteBuilder delete(); /** * Start a set data builder * * @return builder object */ AsyncSetDataBuilder setData(); /** * Start a get ACL builder * * @return builder object */ AsyncGetACLBuilder getACL(); /** * Start a set ACL builder * * @return builder object */ AsyncSetACLBuilder setACL(); /** * Start a reconfig builder * * @return builder object */ AsyncReconfigBuilder reconfig(); /** * Start a transaction builder * * @return builder object */ AsyncMultiTransaction transaction(); /** * Allocate an operation that can be used with {@link #transaction()}. * NOTE: {@link CuratorOp} instances created by this builder are * reusable. * * @return operation builder */ AsyncTransactionOp transactionOp(); /** * Start a sync builder * * @return builder object */ AsyncSyncBuilder sync(); /** * Start a remove watches builder * * @return builder object */ AsyncRemoveWatchesBuilder removeWatches(); /** * Start an add watch builder. Supported only when ZooKeeper JAR of version 3.6 or * above is used, throws {@code IllegalStateException} for ZooKeeper JAR 3.5 or below * * @return builder object * @throws IllegalStateException ZooKeeper JAR is 3.5 or below */ AsyncWatchBuilder addWatch(); } AsyncDeleteBuilder.java000066400000000000000000000034471442004423600357150ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import java.util.Set; /** * Builder for ZNode deletions */ public interface AsyncDeleteBuilder extends AsyncPathable> { /** * Changes the deletion options. By default, no options are used * * @param options set of deletion options * @return this */ AsyncPathable> withOptions(Set options); /** * Set options and version. * * @param options set of deletion options * @param version version to use * @see #withOptions(java.util.Set) * @see #withVersion(int) * @return this */ AsyncPathable> withOptionsAndVersion(Set options, int version); /** * Changes the version number passed to the delete() method. By default, -1 is used * * @param version version to use * @return this */ AsyncPathable> withVersion(int version); } AsyncEnsemblable.java000066400000000000000000000021011442004423600353770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; /** * Terminal operation for config/reconfig builders */ public interface AsyncEnsemblable { /** * Commit the currently building operation and invoke ZooKeeper * * @return async stage */ T forEnsemble(); } AsyncExistsBuilder.java000066400000000000000000000024151442004423600357640ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.Stat; import java.util.Set; /** * Builder for ZNode exists */ public interface AsyncExistsBuilder extends AsyncPathable> { /** * Use the given options. The default is to use no options * * @param options options to use * @return this */ AsyncPathable> withOptions(Set options); } AsyncGetACLBuilder.java000066400000000000000000000024551442004423600355500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; /** * Builder for getAcls */ public interface AsyncGetACLBuilder extends AsyncPathable>> { /** * Have the operation fill the provided stat object * * @param stat the stat to have filled in * @return this */ AsyncPathable>> storingStatIn(Stat stat); } AsyncGetChildrenBuilder.java000066400000000000000000000024301442004423600366720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.Stat; import java.util.List; /** * Builder for getChildren() */ public interface AsyncGetChildrenBuilder extends AsyncPathable>> { /** * Have the operation fill the provided stat object * * @param stat the stat to have filled in * @return this */ AsyncPathable>> storingStatIn(Stat stat); } AsyncGetConfigBuilder.java000066400000000000000000000023671442004423600363600ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.Stat; /** * Builder for getConfig() */ public interface AsyncGetConfigBuilder extends AsyncEnsemblable> { /** * Have the operation fill the provided stat object * * @param stat the stat to have filled in * @return this */ AsyncEnsemblable> storingStatIn(Stat stat); } AsyncGetDataBuilder.java000066400000000000000000000033771442004423600360260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.Stat; /** * Builder to get ZNode data */ public interface AsyncGetDataBuilder extends AsyncPathable> { /** * Cause the data to be de-compressed using the configured compression provider * * @return this */ AsyncPathable> decompressed(); /** * Have the operation fill the provided stat object * * @param stat the stat to have filled in * @return this */ AsyncPathable> storingStatIn(Stat stat); /** * Have the operation fill the provided stat object and have the data be de-compressed * * @param stat the stat to have filled in * @see #decompressed() * @see #storingStatIn(org.apache.zookeeper.data.Stat) * @return this */ AsyncPathable> decompressedStoringStatIn(Stat stat); } AsyncMultiTransaction.java000066400000000000000000000031011442004423600364670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.x.async.AsyncStage; import java.util.List; /** * Terminal operation to support multi/transactions */ public interface AsyncMultiTransaction { /** * Invoke ZooKeeper to commit the given operations as a single transaction. Create the * operation instances via {@link org.apache.curator.x.async.AsyncCuratorFramework#transactionOp()} * * @param operations operations that make up the transaction. * @return AsyncStage instance for managing the completion */ AsyncStage> forOperations(List operations); } AsyncPathAndBytesable.java000066400000000000000000000023231442004423600363460ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; /** * Terminal operation for various builders */ public interface AsyncPathAndBytesable extends AsyncPathable { /** * Commit the currently building operation using the given path and data * and invoke ZooKeeper * * @param path the path * @param data the data * @return usually an async stage */ T forPath(String path, byte[] data); } AsyncPathable.java000066400000000000000000000022001442004423600347060ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; /** * Terminal operation for various builders */ public interface AsyncPathable { /** * Commit the currently building operation using the given path * and invoke ZooKeeper * * @param path the path * @return usually an async stage */ T forPath(String path); } AsyncReconfigBuilder.java000066400000000000000000000113431442004423600362410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.Stat; import java.util.List; /** * Builder for reconfigs */ public interface AsyncReconfigBuilder { /** * Sets one or more members that are meant to be the ensemble. * The expected format is: server.[id]=[hostname]:[peer port]:[election port]:[type];[client port] * * @param servers The new server list * @return this */ AsyncEnsemblable> withNewMembers(List servers); /** * Adds servers to join the ensemble and/or servers to leave the ensemble. The format for joining * is: server.[id]=[hostname]:[peer port]:[election port]:[type];[client port]. The format * for leaving is a list of server IDs. * * @param joining The servers joining * @param leaving The servers leaving * @return this */ AsyncEnsemblable> withJoiningAndLeaving(List joining, List leaving); /** * Same as {@link #withNewMembers(java.util.List)} but allows specified the configuration version to use. * By default the configuration version is -1. * * @param servers The new server list * @param fromConfig the config version to use * @see #withNewMembers(java.util.List) * @return this */ AsyncEnsemblable> withNewMembers(List servers, long fromConfig); /** * Specify joiners, leaves and config version. By default the configuration version is -1. * * @param joining The servers joining * @param leaving The servers leaving * @param fromConfig the config version to use * @see #withJoiningAndLeaving(java.util.List, java.util.List) * @return this */ AsyncEnsemblable> withJoiningAndLeaving(List joining, List leaving, long fromConfig); /** * Same as {@link #withNewMembers(java.util.List)} but allows a stat to hold the stat info from "/zookeeper/config" * * @param servers The servers joining. * @param stat stat to hold the stat value * @see #withNewMembers(java.util.List) * @return this */ AsyncEnsemblable> withNewMembers(List servers, Stat stat); /** * Same as {@link #withJoiningAndLeaving(java.util.List, java.util.List)} * but allows a stat to hold the stat info from "/zookeeper/config" * * @param joining The servers joining * @param leaving The servers leaving * @param stat stat to hold the stat value * @see #withJoiningAndLeaving(java.util.List, java.util.List) * @return this */ AsyncEnsemblable> withJoiningAndLeaving(List joining, List leaving, Stat stat); /** * Same as {@link #withNewMembers(java.util.List)} with stat and config version * * @param servers The servers joining. * @param stat stat to hold the stat value * @param fromConfig the config version to use * @see #withNewMembers(java.util.List, long) * @see #withNewMembers(java.util.List, org.apache.zookeeper.data.Stat) * @return this */ AsyncEnsemblable> withNewMembers(List servers, Stat stat, long fromConfig); /** * Same as {@link #withJoiningAndLeaving(java.util.List, java.util.List)} with stat and config version * * @param joining The servers joining * @param leaving The servers leaving * @param stat stat to hold the stat value * @param fromConfig the config version to use * @see #withJoiningAndLeaving(java.util.List, java.util.List, long) * @see #withJoiningAndLeaving(java.util.List, java.util.List, org.apache.zookeeper.data.Stat) * @return this */ AsyncEnsemblable> withJoiningAndLeaving(List joining, List leaving, Stat stat, long fromConfig); } AsyncRemoveWatchesBuilder.java000066400000000000000000000074631442004423600372710ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.Watcher; import java.util.Set; /** * Builder for watcher removal */ public interface AsyncRemoveWatchesBuilder { /** * @param watcher the watcher to remove * @return this */ AsyncPathable> removing(Watcher watcher); /** * @param watcher the watcher to remove * @return this */ AsyncPathable> removing(CuratorWatcher watcher); /** * Remove all watchers * * @return this */ AsyncPathable> removingAll(); /** * @param watcher the watcher to remove * @param options watcher removal options * @return this */ AsyncPathable> removing(Watcher watcher, Set options); /** * @param watcher the watcher to remove * @param options watcher removal options * @return this */ AsyncPathable> removing(CuratorWatcher watcher, Set options); /** * Remove all watchers * * @param options watcher removal options * @return this */ AsyncPathable> removingAll(Set options); /** * Remove a watcher of a given type * * @param watcher the watcher to remove * @param watcherType watcher type * @param options watcher removal options * @return this */ AsyncPathable> removing(Watcher watcher, Watcher.WatcherType watcherType, Set options); /** * Remove a watcher of a given type * * @param watcher the watcher to remove * @param watcherType watcher type * @param options watcher removal options * @return this */ AsyncPathable> removing(CuratorWatcher watcher, Watcher.WatcherType watcherType, Set options); /** * Remove all watchers of a given type * * @param watcherType watcher type * @param options watcher removal options * @return this */ AsyncPathable> removingAll(Watcher.WatcherType watcherType, Set options); /** * Remove a watcher of a given type * * @param watcher the watcher to remove * @param watcherType watcher type * @return this */ AsyncPathable> removing(Watcher watcher, Watcher.WatcherType watcherType); /** * Remove a watcher of a given type * * @param watcher the watcher to remove * @param watcherType watcher type * @return this */ AsyncPathable> removing(CuratorWatcher watcher, Watcher.WatcherType watcherType); /** * Remove all watchers of a given type * * @param watcherType watcher type * @return this */ AsyncPathable> removingAll(Watcher.WatcherType watcherType); } AsyncSetACLBuilder.java000066400000000000000000000031021442004423600355520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; /** * Builder for setting ACLs */ public interface AsyncSetACLBuilder { /** * Set the given ACLs * * @param aclList ACLs to set * @return this */ AsyncPathable> withACL(List aclList); /** * Set the given ACLs only if the "a" version matches. By default -1 is used * which matches all versions. * * @param aclList ACLs to set * @param version "a" version * @see org.apache.zookeeper.data.Stat#getAversion() * @return this */ AsyncPathable> withACL(List aclList, int version); } AsyncSetDataBuilder.java000066400000000000000000000034411442004423600360320ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.Stat; /** * Builder for setting ZNode data */ public interface AsyncSetDataBuilder extends AsyncPathAndBytesable> { /** * Cause the data to be compressed using the configured compression provider * * @return this */ AsyncPathAndBytesable> compressed(); /** * Cause the data to be compressed using the configured compression provider. * Only sets if the version matches. By default -1 is used * which matches all versions. * * @param version version * @return this */ AsyncPathAndBytesable> compressedWithVersion(int version); /** * Only sets if the version matches. By default -1 is used * which matches all versions. * * @param version version * @return this */ AsyncPathAndBytesable> withVersion(int version); } AsyncSyncBuilder.java000066400000000000000000000017601442004423600354230ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.x.async.AsyncStage; /** * Builder for syncs */ public interface AsyncSyncBuilder extends AsyncPathable> { } AsyncTransactionCheckBuilder.java000066400000000000000000000023101442004423600377220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.framework.api.transaction.CuratorOp; /** * @see AsyncTransactionOp#check() */ public interface AsyncTransactionCheckBuilder extends AsyncPathable { /** * Use the given version (the default is -1) * * @param version version to use * @return this */ AsyncPathable withVersion(int version); } AsyncTransactionCreateBuilder.java000066400000000000000000000063061442004423600401210ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.ACL; import java.util.List; /** * @see AsyncTransactionOp#create() */ public interface AsyncTransactionCreateBuilder extends AsyncPathAndBytesable { /** * Specify a mode for the create. The default is {@link org.apache.zookeeper.CreateMode#PERSISTENT} * * @param createMode mode * @return this */ AsyncPathAndBytesable withMode(CreateMode createMode); /** * Set an ACL list (default is {@link org.apache.zookeeper.ZooDefs.Ids#OPEN_ACL_UNSAFE}) * * @param aclList the ACL list to use * @return this */ AsyncPathAndBytesable withACL(List aclList); /** * Cause the data to be compressed using the configured compression provider * * @return this */ AsyncPathAndBytesable compressed(); /** * Specify a TTL when mode is {@link org.apache.zookeeper.CreateMode#PERSISTENT_WITH_TTL} or * {@link org.apache.zookeeper.CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If * the znode has not been modified within the given TTL, it will be deleted once it has no * children. The TTL unit is milliseconds and must be greater than 0 and less than or equal to * EphemeralType.MAX_TTL. * * @param ttl the ttl * @return this for chaining */ AsyncPathAndBytesable withTtl(long ttl); /** * Specify mode, acl list and compression * * @param createMode mode * @param aclList the ACL list to use * @param compressed true to compress * @see #withMode(org.apache.zookeeper.CreateMode) * @see #withACL(java.util.List) * @see #compressed() * @return this */ AsyncPathAndBytesable withOptions(CreateMode createMode, List aclList, boolean compressed); /** * Specify mode, acl list, compression and ttl * * @param createMode mode * @param aclList the ACL list to use * @param compressed true to compress * @see #withMode(org.apache.zookeeper.CreateMode) * @see #withACL(java.util.List) * @see #compressed() * @see #withTtl(long) * @return this */ AsyncPathAndBytesable withOptions(CreateMode createMode, List aclList, boolean compressed, long ttl); } AsyncTransactionDeleteBuilder.java000066400000000000000000000023301442004423600401110ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.framework.api.transaction.CuratorOp; /** * @see AsyncTransactionOp#delete() */ public interface AsyncTransactionDeleteBuilder extends AsyncPathable { /** * Changes the version number used. By default, -1 is used * * @param version version to use * @return this */ AsyncPathable withVersion(int version); } AsyncTransactionOp.java000066400000000000000000000031271442004423600357630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; /** * Builds operations that can be committed as a transaction * via {@link org.apache.curator.x.async.AsyncCuratorFramework#transaction()} */ public interface AsyncTransactionOp { /** * Start a create builder in the transaction * * @return builder object */ AsyncTransactionCreateBuilder create(); /** * Start a delete builder in the transaction * * @return builder object */ AsyncTransactionDeleteBuilder delete(); /** * Start a setData builder in the transaction * * @return builder object */ AsyncTransactionSetDataBuilder setData(); /** * Start a check builder in the transaction * * @return builder object */ AsyncTransactionCheckBuilder check(); } AsyncTransactionSetDataBuilder.java000066400000000000000000000033121442004423600402350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.framework.api.transaction.CuratorOp; /** * @see AsyncTransactionOp#setData() */ public interface AsyncTransactionSetDataBuilder extends AsyncPathAndBytesable { /** * Changes the version number used. By default, -1 is used * * @param version version to use * @return this */ AsyncPathAndBytesable withVersion(int version); /** * Cause the data to be compressed using the configured compression provider * * @return this */ AsyncPathAndBytesable compressed(); /** * Cause the data to be compressed using the configured compression provider. * Also changes the version number used. By default, -1 is used * * @param version version to use * @return this */ AsyncPathAndBytesable withVersionCompressed(int version); } AsyncWatchBuilder.java000066400000000000000000000025121442004423600355510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.framework.api.WatchableBase; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.AddWatchMode; public interface AsyncWatchBuilder extends WatchableBase>>, AsyncPathable> { /** * The mode to use. By default, {@link org.apache.zookeeper.AddWatchMode#PERSISTENT_RECURSIVE} is used * * @param mode mode * @return this */ AsyncWatchBuilder2 withMode(AddWatchMode mode); }AsyncWatchBuilder2.java000066400000000000000000000020771442004423600356410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; import org.apache.curator.framework.api.WatchableBase; import org.apache.curator.x.async.AsyncStage; public interface AsyncWatchBuilder2 extends WatchableBase>>, AsyncPathable> { }CreateOption.java000066400000000000000000000055671442004423600346070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; /** * Options when creating ZNodes */ public enum CreateOption { /** * Causes any parent nodes to get created if they haven't already been */ createParentsIfNeeded, /** * Causes any parent nodes to get created using {@link org.apache.zookeeper.CreateMode#CONTAINER} if they haven't already been. * IMPORTANT NOTE: container creation is a new feature in recent versions of ZooKeeper. * If the ZooKeeper version you're using does not support containers, the parent nodes * are created as ordinary PERSISTENT nodes. */ createParentsAsContainers, /** *

* Hat-tip to https://github.com/sbridges for pointing this out *

* *

* It turns out there is an edge case that exists when creating sequential-ephemeral * nodes. The creation can succeed on the server, but the server can crash before * the created node name is returned to the client. However, the ZK session is still * valid so the ephemeral node is not deleted. Thus, there is no way for the client to * determine what node was created for them. *

* *

* Even without sequential-ephemeral, however, the create can succeed on the sever * but the client (for various reasons) will not know it. *

* *

* Putting the create builder into protection mode works around this. * The name of the node that is created is prefixed with a GUID. If node creation fails * the normal retry mechanism will occur. On the retry, the parent path is first searched * for a node that has the GUID in it. If that node is found, it is assumed to be the lost * node that was successfully created on the first try and is returned to the caller. *

*/ doProtected, /** * Cause the data to be compressed using the configured compression provider */ compress, /** * If the ZNode already exists, Curator will instead call setData() */ setDataIfExists } DeleteOption.java000066400000000000000000000027311442004423600345740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; /** * Options to use when deleting ZNodes */ public enum DeleteOption { /** * Prevents the reporting of {@link org.apache.zookeeper.KeeperException.NoNodeException}s. * If the ZNode doesn't exist the delete method will appear to succeed. */ quietly, /** * Will also delete children if they exist */ deletingChildrenIfNeeded, /** * Solves edge cases where an operation may succeed on the server but connection failure occurs before a * response can be successfully returned to the client. * * @see org.apache.curator.framework.api.GuaranteeableDeletable */ guaranteed } ExistsOption.java000066400000000000000000000021651442004423600346520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; /** * Options to use when checking for ZNode existence */ public enum ExistsOption { /** * see {@link CreateOption#createParentsIfNeeded} */ createParentsIfNeeded, /** * see {@link CreateOption#createParentsAsContainers} */ createParentsAsContainers } RemoveWatcherOption.java000066400000000000000000000031251442004423600361430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; /** * Options to use when removing watchers */ public enum RemoveWatcherOption { /** * Solves edge cases where an operation may succeed on the server but connection failure occurs before a * response can be successfully returned to the client. * * @see org.apache.curator.framework.api.GuaranteeableDeletable */ guaranteed, /** * Specify if the client should just remove client side watches if a connection to ZK * is not available. Note that the standard Curator retry loop will not be used in t */ local, /** * Prevents the reporting of {@link org.apache.zookeeper.KeeperException.NoNodeException}s. * If the watcher doesn't exist the remove method will appear to succeed. */ quietly } WatchableAsyncCuratorFramework.java000066400000000000000000000031531442004423600403060ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/api/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.api; /** * operations that support watching */ public interface WatchableAsyncCuratorFramework { /** * Start an exists builder. The builder will return a Stat object as if org.apache.zookeeper.ZooKeeper.exists() were called. Thus, a null * means that it does not exist and an actual Stat object means it does exist. * * @return builder object */ AsyncExistsBuilder checkExists(); /** * Start a get data builder * * @return builder object */ AsyncGetDataBuilder getData(); /** * Start a get children builder * * @return builder object */ AsyncGetChildrenBuilder getChildren(); /** * Start a getConfig builder * * @return builder object */ AsyncGetConfigBuilder getConfig(); } curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/000077500000000000000000000000001442004423600322665ustar00rootroot00000000000000AsyncCreateBuilderImpl.java000066400000000000000000000151641442004423600374130ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.CreateBuilderImpl; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.api.AsyncCreateBuilder; import org.apache.curator.x.async.api.AsyncPathAndBytesable; import org.apache.curator.x.async.api.CreateOption; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; import static org.apache.curator.x.async.details.BackgroundProcs.nameProc; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; class AsyncCreateBuilderImpl implements AsyncCreateBuilder { private final CuratorFrameworkImpl client; private final Filters filters; private CreateMode createMode = CreateMode.PERSISTENT; private List aclList = null; private Set options = Collections.emptySet(); private Stat stat = null; private long ttl = -1; private int setDataVersion = -1; AsyncCreateBuilderImpl(CuratorFrameworkImpl client, Filters filters) { this.client = client; this.filters = filters; } @Override public AsyncPathAndBytesable> storingStatIn(Stat stat) { this.stat = stat; return this; } @Override public AsyncPathAndBytesable> withMode(CreateMode createMode) { this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null"); return this; } @Override public AsyncPathAndBytesable> withACL(List aclList) { this.aclList = aclList; return this; } @Override public AsyncPathAndBytesable> withTtl(long ttl) { this.ttl = ttl; return this; } @Override public AsyncPathAndBytesable> withSetDataVersion(int version) { this.setDataVersion = version; return this; } @Override public AsyncPathAndBytesable> withOptions(Set options) { this.options = Objects.requireNonNull(options, "options cannot be null"); return this; } @Override public AsyncPathAndBytesable> withOptions(Set options, List aclList) { this.options = Objects.requireNonNull(options, "options cannot be null"); this.aclList = aclList; return this; } @Override public AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode, List aclList) { this.options = Objects.requireNonNull(options, "options cannot be null"); this.aclList = aclList; this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null"); return this; } @Override public AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode) { this.options = Objects.requireNonNull(options, "options cannot be null"); this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null"); return this; } @Override public AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode, List aclList, Stat stat) { this.options = Objects.requireNonNull(options, "options cannot be null"); this.aclList = aclList; this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null"); this.stat = stat; return this; } @Override public AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode, List aclList, Stat stat, long ttl) { this.options = Objects.requireNonNull(options, "options cannot be null"); this.aclList = aclList; this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null"); this.stat = stat; this.ttl = ttl; return this; } @Override public AsyncPathAndBytesable> withOptions(Set options, CreateMode createMode, List aclList, Stat stat, long ttl, int setDataVersion) { this.options = Objects.requireNonNull(options, "options cannot be null"); this.aclList = aclList; this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null"); this.stat = stat; this.ttl = ttl; this.setDataVersion = setDataVersion; return this; } @Override public AsyncStage forPath(String path) { return internalForPath(path, null, false); } @Override public AsyncStage forPath(String path, byte[] data) { return internalForPath(path, data, true); } private AsyncStage internalForPath(String path, byte[] data, boolean useData) { BuilderCommon common = new BuilderCommon<>(filters, nameProc); CreateBuilderImpl builder = new CreateBuilderImpl(client, createMode, common.backgrounding, options.contains(CreateOption.createParentsIfNeeded) || options.contains(CreateOption.createParentsAsContainers), options.contains(CreateOption.createParentsAsContainers), options.contains(CreateOption.doProtected), options.contains(CreateOption.compress), options.contains(CreateOption.setDataIfExists), aclList, stat, ttl ); builder.setSetDataIfExistsVersion(setDataVersion); return safeCall(common.internalCallback, () -> useData ? builder.forPath(path, data) : builder.forPath(path)); } } AsyncCuratorFrameworkImpl.java000066400000000000000000000202451442004423600401720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import com.google.common.base.Preconditions; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.CuratorMultiTransactionImpl; import org.apache.curator.framework.imps.GetACLBuilderImpl; import org.apache.curator.framework.imps.SyncBuilderImpl; import org.apache.curator.utils.Compatibility; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.WatchMode; import org.apache.curator.x.async.api.*; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.Objects; import java.util.function.UnaryOperator; import static org.apache.curator.x.async.details.BackgroundProcs.*; public class AsyncCuratorFrameworkImpl implements AsyncCuratorFramework { private final CuratorFrameworkImpl client; private final Filters filters; private final WatchMode watchMode; private final boolean watched; public AsyncCuratorFrameworkImpl(CuratorFramework client) { this(reveal(client), new Filters(null, null, null), WatchMode.stateChangeAndSuccess, false); } private static CuratorFrameworkImpl reveal(CuratorFramework client) { try { return (CuratorFrameworkImpl)Objects.requireNonNull(client, "client cannot be null"); } catch ( Exception e ) { throw new IllegalArgumentException("Only Curator clients created through CuratorFrameworkFactory are supported: " + client.getClass().getName()); } } public AsyncCuratorFrameworkImpl(CuratorFrameworkImpl client, Filters filters, WatchMode watchMode, boolean watched) { this.client = Objects.requireNonNull(client, "client cannot be null"); this.filters = Objects.requireNonNull(filters, "filters cannot be null"); this.watchMode = Objects.requireNonNull(watchMode, "watchMode cannot be null"); this.watched = watched; } @Override public AsyncCreateBuilder create() { return new AsyncCreateBuilderImpl(client, filters); } @Override public AsyncDeleteBuilder delete() { return new AsyncDeleteBuilderImpl(client, filters); } @Override public AsyncSetDataBuilder setData() { return new AsyncSetDataBuilderImpl(client, filters); } @Override public AsyncGetACLBuilder getACL() { return new AsyncGetACLBuilder() { private Stat stat = null; @Override public AsyncPathable>> storingStatIn(Stat stat) { this.stat = stat; return this; } @Override public AsyncStage> forPath(String path) { BuilderCommon> common = new BuilderCommon<>(filters, aclProc); GetACLBuilderImpl builder = new GetACLBuilderImpl(client, common.backgrounding, stat); return safeCall(common.internalCallback, () -> builder.forPath(path)); } }; } @Override public AsyncSetACLBuilder setACL() { return new AsyncSetACLBuilderImpl(client, filters); } @Override public AsyncReconfigBuilder reconfig() { return new AsyncReconfigBuilderImpl(client, filters); } @Override public AsyncMultiTransaction transaction() { return operations -> { BuilderCommon> common = new BuilderCommon<>(filters, opResultsProc); CuratorMultiTransactionImpl builder = new CuratorMultiTransactionImpl(client, common.backgrounding); return safeCall(common.internalCallback, () -> builder.forOperations(operations)); }; } @Override public AsyncSyncBuilder sync() { return path -> { BuilderCommon common = new BuilderCommon<>(filters, ignoredProc); SyncBuilderImpl builder = new SyncBuilderImpl(client, common.backgrounding); return safeCall(common.internalCallback, () -> builder.forPath(path)); }; } @Override public AsyncRemoveWatchesBuilder removeWatches() { return new AsyncRemoveWatchesBuilderImpl(client, filters); } @Override public AsyncWatchBuilder addWatch() { Preconditions.checkState(Compatibility.hasPersistentWatchers(), "addWatch() is not supported in the ZooKeeper library being used."); return new AsyncWatchBuilderImpl(client, filters); } @Override public CuratorFramework unwrap() { return client; } @Override public WatchableAsyncCuratorFramework watched() { return new AsyncCuratorFrameworkImpl(client, filters, watchMode, true); } @Override public AsyncCuratorFrameworkDsl with(WatchMode mode) { return new AsyncCuratorFrameworkImpl(client, filters, mode, watched); } @Override public AsyncCuratorFrameworkDsl with(WatchMode mode, UnhandledErrorListener listener, UnaryOperator resultFilter, UnaryOperator watcherFilter) { return new AsyncCuratorFrameworkImpl(client, new Filters(listener, filters.getResultFilter(), filters.getWatcherFilter()), mode, watched); } @Override public AsyncCuratorFrameworkDsl with(UnhandledErrorListener listener) { return new AsyncCuratorFrameworkImpl(client, new Filters(listener, filters.getResultFilter(), filters.getWatcherFilter()), watchMode, watched); } @Override public AsyncCuratorFrameworkDsl with(UnaryOperator resultFilter, UnaryOperator watcherFilter) { return new AsyncCuratorFrameworkImpl(client, new Filters(filters.getListener(), resultFilter, watcherFilter), watchMode, watched); } @Override public AsyncCuratorFrameworkDsl with(UnhandledErrorListener listener, UnaryOperator resultFilter, UnaryOperator watcherFilter) { return new AsyncCuratorFrameworkImpl(client, new Filters(listener, resultFilter, watcherFilter), watchMode, watched); } @Override public AsyncTransactionOp transactionOp() { return new AsyncTransactionOpImpl(client); } @Override public AsyncExistsBuilder checkExists() { return new AsyncExistsBuilderImpl(client, filters, getBuilderWatchMode()); } @Override public AsyncGetDataBuilder getData() { return new AsyncGetDataBuilderImpl(client, filters, getBuilderWatchMode()); } @Override public AsyncGetChildrenBuilder getChildren() { return new AsyncGetChildrenBuilderImpl(client, filters, getBuilderWatchMode()); } @Override public AsyncGetConfigBuilder getConfig() { return new AsyncGetConfigBuilderImpl(client, filters, getBuilderWatchMode()); } Filters getFilters() { return filters; } CuratorFrameworkImpl getClient() { return client; } private WatchMode getBuilderWatchMode() { return watched ? watchMode : null; } } AsyncDeleteBuilderImpl.java000066400000000000000000000054461442004423600374140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.DeleteBuilderImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.api.AsyncDeleteBuilder; import org.apache.curator.x.async.api.AsyncPathable; import org.apache.curator.x.async.api.DeleteOption; import java.util.Collections; import java.util.Objects; import java.util.Set; import static org.apache.curator.x.async.details.BackgroundProcs.ignoredProc; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; class AsyncDeleteBuilderImpl implements AsyncDeleteBuilder { private final CuratorFrameworkImpl client; private final Filters filters; private Set options = Collections.emptySet(); private int version = -1; AsyncDeleteBuilderImpl(CuratorFrameworkImpl client, Filters filters) { this.client = client; this.filters = filters; } @Override public AsyncPathable> withOptions(Set options) { return withOptionsAndVersion(options, -1); } @Override public AsyncPathable> withOptionsAndVersion(Set options, int version) { this.options = Objects.requireNonNull(options, "options cannot be null"); this.version = version; return this; } @Override public AsyncPathable> withVersion(int version) { this.version = version; return this; } @Override public AsyncStage forPath(String path) { BuilderCommon common = new BuilderCommon<>(filters, ignoredProc); DeleteBuilderImpl builder = new DeleteBuilderImpl(client, version, common.backgrounding, options.contains(DeleteOption.deletingChildrenIfNeeded), options.contains(DeleteOption.guaranteed), options.contains(DeleteOption.quietly)); return safeCall(common.internalCallback, () -> builder.forPath(path)); } } AsyncExistsBuilderImpl.java000066400000000000000000000052021442004423600374570ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.ExistsBuilderImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.WatchMode; import org.apache.curator.x.async.api.AsyncExistsBuilder; import org.apache.curator.x.async.api.AsyncPathable; import org.apache.curator.x.async.api.ExistsOption; import org.apache.zookeeper.data.Stat; import java.util.Collections; import java.util.Objects; import java.util.Set; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; import static org.apache.curator.x.async.details.BackgroundProcs.safeStatProc; class AsyncExistsBuilderImpl implements AsyncExistsBuilder { private final CuratorFrameworkImpl client; private final Filters filters; private final WatchMode watchMode; private Set options = Collections.emptySet(); AsyncExistsBuilderImpl(CuratorFrameworkImpl client, Filters filters, WatchMode watchMode) { this.client = client; this.filters = filters; this.watchMode = watchMode; } @Override public AsyncPathable> withOptions(Set options) { this.options = Objects.requireNonNull(options, "options cannot be null"); return this; } @Override public AsyncStage forPath(String path) { BuilderCommon common = new BuilderCommon<>(filters, watchMode, safeStatProc); ExistsBuilderImpl builder = new ExistsBuilderImpl(client, common.backgrounding, common.watcher, options.contains(ExistsOption.createParentsIfNeeded), options.contains(ExistsOption.createParentsAsContainers) ); return safeCall(common.internalCallback, () -> builder.forPath(path)); } } AsyncGetChildrenBuilderImpl.java000066400000000000000000000045071442004423600403770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.GetChildrenBuilderImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.WatchMode; import org.apache.curator.x.async.api.AsyncGetChildrenBuilder; import org.apache.curator.x.async.api.AsyncPathable; import org.apache.zookeeper.data.Stat; import java.util.List; import static org.apache.curator.x.async.details.BackgroundProcs.childrenProc; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; class AsyncGetChildrenBuilderImpl implements AsyncGetChildrenBuilder { private final CuratorFrameworkImpl client; private final Filters filters; private final WatchMode watchMode; private Stat stat = null; AsyncGetChildrenBuilderImpl(CuratorFrameworkImpl client, Filters filters, WatchMode watchMode) { this.client = client; this.filters = filters; this.watchMode = watchMode; } @Override public AsyncStage> forPath(String path) { BuilderCommon> common = new BuilderCommon<>(filters, watchMode, childrenProc); GetChildrenBuilderImpl builder = new GetChildrenBuilderImpl(client, common.watcher, common.backgrounding, stat); return safeCall(common.internalCallback, () -> builder.forPath(path)); } @Override public AsyncPathable>> storingStatIn(Stat stat) { this.stat = stat; return this; } } AsyncGetConfigBuilderImpl.java000066400000000000000000000044001442004423600400440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.GetConfigBuilderImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.WatchMode; import org.apache.curator.x.async.api.AsyncEnsemblable; import org.apache.curator.x.async.api.AsyncGetConfigBuilder; import org.apache.zookeeper.data.Stat; import static org.apache.curator.x.async.details.BackgroundProcs.dataProc; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; class AsyncGetConfigBuilderImpl implements AsyncGetConfigBuilder { private final CuratorFrameworkImpl client; private final Filters filters; private final WatchMode watchMode; private Stat stat = null; AsyncGetConfigBuilderImpl(CuratorFrameworkImpl client, Filters filters, WatchMode watchMode) { this.client = client; this.filters = filters; this.watchMode = watchMode; } @Override public AsyncEnsemblable> storingStatIn(Stat stat) { this.stat = stat; return this; } @Override public AsyncStage forEnsemble() { BuilderCommon common = new BuilderCommon<>(filters, watchMode, dataProc); GetConfigBuilderImpl builder = new GetConfigBuilderImpl(client, common.backgrounding, common.watcher, stat); return safeCall(common.internalCallback, builder::forEnsemble); } } AsyncGetDataBuilderImpl.java000066400000000000000000000051641442004423600375200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.GetDataBuilderImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.WatchMode; import org.apache.curator.x.async.api.AsyncGetDataBuilder; import org.apache.curator.x.async.api.AsyncPathable; import org.apache.zookeeper.data.Stat; import static org.apache.curator.x.async.details.BackgroundProcs.dataProc; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; class AsyncGetDataBuilderImpl implements AsyncGetDataBuilder { private final CuratorFrameworkImpl client; private final Filters filters; private final WatchMode watchMode; private boolean decompressed = false; private Stat stat = null; AsyncGetDataBuilderImpl(CuratorFrameworkImpl client, Filters filters, WatchMode watchMode) { this.client = client; this.filters = filters; this.watchMode = watchMode; } @Override public AsyncPathable> decompressed() { decompressed = true; return this; } @Override public AsyncPathable> storingStatIn(Stat stat) { this.stat = stat; return this; } @Override public AsyncPathable> decompressedStoringStatIn(Stat stat) { decompressed = true; this.stat = stat; return this; } @Override public AsyncStage forPath(String path) { BuilderCommon common = new BuilderCommon<>(filters, watchMode, dataProc); GetDataBuilderImpl builder = new GetDataBuilderImpl(client, stat, common.watcher, common.backgrounding, decompressed); return safeCall(common.internalCallback, () -> builder.forPath(path)); } } AsyncReconfigBuilderImpl.java000066400000000000000000000101541442004423600377360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.ReconfigBuilderImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.api.AsyncEnsemblable; import org.apache.curator.x.async.api.AsyncReconfigBuilder; import org.apache.zookeeper.data.Stat; import java.util.List; import static org.apache.curator.x.async.details.BackgroundProcs.ignoredProc; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; class AsyncReconfigBuilderImpl implements AsyncReconfigBuilder, AsyncEnsemblable> { private final CuratorFrameworkImpl client; private final Filters filters; private Stat stat = null; private long fromConfig = -1; private List newMembers = null; private List joining = null; private List leaving = null; AsyncReconfigBuilderImpl(CuratorFrameworkImpl client, Filters filters) { this.client = client; this.filters = filters; } @Override public AsyncEnsemblable> withNewMembers(List servers) { this.newMembers = servers; return this; } @Override public AsyncEnsemblable> withJoiningAndLeaving(List joining, List leaving) { this.joining = joining; this.leaving = leaving; return this; } @Override public AsyncEnsemblable> withNewMembers(List servers, Stat stat) { this.newMembers = servers; this.stat = stat; return this; } @Override public AsyncEnsemblable> withJoiningAndLeaving(List joining, List leaving, Stat stat) { this.joining = joining; this.leaving = leaving; return this; } @Override public AsyncEnsemblable> withNewMembers(List servers, Stat stat, long fromConfig) { this.newMembers = servers; this.stat = stat; this.fromConfig = fromConfig; return this; } @Override public AsyncEnsemblable> withJoiningAndLeaving(List joining, List leaving, Stat stat, long fromConfig) { this.joining = joining; this.leaving = leaving; this.stat = stat; this.fromConfig = fromConfig; return this; } @Override public AsyncEnsemblable> withNewMembers(List servers, long fromConfig) { this.newMembers = servers; this.fromConfig = fromConfig; return this; } @Override public AsyncEnsemblable> withJoiningAndLeaving(List joining, List leaving, long fromConfig) { this.joining = joining; this.leaving = leaving; this.fromConfig = fromConfig; return this; } @Override public AsyncStage forEnsemble() { BuilderCommon common = new BuilderCommon<>(filters, ignoredProc); ReconfigBuilderImpl builder = new ReconfigBuilderImpl(client, common.backgrounding, stat, fromConfig, newMembers, joining, leaving); return safeCall(common.internalCallback, () -> { builder.forEnsemble(); return null; }); } } AsyncRemoveWatchesBuilderImpl.java000066400000000000000000000151521442004423600407610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.RemoveWatchesBuilderImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.api.AsyncPathable; import org.apache.curator.x.async.api.AsyncRemoveWatchesBuilder; import org.apache.curator.x.async.api.RemoveWatcherOption; import org.apache.zookeeper.Watcher; import java.util.Collections; import java.util.Objects; import java.util.Set; import static org.apache.curator.x.async.details.BackgroundProcs.ignoredProc; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; class AsyncRemoveWatchesBuilderImpl implements AsyncRemoveWatchesBuilder, AsyncPathable> { private final CuratorFrameworkImpl client; private final Filters filters; private Watcher.WatcherType watcherType = Watcher.WatcherType.Any; private Set options = Collections.emptySet(); private Watcher watcher = null; private CuratorWatcher curatorWatcher = null; AsyncRemoveWatchesBuilderImpl(CuratorFrameworkImpl client, Filters filters) { this.client = client; this.filters = filters; } @Override public AsyncPathable> removing(Watcher watcher) { this.watcher = Objects.requireNonNull(watcher, "watcher cannot be null"); this.curatorWatcher = null; return this; } @Override public AsyncPathable> removing(CuratorWatcher watcher) { this.curatorWatcher = Objects.requireNonNull(watcher, "watcher cannot be null"); this.watcher = null; return this; } @Override public AsyncPathable> removingAll() { this.curatorWatcher = null; this.watcher = null; return this; } @Override public AsyncPathable> removing(Watcher watcher, Set options) { this.watcher = Objects.requireNonNull(watcher, "watcher cannot be null"); this.options = Objects.requireNonNull(options, "options cannot be null"); this.curatorWatcher = null; return this; } @Override public AsyncPathable> removing(CuratorWatcher watcher, Set options) { this.curatorWatcher = Objects.requireNonNull(watcher, "watcher cannot be null"); this.options = Objects.requireNonNull(options, "options cannot be null"); this.watcher = null; return this; } @Override public AsyncPathable> removingAll(Set options) { this.options = Objects.requireNonNull(options, "options cannot be null"); this.curatorWatcher = null; this.watcher = null; return this; } @Override public AsyncPathable> removing(Watcher watcher, Watcher.WatcherType watcherType, Set options) { this.watcher = Objects.requireNonNull(watcher, "watcher cannot be null"); this.options = Objects.requireNonNull(options, "options cannot be null"); this.watcherType = Objects.requireNonNull(watcherType, "watcherType cannot be null"); this.curatorWatcher = null; return this; } @Override public AsyncPathable> removing(CuratorWatcher watcher, Watcher.WatcherType watcherType, Set options) { this.curatorWatcher = Objects.requireNonNull(watcher, "watcher cannot be null"); this.options = Objects.requireNonNull(options, "options cannot be null"); this.watcherType = Objects.requireNonNull(watcherType, "watcherType cannot be null"); this.watcher = null; return this; } @Override public AsyncPathable> removingAll(Watcher.WatcherType watcherType, Set options) { this.options = Objects.requireNonNull(options, "options cannot be null"); this.watcherType = Objects.requireNonNull(watcherType, "watcherType cannot be null"); this.curatorWatcher = null; this.watcher = null; return this; } @Override public AsyncPathable> removing(Watcher watcher, Watcher.WatcherType watcherType) { this.watcher = Objects.requireNonNull(watcher, "watcher cannot be null"); this.watcherType = Objects.requireNonNull(watcherType, "watcherType cannot be null"); this.curatorWatcher = null; return this; } @Override public AsyncPathable> removing(CuratorWatcher watcher, Watcher.WatcherType watcherType) { this.curatorWatcher = Objects.requireNonNull(watcher, "watcher cannot be null"); this.watcherType = Objects.requireNonNull(watcherType, "watcherType cannot be null"); this.watcher = null; return this; } @Override public AsyncPathable> removingAll(Watcher.WatcherType watcherType) { this.watcherType = Objects.requireNonNull(watcherType, "watcherType cannot be null"); this.curatorWatcher = null; this.watcher = null; return this; } @Override public AsyncStage forPath(String path) { BuilderCommon common = new BuilderCommon<>(filters, ignoredProc); RemoveWatchesBuilderImpl builder = new RemoveWatchesBuilderImpl(client, watcher, curatorWatcher, watcherType, options.contains(RemoveWatcherOption.guaranteed), options.contains(RemoveWatcherOption.local), options.contains(RemoveWatcherOption.guaranteed), common.backgrounding ); return safeCall(common.internalCallback, () -> builder.forPath(path)); } } AsyncResultImpl.java000066400000000000000000000071241442004423600361540ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.x.async.AsyncResult; import org.apache.zookeeper.KeeperException; import java.util.Objects; import java.util.Optional; public class AsyncResultImpl implements AsyncResult { private final T value; private final KeeperException.Code code; private final Throwable exception; public AsyncResultImpl() { this(null, KeeperException.Code.OK, null); } public AsyncResultImpl(KeeperException.Code code) { this(null, code, null); } public AsyncResultImpl(T value) { this(value, KeeperException.Code.OK, null); } public AsyncResultImpl(Throwable exception) { this(null, KeeperException.Code.SYSTEMERROR, exception); } private AsyncResultImpl(T value, KeeperException.Code code, Throwable exception) { this.value = value; this.exception = exception; this.code = Objects.requireNonNull(code, "error cannot be null"); } public T getRawValue() { return value; } public Optional getValue() { return Optional.ofNullable(value); } public KeeperException.Code getCode() { return code; } public Throwable getRawException() { return exception; } public Optional getException() { return Optional.ofNullable(exception); } public void checkException() { if ( exception != null ) { throw new RuntimeException(exception); } } @Override public void checkError() { checkException(); if ( code != KeeperException.Code.OK ) { throw new RuntimeException(KeeperException.create(code)); } } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } AsyncResultImpl that = (AsyncResultImpl)o; if ( value != null ? !value.equals(that.value) : that.value != null ) { return false; } //noinspection SimplifiableIfStatement if ( code != that.code ) { return false; } return exception != null ? exception.equals(that.exception) : that.exception == null; } @Override public int hashCode() { int result = value != null ? value.hashCode() : 0; result = 31 * result + code.hashCode(); result = 31 * result + (exception != null ? exception.hashCode() : 0); return result; } @Override public String toString() { return "AsyncResult{" + "value=" + value + ", code=" + code + ", exception=" + exception + '}'; } } AsyncSetACLBuilderImpl.java000066400000000000000000000046401442004423600372600ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.SetACLBuilderImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.api.AsyncPathable; import org.apache.curator.x.async.api.AsyncSetACLBuilder; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; import static org.apache.curator.x.async.details.BackgroundProcs.statProc; class AsyncSetACLBuilderImpl implements AsyncSetACLBuilder, AsyncPathable> { private final CuratorFrameworkImpl client; private final Filters filters; private int version = -1; private List aclList = null; AsyncSetACLBuilderImpl(CuratorFrameworkImpl client, Filters filters) { this.client = client; this.filters = filters; } @Override public AsyncPathable> withACL(List aclList) { this.aclList = aclList; return this; } @Override public AsyncPathable> withACL(List aclList, int version) { this.aclList = aclList; this.version = version; return this; } @Override public AsyncStage forPath(String path) { BuilderCommon common = new BuilderCommon<>(filters, statProc); SetACLBuilderImpl builder = new SetACLBuilderImpl(client, common.backgrounding, aclList, version); return safeCall(common.internalCallback, () -> builder.forPath(path)); } } AsyncSetDataBuilderImpl.java000066400000000000000000000054601442004423600375330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.SetDataBuilderImpl; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.api.AsyncPathAndBytesable; import org.apache.curator.x.async.api.AsyncSetDataBuilder; import org.apache.zookeeper.data.Stat; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; import static org.apache.curator.x.async.details.BackgroundProcs.statProc; class AsyncSetDataBuilderImpl implements AsyncSetDataBuilder { private final CuratorFrameworkImpl client; private final Filters filters; private boolean compressed = false; private int version = -1; AsyncSetDataBuilderImpl(CuratorFrameworkImpl client, Filters filters) { this.client = client; this.filters = filters; } @Override public AsyncStage forPath(String path) { return internalForPath(path, null, false); } @Override public AsyncStage forPath(String path, byte[] data) { return internalForPath(path, data, true); } @Override public AsyncPathAndBytesable> compressed() { compressed = true; return this; } @Override public AsyncPathAndBytesable> compressedWithVersion(int version) { compressed = true; this.version = version; return this; } @Override public AsyncPathAndBytesable> withVersion(int version) { this.version = version; return this; } private AsyncStage internalForPath(String path, byte[] data, boolean useData) { BuilderCommon common = new BuilderCommon<>(filters, statProc); SetDataBuilderImpl builder = new SetDataBuilderImpl(client, common.backgrounding, version, compressed); return safeCall(common.internalCallback, () -> useData ? builder.forPath(path, data) : builder.forPath(path)); } } AsyncTransactionOpImpl.java000066400000000000000000000205331442004423600374610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.api.ACLPathAndBytesable; import org.apache.curator.framework.api.PathAndBytesable; import org.apache.curator.framework.api.VersionPathAndBytesable; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.TransactionCreateBuilder2; import org.apache.curator.framework.api.transaction.TransactionSetDataBuilder; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.x.async.api.AsyncPathAndBytesable; import org.apache.curator.x.async.api.AsyncPathable; import org.apache.curator.x.async.api.AsyncTransactionCheckBuilder; import org.apache.curator.x.async.api.AsyncTransactionCreateBuilder; import org.apache.curator.x.async.api.AsyncTransactionDeleteBuilder; import org.apache.curator.x.async.api.AsyncTransactionOp; import org.apache.curator.x.async.api.AsyncTransactionSetDataBuilder; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.ACL; import java.util.List; import java.util.Objects; class AsyncTransactionOpImpl implements AsyncTransactionOp { private final CuratorFrameworkImpl client; AsyncTransactionOpImpl(CuratorFrameworkImpl client) { this.client = client; } @Override public AsyncTransactionCreateBuilder create() { return new AsyncTransactionCreateBuilder() { private List aclList = null; private CreateMode createMode = CreateMode.PERSISTENT; private boolean compressed = false; private long ttl = -1; @Override public AsyncPathAndBytesable withMode(CreateMode createMode) { this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null"); return this; } @Override public AsyncPathAndBytesable withACL(List aclList) { this.aclList = aclList; return this; } @Override public AsyncPathAndBytesable compressed() { compressed = true; return this; } @Override public AsyncPathAndBytesable withTtl(long ttl) { this.ttl = ttl; return this; } @Override public AsyncPathAndBytesable withOptions(CreateMode createMode, List aclList, boolean compressed) { return withOptions(createMode, aclList, compressed, ttl); } @Override public AsyncPathAndBytesable withOptions(CreateMode createMode, List aclList, boolean compressed, long ttl) { this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null"); this.aclList = aclList; this.compressed = compressed; this.ttl = ttl; return this; } @Override public CuratorOp forPath(String path, byte[] data) { return internalForPath(path, data, true); } @Override public CuratorOp forPath(String path) { return internalForPath(path, null, false); } private CuratorOp internalForPath(String path, byte[] data, boolean useData) { TransactionCreateBuilder2 builder1 = (ttl > 0) ? client.transactionOp().create().withTtl(ttl) : client.transactionOp().create(); ACLPathAndBytesable builder2 = compressed ? builder1.compressed().withMode(createMode) : builder1.withMode(createMode); PathAndBytesable builder3 = builder2.withACL(aclList); try { return useData ? builder3.forPath(path, data) : builder3.forPath(path); } catch ( Exception e ) { throw new RuntimeException(e); // should never happen } } }; } @Override public AsyncTransactionDeleteBuilder delete() { return new AsyncTransactionDeleteBuilder() { private int version = -1; @Override public AsyncPathable withVersion(int version) { this.version = version; return this; } @Override public CuratorOp forPath(String path) { try { return client.transactionOp().delete().withVersion(version).forPath(path); } catch ( Exception e ) { throw new RuntimeException(e); // should never happen } } }; } @Override public AsyncTransactionSetDataBuilder setData() { return new AsyncTransactionSetDataBuilder() { private int version = -1; private boolean compressed = false; @Override public AsyncPathAndBytesable withVersion(int version) { this.version = version; return this; } @Override public AsyncPathAndBytesable compressed() { compressed = true; return this; } @Override public AsyncPathAndBytesable withVersionCompressed(int version) { this.version = version; compressed = true; return this; } @Override public CuratorOp forPath(String path, byte[] data) { return internalForPath(path, data, true); } @Override public CuratorOp forPath(String path) { return internalForPath(path, null, false); } private CuratorOp internalForPath(String path, byte[] data, boolean useData) { TransactionSetDataBuilder builder1 = client.transactionOp().setData(); VersionPathAndBytesable builder2 = compressed ? builder1.compressed() : builder1; PathAndBytesable builder3 = builder2.withVersion(version); try { return useData ? builder3.forPath(path, data) : builder3.forPath(path); } catch ( Exception e ) { throw new RuntimeException(e); // should never happen } } }; } @Override public AsyncTransactionCheckBuilder check() { return new AsyncTransactionCheckBuilder() { private int version = -1; @Override public AsyncPathable withVersion(int version) { this.version = version; return this; } @Override public CuratorOp forPath(String path) { try { return client.transactionOp().check().withVersion(version).forPath(path); } catch ( Exception e ) { throw new RuntimeException(e); // should never happen } } }; } } AsyncWatchBuilderImpl.java000066400000000000000000000056051442004423600372550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.api.CuratorWatcher; import org.apache.curator.framework.api.WatchableBase; import org.apache.curator.framework.imps.AddWatchBuilderImpl; import org.apache.curator.framework.imps.CuratorFrameworkImpl; import org.apache.curator.framework.imps.Watching; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.api.AsyncPathable; import org.apache.curator.x.async.api.AsyncWatchBuilder; import org.apache.curator.x.async.api.AsyncWatchBuilder2; import org.apache.zookeeper.AddWatchMode; import org.apache.zookeeper.Watcher; import static org.apache.curator.x.async.details.BackgroundProcs.ignoredProc; import static org.apache.curator.x.async.details.BackgroundProcs.safeCall; class AsyncWatchBuilderImpl implements AsyncWatchBuilder, AsyncWatchBuilder2, WatchableBase>>, AsyncPathable> { private final CuratorFrameworkImpl client; private final Filters filters; private Watching watching; private AddWatchMode mode = AddWatchMode.PERSISTENT_RECURSIVE; AsyncWatchBuilderImpl(CuratorFrameworkImpl client, Filters filters) { this.client = client; this.filters = filters; watching = new Watching(client, true); } @Override public AsyncWatchBuilder2 withMode(AddWatchMode mode) { this.mode = mode; return this; } @Override public AsyncPathable> usingWatcher(Watcher watcher) { watching = new Watching(client, watcher); return this; } @Override public AsyncPathable> usingWatcher(CuratorWatcher watcher) { watching = new Watching(client, watcher); return this; } @Override public AsyncStage forPath(String path) { BuilderCommon common = new BuilderCommon<>(filters, ignoredProc); AddWatchBuilderImpl builder = new AddWatchBuilderImpl(client, watching, common.backgrounding, mode); return safeCall(common.internalCallback, () -> builder.forPath(path)); } }BackgroundProc.java000066400000000000000000000020771442004423600357630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.api.CuratorEvent; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; interface BackgroundProc extends BiFunction, Void> { } BackgroundProcs.java000066400000000000000000000061751442004423600361510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.concurrent.Callable; import java.util.function.Function; class BackgroundProcs { static final BackgroundProc nameProc = makeProc(CuratorEvent::getName); static final BackgroundProc pathProc = makeProc(CuratorEvent::getPath); static final BackgroundProc ignoredProc = makeProc(e -> null); static final BackgroundProc dataProc = makeProc(CuratorEvent::getData); static final BackgroundProc statProc = makeProc(CuratorEvent::getStat); static final BackgroundProc safeStatProc = (event, future) -> { if ( (event.getResultCode() == 0) || (event.getResultCode() == KeeperException.Code.NONODE.intValue()) ) { future.complete(event.getStat()); } else { future.completeExceptionally(KeeperException.create(KeeperException.Code.get(event.getResultCode()), event.getPath())); } return null; }; static final BackgroundProc> childrenProc = makeProc(CuratorEvent::getChildren); static final BackgroundProc> aclProc = makeProc(CuratorEvent::getACLList); static final BackgroundProc> opResultsProc = makeProc(CuratorEvent::getOpResults); static BackgroundProc makeProc(Function proc) { return (event, future) -> { if ( event.getResultCode() == 0 ) { future.complete(proc.apply(event)); } else { future.completeExceptionally(KeeperException.create(KeeperException.Code.get(event.getResultCode()), event.getPath())); } return null; }; } static InternalCallback safeCall(InternalCallback callback, Callable proc) { try { proc.call(); } catch ( Exception e ) { callback.toCompletableFuture().completeExceptionally(e); } return callback; } BackgroundProcs() { } } BuilderCommon.java000066400000000000000000000030641442004423600356140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.imps.Backgrounding; import org.apache.curator.x.async.WatchMode; class BuilderCommon { final InternalCallback internalCallback; final Backgrounding backgrounding; final InternalWatcher watcher; BuilderCommon(Filters filters, BackgroundProc proc) { this(filters,null, proc); } BuilderCommon(Filters filters, WatchMode watchMode, BackgroundProc proc) { watcher = (watchMode != null) ? new InternalWatcher(watchMode, filters.getWatcherFilter()) : null; internalCallback = new InternalCallback<>(proc, watcher, filters.getResultFilter()); backgrounding = new Backgrounding(internalCallback, filters.getListener()); } } Filters.java000066400000000000000000000034211442004423600344620ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.zookeeper.WatchedEvent; import java.util.function.UnaryOperator; public class Filters { private final UnhandledErrorListener listener; private final UnaryOperator resultFilter; private final UnaryOperator watcherFilter; public Filters(UnhandledErrorListener listener, UnaryOperator resultFilter, UnaryOperator watcherFilter) { this.listener = listener; this.resultFilter = resultFilter; this.watcherFilter = watcherFilter; } public UnhandledErrorListener getListener() { return listener; } public UnaryOperator getResultFilter() { return resultFilter; } public UnaryOperator getWatcherFilter() { return watcherFilter; } } InternalCallback.java000066400000000000000000000041271442004423600362470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.WatchedEvent; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.UnaryOperator; class InternalCallback extends CompletableFuture implements BackgroundCallback, AsyncStage { private final BackgroundProc resultFunction; private final InternalWatcher watcher; private final UnaryOperator resultFilter; InternalCallback(BackgroundProc resultFunction, InternalWatcher watcher, UnaryOperator resultFilter) { this.resultFunction = resultFunction; this.watcher = watcher; this.resultFilter = resultFilter; } @Override public CompletionStage event() { return (watcher != null) ? watcher.getFuture() : null; } @Override public void processResult(CuratorFramework client, CuratorEvent event) throws Exception { event = (resultFilter != null) ? resultFilter.apply(event) : event; resultFunction.apply(event, this); } } InternalWatcher.java000066400000000000000000000065771442004423600361630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.details; import com.google.common.base.Preconditions; import org.apache.curator.x.async.AsyncEventException; import org.apache.curator.x.async.WatchMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.UnaryOperator; class InternalWatcher implements Watcher { private final WatchMode watchMode; private final UnaryOperator watcherFilter; private volatile CompletableFuture future = new CompletableFuture<>(); InternalWatcher(WatchMode watchMode, UnaryOperator watcherFilter) { this.watchMode = watchMode; this.watcherFilter = watcherFilter; } CompletableFuture getFuture() { return future; } @Override public void process(WatchedEvent event) { final WatchedEvent localEvent = (watcherFilter != null) ? watcherFilter.apply(event) : event; switch ( localEvent.getState() ) { default: { if ( (watchMode != WatchMode.stateChangeOnly) && (localEvent.getType() != Event.EventType.None) ) { if ( !future.complete(localEvent) ) { future.obtrudeValue(localEvent); } } break; } case Disconnected: case AuthFailed: case Expired: { if ( watchMode != WatchMode.successOnly ) { AsyncEventException exception = new AsyncEventException() { private final AtomicBoolean isReset = new AtomicBoolean(false); @Override public Event.KeeperState getKeeperState() { return localEvent.getState(); } @Override public CompletionStage reset() { Preconditions.checkState(isReset.compareAndSet(false, true), "Already reset"); future = new CompletableFuture<>(); return future; } }; future.completeExceptionally(exception); } break; } } } } curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/migrations/000077500000000000000000000000001442004423600330155ustar00rootroot00000000000000Migration.java000066400000000000000000000023601442004423600355330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/migrations/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.migrations; import org.apache.curator.framework.api.transaction.CuratorOp; import java.util.List; /** * Models a single migration/transition */ @FunctionalInterface public interface Migration { /** * Return the operations to execute in a transaction. IMPORTANT: during a migration * this method may be called multiple times. * * @return operations */ List operations(); } MigrationException.java000066400000000000000000000023501442004423600374110ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/migrations/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.migrations; import java.util.Objects; public class MigrationException extends RuntimeException { private final String migrationId; public MigrationException(String migrationId, String message) { super(message); this.migrationId = Objects.requireNonNull(migrationId, "migrationId cannot be null"); } public String getMigrationId() { return migrationId; } } MigrationManager.java000066400000000000000000000201151442004423600370240ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/migrations/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.migrations; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.imps.ExtractingCuratorOp; import org.apache.curator.framework.recipes.locks.InterProcessLock; import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex; import org.apache.curator.utils.ZKPaths; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.zookeeper.CreateMode; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import static org.apache.curator.x.async.AsyncWrappers.*; /** * Manages migrations */ public class MigrationManager { private final AsyncCuratorFramework client; private final String lockPath; private final String metaDataPath; private final Executor executor; private final Duration lockMax; private static final String META_DATA_NODE_NAME = "meta-"; /** * @param client the curator client * @param lockPath base path for locks used by the manager * @param metaDataPath base path to store the meta data * @param executor the executor to use * @param lockMax max time to wait for locks */ public MigrationManager(AsyncCuratorFramework client, String lockPath, String metaDataPath, Executor executor, Duration lockMax) { this.client = Objects.requireNonNull(client, "client cannot be null"); this.lockPath = Objects.requireNonNull(lockPath, "lockPath cannot be null"); this.metaDataPath = Objects.requireNonNull(metaDataPath, "metaDataPath cannot be null"); this.executor = Objects.requireNonNull(executor, "executor cannot be null"); this.lockMax = Objects.requireNonNull(lockMax, "lockMax cannot be null"); } /** * Process the given migration set * * @param set the set * @return completion stage. If there is a migration-specific error, the stage will be completed * exceptionally with {@link org.apache.curator.x.async.migrations.MigrationException}. */ public CompletionStage migrate(MigrationSet set) { InterProcessLock lock = new InterProcessSemaphoreMutex(client.unwrap(), ZKPaths.makePath(lockPath, set.id())); CompletionStage lockStage = lockAsync(lock, lockMax.toMillis(), TimeUnit.MILLISECONDS, executor); return lockStage.thenCompose(__ -> runMigrationInLock(lock, set)); } /** * Can be overridden to change how the comparison to previous migrations is done. The default * version ensures that the meta data from previous migrations matches the current migration * set exactly (by order and version). If there is a mismatch, MigrationException is thrown. * * @param set the migration set being applied * @param operationHashesInOrder previous operation hashes (may be empty) * @return the list of actual migrations to perform. The filter can return any value here or an empty list. * @throws MigrationException errors */ protected List filter(MigrationSet set, List operationHashesInOrder) throws MigrationException { if ( operationHashesInOrder.size() > set.migrations().size() ) { throw new MigrationException(set.id(), String.format("More metadata than migrations. Migration ID: %s", set.id())); } int compareSize = Math.min(set.migrations().size(), operationHashesInOrder.size()); for ( int i = 0; i < compareSize; ++i ) { byte[] setHash = hash(set.migrations().get(i).operations()); if ( !Arrays.equals(setHash, operationHashesInOrder.get(i)) ) { throw new MigrationException(set.id(), String.format("Metadata mismatch. Migration ID: %s", set.id())); } } return set.migrations().subList(operationHashesInOrder.size(), set.migrations().size()); } private byte[] hash(List operations) { MessageDigest digest; try { digest = MessageDigest.getInstance("SHA-256"); } catch ( NoSuchAlgorithmException e ) { throw new RuntimeException(e); } operations.forEach(op -> { if ( op instanceof ExtractingCuratorOp ) { ((ExtractingCuratorOp)op).addToDigest(digest); } else { digest.update(op.toString().getBytes()); } }); return digest.digest(); } private CompletionStage runMigrationInLock(InterProcessLock lock, MigrationSet set) { String thisMetaDataPath = ZKPaths.makePath(metaDataPath, set.id()); return childrenWithData(client, thisMetaDataPath) .thenCompose(metaData -> applyMetaData(set, metaData, thisMetaDataPath)) .handle((v, e) -> { release(lock, true); if ( e != null ) { Throwables.propagate(e); } return v; } ); } private CompletionStage applyMetaData(MigrationSet set, Map metaData, String thisMetaDataPath) { List sortedMetaData = metaData.keySet() .stream() .sorted(Comparator.naturalOrder()) .map(metaData::get) .collect(Collectors.toList()); List toBeApplied; try { toBeApplied = filter(set, sortedMetaData); } catch ( MigrationException e ) { CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally(e); return future; } if ( toBeApplied.size() == 0 ) { return CompletableFuture.completedFuture(null); } return asyncEnsureContainers(client, thisMetaDataPath) .thenCompose(__ -> applyMetaDataAfterEnsure(toBeApplied, thisMetaDataPath)); } @VisibleForTesting volatile AtomicInteger debugCount = null; private CompletionStage applyMetaDataAfterEnsure(List toBeApplied, String thisMetaDataPath) { if ( debugCount != null ) { debugCount.incrementAndGet(); } List operations = new ArrayList<>(); String metaDataBasePath = ZKPaths.makePath(thisMetaDataPath, META_DATA_NODE_NAME); toBeApplied.forEach(migration -> { List thisMigrationOperations = migration.operations(); operations.addAll(thisMigrationOperations); operations.add(client.transactionOp().create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(metaDataBasePath, hash(thisMigrationOperations))); }); return client.transaction().forOperations(operations).thenApply(__ -> null); } } MigrationSet.java000066400000000000000000000034321442004423600362100ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/migrations/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.migrations; import com.google.common.collect.ImmutableList; import java.util.List; import java.util.Objects; /** * Models a set of migrations. Each individual migration is applied * in a transaction. */ public interface MigrationSet { /** * @return the unique ID for this migration set */ String id(); /** * @return list of migrations in the order that they should be applied */ List migrations(); static MigrationSet build(String id, List migrations) { Objects.requireNonNull(id, "id cannot be null"); final List migrationsCopy = ImmutableList.copyOf(migrations); return new MigrationSet() { @Override public String id() { return id; } @Override public List migrations() { return migrationsCopy; } }; } } curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/000077500000000000000000000000001442004423600322525ustar00rootroot00000000000000JacksonModelSerializer.java000066400000000000000000000077231442004423600374520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; import java.io.IOException; import java.util.Arrays; import java.util.Objects; /** * Model serializer that uses Jackson for JSON serialization. IMPORTANT: * the jackson dependency is specified as provided in the curator-x-async Maven POM * file to avoid adding a new dependency to Curator. Therefore, if you wish to use the * JacksonModelSerializer you must manually add the dependency to your build system */ public class JacksonModelSerializer implements ModelSerializer { private static final ObjectMapper mapper = new ObjectMapper(); static { mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } private final ObjectReader reader; private final ObjectWriter writer; public static JacksonModelSerializer build(Class modelClass) { return new JacksonModelSerializer<>(modelClass); } public static JacksonModelSerializer build(JavaType type) { return new JacksonModelSerializer<>(type); } public static JacksonModelSerializer build(TypeReference type) { return new JacksonModelSerializer<>(type); } public JacksonModelSerializer(Class modelClass) { this(mapper.getTypeFactory().constructType(modelClass)); } public JacksonModelSerializer(JavaType type) { reader = mapper.readerFor(type); writer = mapper.writerFor(type); } public JacksonModelSerializer(TypeReference type) { reader = mapper.readerFor(type); writer = mapper.writerFor(type); } public JacksonModelSerializer(ObjectMapper mapper, JavaType type) { reader = mapper.readerFor(type); writer = mapper.writerFor(type); } public JacksonModelSerializer(ObjectMapper mapper, TypeReference type) { reader = mapper.readerFor(type); writer = mapper.writerFor(type); } public JacksonModelSerializer(ObjectReader reader, ObjectWriter writer) { this.reader = Objects.requireNonNull(reader, "reader cannot be null"); this.writer = Objects.requireNonNull(writer, "writer cannot be null"); } @Override public byte[] serialize(T model) { try { return writer.writeValueAsBytes(model); } catch ( JsonProcessingException e ) { throw new RuntimeException(String.format("Could not serialize value: %s", model), e); } } @Override public T deserialize(byte[] bytes) { try { return reader.readValue(bytes); } catch ( IOException e ) { throw new RuntimeException(String.format("Could not deserialize value: %s", Arrays.toString(bytes)), e); } } } ModelSerializer.java000066400000000000000000000033131442004423600361300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; /** * Serializing interface for models */ public interface ModelSerializer { /** * Given a model return the serialized bytes * * @param model model * @return bytes */ byte[] serialize(T model); /** * Given bytes serialized via {@link #serialize(Object)} return * the model * * @param bytes serialized bytes * @return model * @throws RuntimeException if bytes is invalid or there was an error deserializing */ T deserialize(byte[] bytes); /** * A pass through serializer */ ModelSerializer raw = new ModelSerializer() { @Override public byte[] serialize(byte[] model) { return model; } @Override public byte[] deserialize(byte[] bytes) { return bytes; } }; } ModelSpec.java000066400000000000000000000161021442004423600347110ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import com.google.common.collect.ImmutableSet; import org.apache.curator.framework.schema.Schema; import org.apache.curator.x.async.api.CreateOption; import org.apache.curator.x.async.api.DeleteOption; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.ACL; import java.util.List; import java.util.Set; /** * A full specification for dealing with a portion of the ZooKeeper tree. ModelSpec's contain: * *
    *
  • A node path
  • *
  • Serializer for the data stored
  • *
  • Options for how to create the node (mode, compression, etc.)
  • *
  • Options for how to deleting the node (quietly, guaranteed, etc.)
  • *
  • ACLs
  • *
  • Optional schema generation
  • *
*/ public interface ModelSpec extends Resolvable { Set defaultCreateOptions = ImmutableSet.of(CreateOption.createParentsAsContainers, CreateOption.setDataIfExists); Set defaultDeleteOptions = ImmutableSet.of(DeleteOption.guaranteed); /** * Start a new ModelSpecBuilder for the given path and serializer. The returned ModelSpecBuilder * uses {@link #defaultCreateOptions} and {@link #defaultDeleteOptions}, but you can change these * with builder methods. * * @param path path to model * @param serializer the model's serializer * @return builder */ static ModelSpecBuilder builder(ZPath path, ModelSerializer serializer) { return new ModelSpecBuilder<>(path, serializer) .withCreateOptions(defaultCreateOptions) .withDeleteOptions(defaultDeleteOptions); } /** * Start a new ModelSpecBuilder for the given serializer. The returned ModelSpecBuilder * uses {@link #defaultCreateOptions} and {@link #defaultDeleteOptions}, but you can change these * with builder methods. You must set a path before calling {@link ModelSpecBuilder#build()} * * @param serializer the model's serializer * @return builder */ static ModelSpecBuilder builder(ModelSerializer serializer) { return new ModelSpecBuilder<>(serializer) .withCreateOptions(defaultCreateOptions) .withDeleteOptions(defaultDeleteOptions); } /** *

* Return a new CuratorModel instance with all the same options but applying to the given child node of this CuratorModel's * path. E.g. if this CuratorModel instance applies to "/a/b", calling modeled.at("c") returns an instance that applies to * "/a/b/c". *

* *

* The replacement is the toString() value of child or, * if it implements {@link org.apache.curator.x.async.modeled.NodeName}, * the value of nodeName(). *

* * @param child child node. * @return new Modeled Spec instance */ ModelSpec child(Object child); /** *

* Return a new CuratorModel instance with all the same options but applying to the parent node of this CuratorModel's * path. E.g. if this CuratorModel instance applies to "/a/b/c", calling modeled.parent() returns an instance that applies to * "/a/b". *

* *

* The replacement is the toString() value of child or, * if it implements {@link org.apache.curator.x.async.modeled.NodeName}, * the value of nodeName(). *

* * @return new Modeled Spec instance */ ModelSpec parent(); /** * Return a new CuratorModel instance with all the same options but using the given path. * * @param path new path * @return new Modeled Spec instance */ ModelSpec withPath(ZPath path); /** *

* Return a new CuratorModel instance with all the same options but using a resolved * path by calling {@link org.apache.curator.x.async.modeled.ZPath#resolved(Object...)} * using the given parameters *

* *

* The replacement is the toString() value of the parameter object or, * if the object implements {@link org.apache.curator.x.async.modeled.NodeName}, * the value of nodeName(). *

* * @param parameters list of replacements. Must have be the same length as the number of * parameter nodes in the path * @return new resolved ModelSpec */ @Override ModelSpec resolved(Object... parameters); /** *

* Return a new CuratorModel instance with all the same options but using a resolved * path by calling {@link org.apache.curator.x.async.modeled.ZPath#resolved(java.util.List)} * using the given parameters *

* *

* The replacement is the toString() value of the parameter object or, * if the object implements {@link org.apache.curator.x.async.modeled.NodeName}, * the value of nodeName(). *

* * @param parameters list of replacements. Must have be the same length as the number of * parameter nodes in the path * @return new resolved ModelSpec */ @Override ModelSpec resolved(List parameters); /** * Return the model's path * * @return path */ ZPath path(); /** * Return the model's serializer * * @return serializer */ ModelSerializer serializer(); /** * Return the model's create mode * * @return create mode */ CreateMode createMode(); /** * Return the model's ACL list * * @return ACL list */ List aclList(); /** * Return the model's create options * * @return create options */ Set createOptions(); /** * Return the model's delete options * * @return delete options */ Set deleteOptions(); /** * Return the TTL to use or -1 * * @return ttl */ long ttl(); /** * Return a Curator schema that validates ZNodes at this model's * path using this model's values * * @return schema */ Schema schema(); } ModelSpecBuilder.java000066400000000000000000000106531442004423600362250ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import com.google.common.collect.ImmutableSet; import org.apache.curator.x.async.api.CreateOption; import org.apache.curator.x.async.api.DeleteOption; import org.apache.curator.x.async.modeled.details.ModelSpecImpl; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.ACL; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; public class ModelSpecBuilder { private final ModelSerializer serializer; private ZPath path; private CreateMode createMode = CreateMode.PERSISTENT; private List aclList = Collections.emptyList(); private Set createOptions = Collections.emptySet(); private Set deleteOptions = Collections.emptySet(); private long ttl = -1; /** * Build a new ModelSpec instance * * @return new ModelSpec instance */ public ModelSpec build() { return new ModelSpecImpl<>(path, serializer, createMode, aclList, createOptions, deleteOptions, ttl); } /** * Use the given createMode for create operations on the Modeled Curator's ZNode * * @param createMode create mode * @return this for chaining */ public ModelSpecBuilder withCreateMode(CreateMode createMode) { this.createMode = createMode; return this; } /** * Specify a TTL when mode is {@link org.apache.zookeeper.CreateMode#PERSISTENT_WITH_TTL} or * {@link org.apache.zookeeper.CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If * the znode has not been modified within the given TTL, it will be deleted once it has no * children. The TTL unit is milliseconds and must be greater than 0 and less than or equal to * EphemeralType.MAX_TTL. * * @param ttl the ttl * @return this for chaining */ public ModelSpecBuilder withTtl(long ttl) { this.ttl = ttl; return this; } /** * Use the given aclList for create operations on the Modeled Curator's ZNode * * @param aclList ACLs * @return this for chaining */ public ModelSpecBuilder withAclList(List aclList) { this.aclList = aclList; return this; } /** * Use the given create options on the Modeled Curator's ZNode * * @param createOptions options * @return this for chaining */ public ModelSpecBuilder withCreateOptions(Set createOptions) { this.createOptions = (createOptions != null) ? ImmutableSet.copyOf(createOptions) : null; return this; } /** * Use the given delete options on the Modeled Curator's ZNode * * @param deleteOptions options * @return this for chaining */ public ModelSpecBuilder withDeleteOptions(Set deleteOptions) { this.deleteOptions = (deleteOptions != null) ? ImmutableSet.copyOf(deleteOptions) : null; return this; } /** * Change the model spec's path * * @param path new path * @return this for chaining */ public ModelSpecBuilder withPath(ZPath path) { this.path = Objects.requireNonNull(path, "path cannot be null"); return this; } ModelSpecBuilder(ModelSerializer serializer) { this.serializer = Objects.requireNonNull(serializer, "serializer cannot be null"); } ModelSpecBuilder(ZPath path, ModelSerializer serializer) { this.path = Objects.requireNonNull(path, "path cannot be null"); this.serializer = Objects.requireNonNull(serializer, "serializer cannot be null"); } } ModeledFramework.java000066400000000000000000000312511442004423600362670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.modeled.cached.CachedModeledFramework; import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.concurrent.ExecutorService; public interface ModeledFramework { /** * Return a new ModeledFramework for the given model * * @param client Curator client * @param model the model * @return new Modeled Curator instance */ static ModeledFramework wrap(AsyncCuratorFramework client, ModelSpec model) { return builder(client, model).build(); } /** * Start a new ModeledFrameworkBuilder for the given model * * @param client Curator client * @param model the model * @return builder */ static ModeledFrameworkBuilder builder(AsyncCuratorFramework client, ModelSpec model) { return new ModeledFrameworkBuilder<>(client, model); } /** * Start a new ModeledFrameworkBuilder. A client and model must be provided prior to the instance * being built via {@link org.apache.curator.x.async.modeled.ModeledFrameworkBuilder#withClient(org.apache.curator.x.async.AsyncCuratorFramework)} * and {@link org.apache.curator.x.async.modeled.ModeledFrameworkBuilder#withModelSpec(ModelSpec)} * * @return builder */ static ModeledFrameworkBuilder builder() { return new ModeledFrameworkBuilder<>(); } /** *

* Use an internally created cache as a front for this modeled instance. All read APIs use the internal * cache. i.e. read calls always use the cache instead of making direct queries. Note: you must call * {@link org.apache.curator.x.async.modeled.cached.CachedModeledFramework#start()} and * {@link org.apache.curator.x.async.modeled.cached.CachedModeledFramework#close()} to start/stop *

* *

* Note: the root node (the main path of the model) is not cached. i.e. only nodes * below the root are cached. *

* *

* Note: this method internally allocates an Executor for the cache and read methods. Use * {@link #cached(java.util.concurrent.ExecutorService)} if you'd like to provide your own executor service. *

* * @return wrapped instance */ CachedModeledFramework cached(); /** * Same as {@link #cached()} but allows for providing an executor service * * @param executor thread pool to use for the cache and for read operations * @return wrapped instance */ CachedModeledFramework cached(ExecutorService executor); /** * Return mutator APIs that work with {@link org.apache.curator.x.async.modeled.versioned.Versioned} containers * * @return wrapped instance */ VersionedModeledFramework versioned(); /** * Returns the client that was originally passed to {@link #wrap(org.apache.curator.x.async.AsyncCuratorFramework, ModelSpec)} or * the builder. * * @return original client */ AsyncCuratorFramework unwrap(); /** * Return the model being used * * @return model */ ModelSpec modelSpec(); /** *

* Return a new Modeled Curator instance with all the same options but applying to the given child node of this Modeled Curator's * path. E.g. if this Modeled Curator instance applies to "/a/b", calling modeled.at("c") returns an instance that applies to * "/a/b/c". *

* *

* The replacement is the toString() value of child or, * if it implements {@link org.apache.curator.x.async.modeled.NodeName}, * the value of nodeName(). *

* * @param child child node. * @return new Modeled Curator instance */ ModeledFramework child(Object child); /** *

* Return a new Modeled Curator instance with all the same options but applying to the parent node of this Modeled Curator's * path. E.g. if this Modeled Curator instance applies to "/a/b/c", calling modeled.parent() returns an instance that applies to * "/a/b". *

* *

* The replacement is the toString() value of child or, * if it implements {@link org.apache.curator.x.async.modeled.NodeName}, * the value of nodeName(). *

* * @return new Modeled Curator instance */ ModeledFramework parent(); /** * Return a Modeled Curator instance with all the same options but using the given path. * * @param path new path * @return new Modeled Curator instance */ ModeledFramework withPath(ZPath path); /** * Create (or update depending on build options) a ZNode at this instance's path with a serialized * version of the given model * * @param model model to write * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage set(T model); /** * Create (or update depending on build options) a ZNode at this instance's path with a serialized * version of the given model * * @param model model to write * @param version if data is being set instead of creating the node, the data version to use * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage set(T model, int version); /** * Create (or update depending on build options) a ZNode at this instance's path with a serialized * form of the given model * * @param model model to write * @param storingStatIn the stat for the new ZNode is stored here * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage set(T model, Stat storingStatIn); /** * Create (or update depending on build options) a ZNode at this instance's path with a serialized * form of the given model * * @param model model to write * @param version if data is being set instead of creating the node, the data version to use * @param storingStatIn the stat for the new ZNode is stored here * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage set(T model, Stat storingStatIn, int version); /** * Read the ZNode at this instance's path and deserialize into a model * * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage read(); /** * Read the ZNode at this instance's path and deserialize into a model * * @param storingStatIn the stat for the new ZNode is stored here * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage read(Stat storingStatIn); /** * Read the ZNode at this instance's path and deserialize into a model * * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage> readAsZNode(); /** * Update the ZNode at this instance's path with a serialized * form of the given model passing "-1" for the update version * * @param model model to write * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage update(T model); /** * Update the ZNode at this instance's path with a serialized * form of the given model passing the given update version * * @param model model to write * @param version update version to use * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage update(T model, int version); /** * Delete the ZNode at this instance's path passing -1 for the delete version * * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage delete(); /** * Delete the ZNode at this instance's path passing the given delete version * * @param version update version to use * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage delete(int version); /** * Check to see if the ZNode at this instance's path exists * * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage checkExists(); /** * Return the child paths of this instance's path (in no particular order) * * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage> children(); /** * Return the child paths of this instance's path (in no particular order) * and deserialize into a models. IMPORTANT: this results in a ZooKeeper query * for each child node returned. i.e. if the initial children() call returns * 10 nodes an additional 10 ZooKeeper queries are made to get the data. Note: * cannot be used if any of the {@link ModeledFrameworkBuilder#watched()} modes * are used. * * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage>> childrenAsZNodes(); /** * Create operation instance that can be passed among other operations to * {@link #inTransaction(java.util.List)} to be executed as a single transaction. Note: * due to ZooKeeper transaction limits, this is a _not_ a "set or update" operation but only * a create operation and will generate an error if the node already exists. * * @param model the model * @return operation */ CuratorOp createOp(T model); /** * Update operation instance that can be passed among other operations to * {@link #inTransaction(java.util.List)} to be executed as a single transaction. * * @param model the model * @return operation */ CuratorOp updateOp(T model); /** * Create operation instance that can be passed among other operations to * {@link #inTransaction(java.util.List)} to be executed as a single transaction. * * @param model the model * @param version update version to use * @return operation */ CuratorOp updateOp(T model, int version); /** * Delete operation instance that can be passed among other operations to * {@link #inTransaction(java.util.List)} to be executed as a single transaction. * * @return operation */ CuratorOp deleteOp(); /** * Delete operation instance that can be passed among other operations to * {@link #inTransaction(java.util.List)} to be executed as a single transaction. * * @param version delete version to use * @return operation */ CuratorOp deleteOp(int version); /** * Check exists operation instance that can be passed among other operations to * {@link #inTransaction(java.util.List)} to be executed as a single transaction. * * @return operation */ CuratorOp checkExistsOp(); /** * Check exists operation instance that can be passed among other operations to * {@link #inTransaction(java.util.List)} to be executed as a single transaction. * * @param version version to use * @return operation */ CuratorOp checkExistsOp(int version); /** * Invoke ZooKeeper to commit the given operations as a single transaction. * * @param operations operations that make up the transaction. * @return AsyncStage instance for managing the completion */ AsyncStage> inTransaction(List operations); } ModeledFrameworkBuilder.java000066400000000000000000000130431442004423600375750ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import com.google.common.collect.ImmutableSet; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.WatchMode; import org.apache.curator.x.async.modeled.details.ModeledFrameworkImpl; import org.apache.zookeeper.WatchedEvent; import java.util.Collections; import java.util.Objects; import java.util.Set; import java.util.function.UnaryOperator; public class ModeledFrameworkBuilder { private AsyncCuratorFramework client; private ModelSpec modelSpec; private WatchMode watchMode; private UnaryOperator watcherFilter; private UnhandledErrorListener unhandledErrorListener; private UnaryOperator resultFilter; private Set modeledOptions; /** * Build a new ModeledFramework instance * * @return new ModeledFramework instance */ public ModeledFramework build() { return ModeledFrameworkImpl.build( client, modelSpec, watchMode, watcherFilter, unhandledErrorListener, resultFilter, modeledOptions ); } /** * Add watchers as appropriate to the Modeled Curator's ZNode using * {@link org.apache.curator.x.async.WatchMode#stateChangeAndSuccess} * * @return this for chaining * @see org.apache.curator.x.async.AsyncStage#event() */ public ModeledFrameworkBuilder watched() { this.watchMode = WatchMode.stateChangeAndSuccess; return this; } /** * Add watchers as appropriate using the given watchMode to the Modeled Curator's ZNode * * @param watchMode watcher style * @return this for chaining * @see org.apache.curator.x.async.AsyncStage#event() */ public ModeledFrameworkBuilder watched(WatchMode watchMode) { this.watchMode = watchMode; return this; } /** * Add watchers as appropriate using the given watchMode and filter to the Modeled Curator's ZNode * * @param watchMode watcher style * @param watcherFilter filter * @return this for chaining * @see org.apache.curator.x.async.AsyncStage#event() */ public ModeledFrameworkBuilder watched(WatchMode watchMode, UnaryOperator watcherFilter) { this.watchMode = watchMode; this.watcherFilter = watcherFilter; return this; } /** * Use the given unhandledErrorListener for operations on the Modeled Curator's ZNode * * @param unhandledErrorListener listener * @return this for chaining */ public ModeledFrameworkBuilder withUnhandledErrorListener(UnhandledErrorListener unhandledErrorListener) { this.unhandledErrorListener = unhandledErrorListener; return this; } /** * Use the given result filter for operations on the Modeled Curator's ZNode * * @param resultFilter filter * @return this for chaining */ public ModeledFrameworkBuilder withResultFilter(UnaryOperator resultFilter) { this.resultFilter = resultFilter; return this; } /** * Change the model spec to use * * @param modelSpec model spec * @return this for chaining */ public ModeledFrameworkBuilder withModelSpec(ModelSpec modelSpec) { this.modelSpec = Objects.requireNonNull(modelSpec, "modelSpec cannot be null"); return this; } /** * Change the client to use * * @param client new client * @return this for chaining */ public ModeledFrameworkBuilder withClient(AsyncCuratorFramework client) { this.client = Objects.requireNonNull(client, "client cannot be null"); return this; } /** * Change the modeled options * * @param modeledOptions new options set * @return this for chaining */ public ModeledFrameworkBuilder withOptions(Set modeledOptions) { this.modeledOptions = ImmutableSet.copyOf(Objects.requireNonNull(modeledOptions, "client cannot be null")); return this; } ModeledFrameworkBuilder() { modeledOptions = Collections.singleton(ModeledOptions.ignoreMissingNodesForChildren); } ModeledFrameworkBuilder(AsyncCuratorFramework client, ModelSpec modelSpec) { this.client = Objects.requireNonNull(client, "client cannot be null"); this.modelSpec = Objects.requireNonNull(modelSpec, "modelSpec cannot be null"); modeledOptions = Collections.singleton(ModeledOptions.ignoreMissingNodesForChildren); } } ModeledOptions.java000066400000000000000000000021761442004423600357710ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; public enum ModeledOptions { /** * Causes {@link ModeledFramework#children()} and {@link ModeledFramework#childrenAsZNodes()} * to ignore {@link org.apache.zookeeper.KeeperException.NoNodeException} and merely return * an empty list */ ignoreMissingNodesForChildren } NodeName.java000066400000000000000000000025011442004423600345220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; /** * Used by the various "resolved" methods and "at" methods. * If the argument to one of these methods implements this interface, * the {@link #nodeName()} method is used instead of calling toString() */ @FunctionalInterface public interface NodeName { String nodeName(); static String nameFrom(Object obj) { if ( obj instanceof NodeName ) { return ((NodeName)obj).nodeName(); } return String.valueOf(obj); } } Resolvable.java000066400000000000000000000034741442004423600351440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import java.util.Arrays; import java.util.List; public interface Resolvable { /** * When creating paths, any node in the path can be set to {@link ZPath#parameter()}. * At runtime, the ZPath can be "resolved" by replacing these nodes with values. * * @param parameters list of replacements. Must have be the same length as the number of * parameter nodes in the path * @return new resolved ZPath */ default Object resolved(Object... parameters) { return resolved(Arrays.asList(parameters)); } /** * When creating paths, any node in the path can be set to {@link ZPath#parameter()}. * At runtime, the ZPath can be "resolved" by replacing these nodes with values. * * @param parameters list of replacements. Must have be the same length as the number of * parameter nodes in the path * @return new resolved ZPath */ Object resolved(List parameters); } ZNode.java000066400000000000000000000040251442004423600340560ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; /** * Abstracts a ZooKeeper node */ public interface ZNode { /** * The path of the node * * @return path */ ZPath path(); /** * The node's last known stat if available * * @return stat */ Stat stat(); /** * The node's current model * * @return model */ T model(); /** * Utility that modifies an async stage of znodes into an async stage of models * * @param from original stage * @return stage of models */ static CompletionStage> models(AsyncStage>> from) { return from.thenApply(nodes -> nodes.stream().map(ZNode::model).collect(Collectors.toList())); } /** * Utility that modifies an async stage of a znode into an async stage of a model * * @param from original stage * @return stage of a model */ static CompletionStage model(AsyncStage> from) { return from.thenApply(ZNode::model); } } ZPath.java000066400000000000000000000225061442004423600340710ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import org.apache.curator.x.async.modeled.details.ZPathImpl; import java.util.Arrays; import java.util.List; import java.util.function.UnaryOperator; import java.util.regex.Pattern; import static org.apache.curator.utils.ZKPaths.PATH_SEPARATOR; /** * Abstracts a ZooKeeper ZNode path */ public interface ZPath extends Resolvable { /** * The root path: "/" */ ZPath root = ZPathImpl.root; /** * Returns the special node name that can be used for replacements at runtime * via {@link #resolved(Object...)} when passed via the various from() methods */ static String parameter() { return parameter("id"); } /** * Same as {@link #parameter()} but allows you to specify an alternate code/name. This name * has no effect and is only for debugging purposes. When toString() is called * on ZPaths, this code shows. */ static String parameter(String name) { return PATH_SEPARATOR + "{" + name + "}"; } /** * Take a string path and return a ZPath. *

* Note: This method always produces a fully resolved path despite the presence of any parameter-like elements (i.e, {@code {one}}). * For substituting parameter elements and for proper parameter resolution status checks, use {@code parseWithIds()} instead. * * @param fullPath the path to parse * @return ZPath * @throws IllegalArgumentException if the path is invalid */ static ZPath parse(String fullPath) { return ZPathImpl.parse(fullPath, s -> s); } /** * Take a string path and return a ZPath. Each part of the path * that is {XXXX} is replaced with {@link #parameter()}. * E.g. parseWithIds("/one/two/{first}/three/{second}") is the equivalent * of calling ZPath.from("one", "two", parameter(), "three", parameter()) * * @param fullPath the path to parse * @return ZPath * @throws IllegalArgumentException if the path is invalid */ static ZPath parseWithIds(String fullPath) { return ZPathImpl.parse(fullPath, s -> isId(s) ? (PATH_SEPARATOR + s) : s); } /** * Return true if the given string conforms to the "{XXXX}" ID pattern * * @param s string to check * @return true/false */ static boolean isId(String s) { return s.startsWith("{") && s.endsWith("}"); } /** * Take a ZNode string path and return a ZPath * * @param fullPath the path to parse * @param nameFilter each part of the path is passed through this filter * @return ZPath * @throws IllegalArgumentException if the path is invalid */ static ZPath parse(String fullPath, UnaryOperator nameFilter) { return ZPathImpl.parse(fullPath, nameFilter); } /** * Convert individual path names into a ZPath. E.g. * ZPath.from("my", "full", "path"). Any/all of the names can be passed as * {@link #parameter()} so that the path can be resolved later using * of the resolved() methods. * * @param names path names * @return ZPath * @throws IllegalArgumentException if any of the names is invalid */ static ZPath from(String... names) { return ZPathImpl.from(names); } /** * Convert individual path names into a ZPath. Any/all of the names can be passed as * {@link #parameter()} so that the path can be resolved later using * of the resolved() methods. * * @param names path names * @return ZPath * @throws IllegalArgumentException if any of the names is invalid */ static ZPath from(List names) { return ZPathImpl.from(names); } /** * Convert individual path names into a ZPath starting at the given base. E.g. * if base is "/home/base" ZPath.from(base, "my", "full", "path") * would be "/home/base/my/full/path". Any/all of the names can be passed as * {@link #parameter()} so that the path can be resolved later using * of the resolved() methods. * * @param base base/starting path * @param names path names * @return ZPath * @throws IllegalArgumentException if any of the names is invalid */ static ZPath from(ZPath base, String... names) { return ZPathImpl.from(base, names); } /** * Convert individual path names into a ZPath starting at the given base. Any/all of the names can be passed as * {@link #parameter()} so that the path can be resolved later using * of the resolved() methods. * * @param base base/starting path * @param names path names * @return ZPath * @throws IllegalArgumentException if any of the names is invalid */ static ZPath from(ZPath base, List names) { return ZPathImpl.from(base, names); } /** *

* When creating paths, any node in the path can be set to {@link #parameter()}. * At runtime, the ZPath can be "resolved" by replacing these nodes with values. *

* *

* The replacement is the toString() value of the parameter object or, * if the object implements {@link org.apache.curator.x.async.modeled.NodeName}, * the value of nodeName(). *

* * @param parameters list of replacements. Must have be the same length as the number of * parameter nodes in the path * @return new resolved ZPath */ @Override default ZPath resolved(Object... parameters) { return resolved(Arrays.asList(parameters)); } /** *

* When creating paths, any node in the path can be set to {@link #parameter()}. * At runtime, the ZPath can be "resolved" by replacing these nodes with values. *

* *

* The replacement is the toString() value of the parameter object or, * if the object implements {@link org.apache.curator.x.async.modeled.NodeName}, * the value of nodeName(). *

* * @param parameters list of replacements. Must have be the same length as the number of * parameter nodes in the path * @return new resolved ZPath */ @Override ZPath resolved(List parameters); /** *

* Return a ZPath that represents a child ZNode of this ZPath. e.g. * ZPath.from("a", "b").at("c") represents the path "/a/b/c" *

* *

* The replacement is the toString() value of child or, * if it implements {@link org.apache.curator.x.async.modeled.NodeName}, * the value of nodeName(). *

* * @param child child node name * @return ZPath */ ZPath child(Object child); /** * Return this ZPath's parent * * @return parent ZPath * @throws java.util.NoSuchElementException if this is the root ZPath */ ZPath parent(); /** * Return true/false if this is the root ZPath * * @return true false */ boolean isRoot(); /** * Return true if this path is fully resolved (i.e. has no unresolved parameters). *

* Note: ZPath's returned by the {@code parse()} method are always considered fully resolved, despite if there are * remaining elements in the path which appear to be parameters (but are not, i.e. {@code {one}}). *

* When working with parameters, use the {@code parseWithIds()} method, which returns a ZPath with a * resolved state based on the presence of unresolved parameter elements in the ZPath. * * @return true/false */ boolean isResolved(); /** * Return true if this path starts with the given path. i.e. * ZPath.from("/one/two/three").startsWith(ZPath.from("/one/two")) returns true * * @param path base path * @return true/false */ boolean startsWith(ZPath path); /** * The string full path that this ZPath represents * * @return full path */ String fullPath(); /** * The node name at this ZPath * * @return name */ String nodeName(); /** * Return a regex Pattern useful for using in {@link org.apache.curator.framework.schema.Schema} * * @return pattern for this path */ Pattern toSchemaPathPattern(); } cached/000077500000000000000000000000001442004423600334025ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeledCachedModeledFramework.java000066400000000000000000000064421442004423600405720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.cached; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ZNode; import org.apache.curator.x.async.modeled.ZPath; import org.apache.zookeeper.data.Stat; import java.io.Closeable; import java.util.List; public interface CachedModeledFramework extends ModeledFramework, Closeable { /** * Return the cache instance * * @return cache */ ModeledCache cache(); /** * Start the internally created cache */ void start(); /** * Close/stop the internally created cache */ @Override void close(); /** * Return the listener container so that you can add/remove listeners * * @return listener container */ Listenable> listenable(); /** * Same as {@link org.apache.curator.x.async.modeled.ModeledFramework#childrenAsZNodes()} * but always reads from cache - i.e. no additional queries to ZooKeeper are made * * @return AsyncStage stage */ @Override AsyncStage>> childrenAsZNodes(); /** * {@inheritDoc} */ @Override CachedModeledFramework child(Object child); /** * {@inheritDoc} */ @Override CachedModeledFramework withPath(ZPath path); /** * Same as {@link #read()} except that if the cache does not have a value * for this path a direct query is made. * * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage readThrough(); /** * Same as {@link #read(org.apache.zookeeper.data.Stat)} except that if the cache does not have a value * for this path a direct query is made. * * @param storingStatIn the stat for the new ZNode is stored here * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage readThrough(Stat storingStatIn); /** * Same as {@link #readAsZNode()} except that if the cache does not have a value * for this path a direct query is made. * * * @return AsyncStage * @see org.apache.curator.x.async.AsyncStage */ AsyncStage> readThroughAsZNode(); /** * Return the instances of the base path of this cached framework * * @return listing of all models in the base path */ AsyncStage> list(); } ModeledCache.java000066400000000000000000000034531442004423600365470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.cached; import org.apache.curator.x.async.modeled.ZNode; import org.apache.curator.x.async.modeled.ZPath; import java.util.Map; import java.util.Optional; public interface ModeledCache { /** * Return the modeled current data for the given path. There are no guarantees of accuracy. This is * merely the most recent view of the data. If there is no node at the given path, * {@link java.util.Optional#empty()} is returned. * * @param path path to the node to check * @return data if the node is alive, or empty */ Optional> currentData(ZPath path); /** * Return the modeled current set of children at the given path, mapped by child name. There are no * guarantees of accuracy; this is merely the most recent view of the data. * * @param path path to the node to check * @return a possibly-empty map of children if the node is alive */ Map> currentChildren(ZPath path); } ModeledCacheListener.java000066400000000000000000000062261442004423600402560ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/cached/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.cached; import org.apache.curator.x.async.modeled.ZPath; import org.apache.zookeeper.data.Stat; import org.slf4j.LoggerFactory; @FunctionalInterface public interface ModeledCacheListener { enum Type { /** * A child was added to the path */ NODE_ADDED, /** * A child's data was changed */ NODE_UPDATED, /** * A child was removed from the path */ NODE_REMOVED } /** * The given path was added, updated or removed * * @param type action type * @param path the path * @param stat the node's stat (previous stat for removal) * @param model the node's model (previous model for removal) */ void accept(Type type, ZPath path, Stat stat, T model); /** * The cache has finished initializing */ default void initialized() { // NOP } /** * Called when there is an exception processing a message from the internal cache. This is most * likely due to a de-serialization problem. * * @param e the exception */ default void handleException(Exception e) { LoggerFactory.getLogger(getClass()).error("Could not process cache message", e); } /** * Returns a version of this listener that only begins calling * {@link #accept(org.apache.curator.x.async.modeled.cached.ModeledCacheListener.Type, org.apache.curator.x.async.modeled.ZPath, org.apache.zookeeper.data.Stat, Object)} * once {@link #initialized()} has been called. i.e. changes that occur as the cache is initializing are not sent * to the listener * * @return wrapped listener */ default ModeledCacheListener postInitializedOnly() { return new ModeledCacheListener() { private volatile boolean isInitialized = false; @Override public void accept(Type type, ZPath path, Stat stat, T model) { if ( isInitialized ) { ModeledCacheListener.this.accept(type, path, stat, model); } } @Override public void initialized() { isInitialized = true; ModeledCacheListener.this.initialized(); } }; } } details/000077500000000000000000000000001442004423600336205ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeledCachedModeledFrameworkImpl.java000066400000000000000000000217721442004423600416350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.details; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ZNode; import org.apache.curator.x.async.modeled.ZPath; import org.apache.curator.x.async.modeled.cached.CachedModeledFramework; import org.apache.curator.x.async.modeled.cached.ModeledCache; import org.apache.curator.x.async.modeled.cached.ModeledCacheListener; import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.DataTree; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; class CachedModeledFrameworkImpl implements CachedModeledFramework { private final ModeledFramework client; private final ModeledCacheImpl cache; private final Executor executor; CachedModeledFrameworkImpl(ModeledFramework client, ExecutorService executor) { this(client, new ModeledCacheImpl<>(client.unwrap().unwrap(), client.modelSpec(), executor), executor); } private CachedModeledFrameworkImpl(ModeledFramework client, ModeledCacheImpl cache, Executor executor) { this.client = client; this.cache = cache; this.executor = executor; } @Override public ModeledCache cache() { return cache; } @Override public void start() { cache.start(); } @Override public void close() { cache.close(); } @Override public Listenable> listenable() { return cache.listenable(); } @Override public CachedModeledFramework cached() { throw new UnsupportedOperationException("Already a cached instance"); } @Override public CachedModeledFramework cached(ExecutorService executor) { throw new UnsupportedOperationException("Already a cached instance"); } @Override public VersionedModeledFramework versioned() { return new VersionedModeledFrameworkImpl<>(this); } @Override public AsyncCuratorFramework unwrap() { return client.unwrap(); } @Override public ModelSpec modelSpec() { return client.modelSpec(); } @Override public CachedModeledFramework child(Object child) { return new CachedModeledFrameworkImpl<>(client.child(child), cache, executor); } @Override public ModeledFramework parent() { throw new UnsupportedOperationException("Not supported for CachedModeledFramework. Instead, call parent() on the ModeledFramework before calling cached()"); } @Override public CachedModeledFramework withPath(ZPath path) { return new CachedModeledFrameworkImpl<>(client.withPath(path), cache, executor); } @Override public AsyncStage set(T model) { return client.set(model); } @Override public AsyncStage set(T model, Stat storingStatIn) { return client.set(model, storingStatIn); } @Override public AsyncStage set(T model, Stat storingStatIn, int version) { return client.set(model, storingStatIn, version); } @Override public AsyncStage set(T model, int version) { return client.set(model, version); } @Override public AsyncStage read() { return internalRead(ZNode::model, this::exceptionally); } @Override public AsyncStage read(Stat storingStatIn) { return internalRead(n -> { if ( storingStatIn != null ) { DataTree.copyStat(n.stat(), storingStatIn); } return n.model(); }, this::exceptionally); } @Override public AsyncStage> readAsZNode() { return internalRead(Function.identity(), this::exceptionally); } @Override public AsyncStage readThrough() { return internalRead(ZNode::model, client::read); } @Override public AsyncStage readThrough(Stat storingStatIn) { return internalRead(ZNode::model, () -> client.read(storingStatIn)); } @Override public AsyncStage> readThroughAsZNode() { return internalRead(Function.identity(), client::readAsZNode); } @Override public AsyncStage> list() { List children = cache.currentChildren() .values() .stream() .map(ZNode::model) .collect(Collectors.toList()); return ModelStage.completed(children); } @Override public AsyncStage update(T model) { return client.update(model); } @Override public AsyncStage update(T model, int version) { return client.update(model, version); } @Override public AsyncStage delete() { return client.delete(); } @Override public AsyncStage delete(int version) { return client.delete(version); } @Override public AsyncStage checkExists() { ZPath path = client.modelSpec().path(); Optional> data = cache.currentData(path); return data.map(node -> completed(node.stat())).orElseGet(() -> completed(null)); } @Override public AsyncStage> children() { List paths = cache.currentChildren(client.modelSpec().path()) .keySet() .stream() .filter(path -> !path.isRoot() && path.parent().equals(client.modelSpec().path())) .collect(Collectors.toList()); return completed(paths); } @Override public AsyncStage>> childrenAsZNodes() { List> nodes = cache.currentChildren(client.modelSpec().path()) .entrySet() .stream() .filter(e -> !e.getKey().isRoot() && e.getKey().parent().equals(client.modelSpec().path())) .map(Map.Entry::getValue) .collect(Collectors.toList()); return completed(nodes); } @Override public CuratorOp createOp(T model) { return client.createOp(model); } @Override public CuratorOp updateOp(T model) { return client.updateOp(model); } @Override public CuratorOp updateOp(T model, int version) { return client.updateOp(model, version); } @Override public CuratorOp deleteOp() { return client.deleteOp(); } @Override public CuratorOp deleteOp(int version) { return client.deleteOp(version); } @Override public CuratorOp checkExistsOp() { return client.checkExistsOp(); } @Override public CuratorOp checkExistsOp(int version) { return client.checkExistsOp(version); } @Override public AsyncStage> inTransaction(List operations) { return client.inTransaction(operations); } private AsyncStage completed(U value) { return ModelStage.completed(value); } private AsyncStage exceptionally() { KeeperException.NoNodeException exception = new KeeperException.NoNodeException(client.modelSpec().path().fullPath()); return ModelStage.exceptionally(exception); } private AsyncStage internalRead(Function, U> resolver, Supplier> elseProc) { ZPath path = client.modelSpec().path(); Optional> data = cache.currentData(path); return data.map(node -> completed(resolver.apply(node))) .orElseGet(elseProc); } } ModelSpecImpl.java000066400000000000000000000162761442004423600371740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.details; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.apache.curator.framework.schema.Schema; import org.apache.curator.framework.schema.SchemaValidator; import org.apache.curator.framework.schema.SchemaViolation; import org.apache.curator.x.async.api.CreateOption; import org.apache.curator.x.async.api.DeleteOption; import org.apache.curator.x.async.modeled.ModelSerializer; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ZPath; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import java.util.List; import java.util.Objects; import java.util.Set; public class ModelSpecImpl implements ModelSpec, SchemaValidator { private final ZPath path; private final ModelSerializer serializer; private final CreateMode createMode; private final List aclList; private final Set createOptions; private final Set deleteOptions; private final long ttl; private volatile Schema schema = null; public ModelSpecImpl(ZPath path, ModelSerializer serializer, CreateMode createMode, List aclList, Set createOptions, Set deleteOptions, long ttl) { this.path = Objects.requireNonNull(path, "path cannot be null"); this.serializer = Objects.requireNonNull(serializer, "serializer cannot be null"); this.createMode = Objects.requireNonNull(createMode, "createMode cannot be null"); this.aclList = ImmutableList.copyOf(Objects.requireNonNull(aclList, "aclList cannot be null")); this.createOptions = ImmutableSet.copyOf(Objects.requireNonNull(createOptions, "createOptions cannot be null")); this.deleteOptions = ImmutableSet.copyOf(Objects.requireNonNull(deleteOptions, "deleteOptions cannot be null")); this.ttl = ttl; } @Override public ModelSpec child(Object child) { return withPath(path.child(child)); } @Override public ModelSpec parent() { return withPath(path.parent()); } @Override public ModelSpec resolved(Object... parameters) { return withPath(path.resolved(parameters)); } @Override public ModelSpec resolved(List parameters) { return withPath(path.resolved(parameters)); } @Override public ModelSpec withPath(ZPath newPath) { return new ModelSpecImpl<>(newPath, serializer, createMode, aclList, createOptions, deleteOptions, ttl); } @Override public ZPath path() { return path; } @Override public ModelSerializer serializer() { return serializer; } @Override public CreateMode createMode() { return createMode; } @Override public List aclList() { return aclList; } @Override public Set createOptions() { return createOptions; } @Override public Set deleteOptions() { return deleteOptions; } @Override public long ttl() { return ttl; } @Override public Schema schema() { if ( schema == null ) { schema = Schema.builder(path.toSchemaPathPattern()) .dataValidator(this) .ephemeral(createMode.isEphemeral() ? Schema.Allowance.MUST : Schema.Allowance.CANNOT) .canBeDeleted(true) .sequential(createMode.isSequential() ? Schema.Allowance.MUST : Schema.Allowance.CANNOT) .watched(Schema.Allowance.CAN) .build(); } return schema; } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } ModelSpecImpl modelSpec = (ModelSpecImpl)o; if ( ttl != modelSpec.ttl ) { return false; } if ( !path.equals(modelSpec.path) ) { return false; } if ( !serializer.equals(modelSpec.serializer) ) { return false; } if ( createMode != modelSpec.createMode ) { return false; } if ( !aclList.equals(modelSpec.aclList) ) { return false; } if ( !createOptions.equals(modelSpec.createOptions) ) { return false; } //noinspection SimplifiableIfStatement if ( !deleteOptions.equals(modelSpec.deleteOptions) ) { return false; } return schema.equals(modelSpec.schema); } @Override public int hashCode() { int result = path.hashCode(); result = 31 * result + serializer.hashCode(); result = 31 * result + createMode.hashCode(); result = 31 * result + aclList.hashCode(); result = 31 * result + createOptions.hashCode(); result = 31 * result + deleteOptions.hashCode(); result = 31 * result + (int)(ttl ^ (ttl >>> 32)); result = 31 * result + schema.hashCode(); return result; } @Override public String toString() { return "ModelSpecImpl{" + "path=" + path + ", serializer=" + serializer + ", createMode=" + createMode + ", aclList=" + aclList + ", createOptions=" + createOptions + ", deleteOptions=" + deleteOptions + ", ttl=" + ttl + ", schema=" + schema + '}'; } @Override public boolean isValid(Schema schema, String path, byte[] data, List acl) { if ( acl != null ) { List localAclList = (aclList.size() > 0) ? aclList : ZooDefs.Ids.OPEN_ACL_UNSAFE; if ( !acl.equals(localAclList) ) { throw new SchemaViolation(schema, new SchemaViolation.ViolatorData(path, data, acl), "ACLs do not match model ACLs"); } } if ( data != null ) { try { serializer.deserialize(data); } catch ( RuntimeException e ) { throw new SchemaViolation(schema, new SchemaViolation.ViolatorData(path, data, acl), "Data cannot be deserialized into a model"); } } return true; } } ModelStage.java000066400000000000000000000125041442004423600365110ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.details; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.WatchedEvent; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.Executor; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; class ModelStage extends CompletableFuture implements AsyncStage { private final CompletionStage event; static ModelStage make() { return new ModelStage<>(null); } static ModelStage make(CompletionStage event) { return new ModelStage<>(event); } static ModelStage completed(U value) { ModelStage stage = new ModelStage<>(null); stage.complete(value); return stage; } static ModelStage exceptionally(Exception e) { ModelStage stage = new ModelStage<>(null); stage.completeExceptionally(e); return stage; } static ModelStage async(Executor executor) { return new AsyncModelStage<>(executor); } static ModelStage asyncCompleted(U value, Executor executor) { ModelStage stage = new AsyncModelStage<>(executor); stage.complete(value); return stage; } static ModelStage asyncExceptionally(Exception e, Executor executor) { ModelStage stage = new AsyncModelStage<>(executor); stage.completeExceptionally(e); return stage; } @Override public CompletionStage event() { return event; } private ModelStage(CompletionStage event) { this.event = event; } private static class AsyncModelStage extends ModelStage { private final Executor executor; public AsyncModelStage(Executor executor) { super(null); this.executor = executor; } @Override public CompletableFuture thenApplyAsync(Function fn) { return super.thenApplyAsync(fn, executor); } @Override public CompletableFuture thenAcceptAsync(Consumer action) { return super.thenAcceptAsync(action, executor); } @Override public CompletableFuture thenRunAsync(Runnable action) { return super.thenRunAsync(action, executor); } @Override public CompletableFuture thenCombineAsync(CompletionStage other, BiFunction fn) { return super.thenCombineAsync(other, fn, executor); } @Override public CompletableFuture thenAcceptBothAsync(CompletionStage other, BiConsumer action) { return super.thenAcceptBothAsync(other, action, executor); } @Override public CompletableFuture runAfterBothAsync(CompletionStage other, Runnable action) { return super.runAfterBothAsync(other, action, executor); } @Override public CompletableFuture applyToEitherAsync(CompletionStage other, Function fn) { return super.applyToEitherAsync(other, fn, executor); } @Override public CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action) { return super.acceptEitherAsync(other, action, executor); } @Override public CompletableFuture runAfterEitherAsync(CompletionStage other, Runnable action) { return super.runAfterEitherAsync(other, action, executor); } @Override public CompletableFuture thenComposeAsync(Function> fn) { return super.thenComposeAsync(fn, executor); } @Override public CompletableFuture whenCompleteAsync(BiConsumer action) { return super.whenCompleteAsync(action, executor); } @Override public CompletableFuture handleAsync(BiFunction fn) { return super.handleAsync(fn, executor); } } } ModeledCacheImpl.java000066400000000000000000000170671442004423600376150ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.details; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.EnsureContainers; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.recipes.cache.CuratorCache; import org.apache.curator.framework.recipes.cache.CuratorCacheBridge; import org.apache.curator.framework.recipes.cache.CuratorCacheBridgeBuilder; import org.apache.curator.framework.recipes.cache.CuratorCacheListener; import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.apache.curator.framework.recipes.cache.TreeCacheListener; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.x.async.api.CreateOption; import org.apache.curator.x.async.modeled.ModelSerializer; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ZNode; import org.apache.curator.x.async.modeled.ZPath; import org.apache.curator.x.async.modeled.cached.ModeledCache; import org.apache.curator.x.async.modeled.cached.ModeledCacheListener; import org.apache.zookeeper.data.Stat; import java.util.AbstractMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; class ModeledCacheImpl implements TreeCacheListener, ModeledCache { private final CuratorCacheBridge cache; private final Map> entries = new ConcurrentHashMap<>(); private final ModelSerializer serializer; private final StandardListenerManager> listenerContainer = StandardListenerManager.standard(); private final ZPath basePath; private final EnsureContainers ensureContainers; private static final class Entry { final Stat stat; final T model; Entry(Stat stat, T model) { this.stat = stat; this.model = model; } } ModeledCacheImpl(CuratorFramework client, ModelSpec modelSpec, ExecutorService executor) { if ( !modelSpec.path().isResolved() && !modelSpec.path().isRoot() && modelSpec.path().parent().isResolved() ) { modelSpec = modelSpec.parent(); // i.e. the last item is a parameter } basePath = modelSpec.path(); this.serializer = modelSpec.serializer(); CuratorCacheBridgeBuilder bridgeBuilder = CuratorCache.bridgeBuilder(client, basePath.fullPath()).withDataNotCached().withExecutorService(executor); if ( modelSpec.createOptions().contains(CreateOption.compress) ) { bridgeBuilder = bridgeBuilder.withOptions(CuratorCache.Options.COMPRESSED_DATA); } cache = bridgeBuilder.build(); cache.listenable().addListener(CuratorCacheListener.builder().forTreeCache(client, this).build()); if ( modelSpec.createOptions().contains(CreateOption.createParentsIfNeeded) || modelSpec.createOptions().contains(CreateOption.createParentsAsContainers) ) { ensureContainers = new EnsureContainers(client, basePath.fullPath()); } else { ensureContainers = null; } } public void start() { try { if ( ensureContainers != null ) { ensureContainers.ensure(); } cache.start(); } catch ( Exception e ) { throw new RuntimeException(e); } } public void close() { cache.close(); entries.clear(); } @Override public Optional> currentData(ZPath path) { Entry entry = entries.get(path); if ( entry != null ) { return Optional.of(new ZNodeImpl<>(path, entry.stat, entry.model)); } return Optional.empty(); } ZPath basePath() { return basePath; } Map> currentChildren() { return currentChildren(basePath); } @Override public Map> currentChildren(ZPath path) { return entries.entrySet() .stream() .filter(entry -> entry.getKey().startsWith(path)) .map(entry -> new AbstractMap.SimpleEntry<>(entry.getKey(), new ZNodeImpl<>(entry.getKey(), entry.getValue().stat, entry.getValue().model))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } public Listenable> listenable() { return listenerContainer; } @Override public void childEvent(CuratorFramework client, TreeCacheEvent event) { try { internalChildEvent(event); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); listenerContainer.forEach(l -> l.handleException(e)); } } private void internalChildEvent(TreeCacheEvent event) { switch ( event.getType() ) { case NODE_ADDED: case NODE_UPDATED: { ZPath path = ZPath.parse(event.getData().getPath()); byte[] bytes = event.getData().getData(); if ( (bytes != null) && (bytes.length > 0) ) // otherwise it's probably just a parent node being created { T model = serializer.deserialize(bytes); entries.put(path, new Entry<>(event.getData().getStat(), model)); ModeledCacheListener.Type type = (event.getType() == TreeCacheEvent.Type.NODE_ADDED) ? ModeledCacheListener.Type.NODE_ADDED : ModeledCacheListener.Type.NODE_UPDATED; accept(type, path, event.getData().getStat(), model); } break; } case NODE_REMOVED: { ZPath path = ZPath.parse(event.getData().getPath()); Entry entry = entries.remove(path); T model = null; if (entry != null) { model = entry.model; } else if (event.getData().getData() != null) { model = serializer.deserialize(event.getData().getData()); } if (model != null) { Stat stat = (entry != null) ? entry.stat : event.getData().getStat(); accept(ModeledCacheListener.Type.NODE_REMOVED, path, stat, model); } break; } case INITIALIZED: { listenerContainer.forEach(ModeledCacheListener::initialized); break; } default: // ignore break; } } private void accept(ModeledCacheListener.Type type, ZPath path, Stat stat, T model) { listenerContainer.forEach(l -> l.accept(type, path, stat, model)); } } ModeledFrameworkImpl.java000066400000000000000000000367641442004423600405540ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.details; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.apache.curator.framework.api.CuratorEvent; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.framework.api.transaction.CuratorTransactionResult; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.WatchMode; import org.apache.curator.x.async.api.AsyncCuratorFrameworkDsl; import org.apache.curator.x.async.api.AsyncPathAndBytesable; import org.apache.curator.x.async.api.AsyncPathable; import org.apache.curator.x.async.api.AsyncSetDataBuilder; import org.apache.curator.x.async.api.AsyncTransactionSetDataBuilder; import org.apache.curator.x.async.api.CreateOption; import org.apache.curator.x.async.api.WatchableAsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledOptions; import org.apache.curator.x.async.modeled.ZNode; import org.apache.curator.x.async.modeled.ZPath; import org.apache.curator.x.async.modeled.cached.CachedModeledFramework; import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.stream.Collectors; public class ModeledFrameworkImpl implements ModeledFramework { private final AsyncCuratorFramework client; private final WatchableAsyncCuratorFramework watchableClient; private final ModelSpec modelSpec; private final WatchMode watchMode; private final UnaryOperator watcherFilter; private final UnhandledErrorListener unhandledErrorListener; private final UnaryOperator resultFilter; private final AsyncCuratorFrameworkDsl dslClient; private final boolean isWatched; private final Set modeledOptions; public static ModeledFrameworkImpl build(AsyncCuratorFramework client, ModelSpec model, WatchMode watchMode, UnaryOperator watcherFilter, UnhandledErrorListener unhandledErrorListener, UnaryOperator resultFilter, Set modeledOptions) { boolean isWatched = (watchMode != null); Objects.requireNonNull(client, "client cannot be null"); Objects.requireNonNull(model, "model cannot be null"); modeledOptions = ImmutableSet.copyOf(Objects.requireNonNull(modeledOptions, "modeledOptions cannot be null")); watchMode = (watchMode != null) ? watchMode : WatchMode.stateChangeAndSuccess; AsyncCuratorFrameworkDsl dslClient = client.with(watchMode, unhandledErrorListener, resultFilter, watcherFilter); WatchableAsyncCuratorFramework watchableClient = isWatched ? dslClient.watched() : dslClient; return new ModeledFrameworkImpl<>( client, dslClient, watchableClient, model, watchMode, watcherFilter, unhandledErrorListener, resultFilter, isWatched, modeledOptions ); } private ModeledFrameworkImpl(AsyncCuratorFramework client, AsyncCuratorFrameworkDsl dslClient, WatchableAsyncCuratorFramework watchableClient, ModelSpec modelSpec, WatchMode watchMode, UnaryOperator watcherFilter, UnhandledErrorListener unhandledErrorListener, UnaryOperator resultFilter, boolean isWatched, Set modeledOptions) { this.client = client; this.dslClient = dslClient; this.watchableClient = watchableClient; this.modelSpec = modelSpec; this.watchMode = watchMode; this.watcherFilter = watcherFilter; this.unhandledErrorListener = unhandledErrorListener; this.resultFilter = resultFilter; this.isWatched = isWatched; this.modeledOptions = modeledOptions; } @Override public CachedModeledFramework cached() { return cached(ThreadUtils.newSingleThreadExecutor("CachedModeledFramework")); } @Override public CachedModeledFramework cached(ExecutorService executor) { Preconditions.checkState(!isWatched, "CachedModeledFramework cannot be used with watched instances as the internal cache would bypass the watchers."); return new CachedModeledFrameworkImpl<>(this, Objects.requireNonNull(executor, "executor cannot be null")); } @Override public VersionedModeledFramework versioned() { return new VersionedModeledFrameworkImpl<>(this); } @Override public ModelSpec modelSpec() { return modelSpec; } @Override public AsyncCuratorFramework unwrap() { return client; } @Override public AsyncStage set(T item) { return set(item, null, -1); } @Override public AsyncStage set(T item, Stat storingStatIn) { return set(item, storingStatIn, -1); } @Override public AsyncStage set(T item, int version) { return set(item, null, version); } @Override public AsyncStage set(T item, Stat storingStatIn, int version) { try { byte[] bytes = modelSpec.serializer().serialize(item); return dslClient.create() .withOptions(modelSpec.createOptions(), modelSpec.createMode(), fixAclList(modelSpec.aclList()), storingStatIn, modelSpec.ttl(), version) .forPath(resolveForSet(item), bytes); } catch ( Exception e ) { return ModelStage.exceptionally(e); } } @Override public AsyncStage read() { return internalRead(ZNode::model, null); } @Override public AsyncStage read(Stat storingStatIn) { return internalRead(ZNode::model, storingStatIn); } @Override public AsyncStage> readAsZNode() { return internalRead(Function.identity(), null); } @Override public AsyncStage update(T item) { return update(item, -1); } @Override public AsyncStage update(T item, int version) { try { byte[] bytes = modelSpec.serializer().serialize(item); AsyncSetDataBuilder dataBuilder = dslClient.setData(); AsyncPathAndBytesable> next = isCompressed() ? dataBuilder.compressedWithVersion(version) : dataBuilder.withVersion(version); return next.forPath(resolveForSet(item), bytes); } catch ( Exception e ) { return ModelStage.exceptionally(e); } } @Override public AsyncStage checkExists() { return watchableClient.checkExists().forPath(modelSpec.path().fullPath()); } @Override public AsyncStage delete() { return delete(-1); } @Override public AsyncStage delete(int version) { return dslClient.delete().withOptionsAndVersion(modelSpec.deleteOptions(), version).forPath(modelSpec.path().fullPath()); } @Override public AsyncStage> children() { return internalGetChildren(modelSpec.path()); } @Override public AsyncStage>> childrenAsZNodes() { ModelStage>> modelStage = ModelStage.make(); Preconditions.checkState(!isWatched, "childrenAsZNodes() cannot be used with watched instances."); children().handle((children, e) -> { if ( e != null ) { modelStage.completeExceptionally(e); } else { completeChildrenAsZNodes(modelStage, children); } return null; }); return modelStage; } private void completeChildrenAsZNodes(ModelStage>> modelStage, List children) { List> nodes = Lists.newArrayList(); if ( children.size() == 0 ) { modelStage.complete(nodes); return; } children.forEach(path -> withPath(path).readAsZNode().handle((node, e) -> { if ( e != null ) { modelStage.completeExceptionally(e); } else { nodes.add(node); if ( nodes.size() == children.size() ) { modelStage.complete(nodes); } } return null; })); } private AsyncStage> internalGetChildren(ZPath path) { AsyncStage> asyncStage = watchableClient.getChildren().forPath(path.fullPath()); ModelStage> modelStage = ModelStage.make(asyncStage.event()); asyncStage.whenComplete((children, e) -> { if ( e != null ) { if ( modeledOptions.contains(ModeledOptions.ignoreMissingNodesForChildren) && (Throwables.getRootCause(e) instanceof KeeperException.NoNodeException) ) { modelStage.complete(Collections.emptyList()); } else { modelStage.completeExceptionally(e); } } else { modelStage.complete(children.stream().map(path::child).collect(Collectors.toList())); } }); return modelStage; } @Override public ModeledFramework parent() { ModelSpec newModelSpec = modelSpec.parent(); return new ModeledFrameworkImpl<>( client, dslClient, watchableClient, newModelSpec, watchMode, watcherFilter, unhandledErrorListener, resultFilter, isWatched, modeledOptions ); } @Override public ModeledFramework child(Object child) { ModelSpec newModelSpec = modelSpec.child(child); return new ModeledFrameworkImpl<>( client, dslClient, watchableClient, newModelSpec, watchMode, watcherFilter, unhandledErrorListener, resultFilter, isWatched, modeledOptions ); } @Override public ModeledFramework withPath(ZPath path) { ModelSpec newModelSpec = modelSpec.withPath(path); return new ModeledFrameworkImpl<>( client, dslClient, watchableClient, newModelSpec, watchMode, watcherFilter, unhandledErrorListener, resultFilter, isWatched, modeledOptions ); } public static boolean isCompressed(Set createOptions) { return createOptions.contains(CreateOption.compress); } @Override public CuratorOp createOp(T model) { return client.transactionOp() .create() .withOptions(modelSpec.createMode(), fixAclList(modelSpec.aclList()), modelSpec.createOptions().contains(CreateOption.compress), modelSpec.ttl()) .forPath(resolveForSet(model), modelSpec.serializer().serialize(model)); } @Override public CuratorOp updateOp(T model) { return updateOp(model, -1); } @Override public CuratorOp updateOp(T model, int version) { AsyncTransactionSetDataBuilder builder = client.transactionOp().setData(); if ( isCompressed() ) { return builder.withVersionCompressed(version).forPath(resolveForSet(model), modelSpec.serializer().serialize(model)); } return builder.withVersion(version).forPath(resolveForSet(model), modelSpec.serializer().serialize(model)); } @Override public CuratorOp deleteOp() { return deleteOp(-1); } @Override public CuratorOp deleteOp(int version) { return client.transactionOp().delete().withVersion(version).forPath(modelSpec.path().fullPath()); } @Override public CuratorOp checkExistsOp() { return checkExistsOp(-1); } @Override public CuratorOp checkExistsOp(int version) { return client.transactionOp().check().withVersion(version).forPath(modelSpec.path().fullPath()); } @Override public AsyncStage> inTransaction(List operations) { return client.transaction().forOperations(operations); } private boolean isCompressed() { return modelSpec.createOptions().contains(CreateOption.compress); } private ModelStage internalRead(Function, U> resolver, Stat storingStatIn) { Stat stat = (storingStatIn != null) ? storingStatIn : new Stat(); AsyncPathable> next = isCompressed() ? watchableClient.getData().decompressedStoringStatIn(stat) : watchableClient.getData().storingStatIn(stat); AsyncStage asyncStage = next.forPath(modelSpec.path().fullPath()); ModelStage modelStage = ModelStage.make(asyncStage.event()); asyncStage.whenComplete((value, e) -> { if ( e != null ) { modelStage.completeExceptionally(e); } else { try { ZNode node = new ZNodeImpl<>(modelSpec.path(), stat, modelSpec.serializer().deserialize(value)); modelStage.complete(resolver.apply(node)); } catch ( Exception deserializeException ) { modelStage.completeExceptionally(deserializeException); } } }); return modelStage; } private String resolveForSet(T model) { if ( modelSpec.path().isResolved() ) { return modelSpec.path().fullPath(); } return modelSpec.path().resolved(model).fullPath(); } private List fixAclList(List aclList) { return (aclList.size() > 0) ? aclList : null; // workaround for old, bad design. empty list not accepted } } VersionedModeledFrameworkImpl.java000066400000000000000000000053041442004423600424150ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.details; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.versioned.Versioned; import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework; import org.apache.zookeeper.data.Stat; class VersionedModeledFrameworkImpl implements VersionedModeledFramework { private final ModeledFramework client; VersionedModeledFrameworkImpl(ModeledFramework client) { this.client = client; } @Override public AsyncStage set(Versioned model) { return client.set(model.model(), model.version()); } @Override public AsyncStage set(Versioned model, Stat storingStatIn) { return client.set(model.model(), storingStatIn, model.version()); } @Override public AsyncStage> read() { return read(null); } @Override public AsyncStage> read(Stat storingStatIn) { Stat localStat = (storingStatIn != null) ? storingStatIn : new Stat(); AsyncStage stage = client.read(localStat); ModelStage> modelStage = ModelStage.make(stage.event()); stage.whenComplete((model, e) -> { if ( e != null ) { modelStage.completeExceptionally(e); } else { modelStage.complete(Versioned.from(model, localStat.getVersion())); } }); return modelStage; } @Override public AsyncStage update(Versioned model) { return client.update(model.model(), model.version()); } @Override public CuratorOp updateOp(Versioned model) { return client.updateOp(model.model(), model.version()); } } ZNodeImpl.java000066400000000000000000000031771442004423600363340ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.details; import org.apache.curator.x.async.modeled.ZNode; import org.apache.curator.x.async.modeled.ZPath; import org.apache.zookeeper.data.Stat; import java.util.Objects; public class ZNodeImpl implements ZNode { private final ZPath path; private final Stat stat; private final T model; public ZNodeImpl(ZPath path, Stat stat, T model) { this.path = Objects.requireNonNull(path, "path cannot be null"); this.stat = Objects.requireNonNull(stat, "stat cannot be null"); this.model = Objects.requireNonNull(model, "model cannot be null"); } @Override public ZPath path() { return path; } @Override public Stat stat() { return stat; } @Override public T model() { return model; } } ZPathImpl.java000066400000000000000000000173721442004423600363450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.details; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import org.apache.curator.x.async.modeled.NodeName; import org.apache.curator.x.async.modeled.ZPath; import org.apache.zookeeper.common.PathUtils; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.function.UnaryOperator; import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.apache.curator.utils.ZKPaths.PATH_SEPARATOR; public class ZPathImpl implements ZPath { public static final ZPath root = new ZPathImpl(Collections.singletonList(PATH_SEPARATOR), null); private final List nodes; private final boolean isResolved; private volatile String fullPath = null; private volatile ZPath parent = null; private volatile Pattern schema = null; public static ZPath parse(String fullPath, UnaryOperator nameFilter) { return parseInternal(fullPath, nameFilter); } private static ZPathImpl parseInternal(String fullPath, UnaryOperator nameFilter) { List nodes = ImmutableList.builder() .add(PATH_SEPARATOR) .addAll( Splitter.on(PATH_SEPARATOR) .omitEmptyStrings() .splitToList(fullPath) .stream() .map(nameFilter) .collect(Collectors.toList()) ) .build(); nodes.forEach(ZPathImpl::validate); return new ZPathImpl(nodes, null); } public static ZPath from(String[] names) { return from(null, Arrays.asList(names)); } public static ZPath from(List names) { return from(null, names); } public static ZPath from(ZPath base, String[] names) { return from(base, Arrays.asList(names)); } public static ZPath from(ZPath base, List names) { names = Objects.requireNonNull(names, "names cannot be null"); names.forEach(ZPathImpl::validate); ImmutableList.Builder builder = ImmutableList.builder(); if ( base != null ) { if ( base instanceof ZPathImpl ) { builder.addAll(((ZPathImpl)base).nodes); } else { builder.addAll(Splitter.on(PATH_SEPARATOR).omitEmptyStrings().splitToList(base.fullPath())); } } else { builder.add(PATH_SEPARATOR); } List nodes = builder.addAll(names).build(); return new ZPathImpl(nodes, null); } @Override public ZPath child(Object child) { return new ZPathImpl(nodes, NodeName.nameFrom(child)); } @Override public ZPath parent() { checkRootAccess(); if ( parent == null ) { parent = new ZPathImpl(nodes.subList(0, nodes.size() - 1), null); } return parent; } @Override public boolean isRoot() { return nodes.size() == 1; } @Override public boolean startsWith(ZPath path) { ZPathImpl rhs; if ( path instanceof ZPathImpl ) { rhs = (ZPathImpl)path; } else { rhs = parseInternal(path.fullPath(), s -> s); } return (nodes.size() >= rhs.nodes.size()) && nodes.subList(0, rhs.nodes.size()).equals(rhs.nodes); } @Override public Pattern toSchemaPathPattern() { if ( schema == null ) { schema = Pattern.compile(buildFullPath(s -> isParameter(s) ? ".*" : s)); } return schema; } @Override public String fullPath() { checkResolved(); if ( fullPath == null ) { fullPath = buildFullPath(s -> s); } return fullPath; } @Override public String nodeName() { return nodes.get(nodes.size() - 1); } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } ZPathImpl zPaths = (ZPathImpl)o; return nodes.equals(zPaths.nodes); } @Override public int hashCode() { return nodes.hashCode(); } @Override public String toString() { return nodes.subList(1, nodes.size()) .stream().map(name -> isParameter(name) ? name.substring(1) : name) .collect(Collectors.joining(PATH_SEPARATOR, PATH_SEPARATOR, "")); } @Override public ZPath resolved(List parameters) { Iterator iterator = parameters.iterator(); List nodeNames = nodes.stream() .map(name -> { if ( isParameter(name) && iterator.hasNext() ) { return NodeName.nameFrom(iterator.next()); } return name; }) .collect(Collectors.toList()); return new ZPathImpl(nodeNames, null); } @Override public boolean isResolved() { return isResolved; } private static boolean isParameter(String name) { return (name.length() > 1) && name.startsWith(PATH_SEPARATOR); } private ZPathImpl(List nodes, String child) { ImmutableList.Builder builder = ImmutableList.builder().addAll(nodes); if ( child != null ) { validate(child); builder.add(child); } this.nodes = builder.build(); isResolved = this.nodes.stream().noneMatch(ZPathImpl::isParameter); } private void checkRootAccess() { if ( isRoot() ) { throw new NoSuchElementException("The root has no parent"); } } private void checkResolved() { if ( !isResolved) { throw new IllegalStateException("This ZPath has not been resolved: " + toString()); } } private static void validate(String nodeName) { if ( isParameter(Objects.requireNonNull(nodeName, "nodeName cannot be null")) ) { return; } if ( nodeName.equals(PATH_SEPARATOR) ) { return; } PathUtils.validatePath(PATH_SEPARATOR + nodeName); } private String buildFullPath(UnaryOperator filter) { boolean addSeparator = false; StringBuilder str = new StringBuilder(); int size = nodes.size(); int parameterIndex = 0; for ( int i = 0; i < size; ++i ) { if ( i > 1 ) { str.append(PATH_SEPARATOR); } str.append(filter.apply(nodes.get(i))); } return str.toString(); } } curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/000077500000000000000000000000001442004423600333775ustar00rootroot00000000000000TypedModelSpec.java000066400000000000000000000063201442004423600370450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** *

* Abstraction that allows the construction of ModelSpecs using strongly typed parameter replacements. * For example, given a ModelSpec with a path such as "/root/registry/people/{id}" where "id" should * be PersonId. *

* *

*


 * // Step 1. Create a typed ZPath
 * TypedZPath<PersonId> typedPath = TypedZPath.from("/root/registry/people/{id}");
 *
 * // Step 2. Create a ModelSpec builder (do not build at this point)
 * ModelSpecBuilder<Person> builder = ModelSpec.builder(JacksonModelSerializer.build(Person.class))
 *
 * // Step 3. Create a typed ModelSpec using the typed ZPath and ModelSpec builder
 * TypedModelSpec<Person, PersonId> typedModelSpec = TypedModelSpec.from(builder, path);
 *
 * // later on the TypedModelSpec can be resolved into a useable ModelSpec
 * ModelSpec<Person> modelSpec = typedModelSpec.resolve(personId);
 * 
*

*/ @FunctionalInterface public interface TypedModelSpec { /** * Resolve into a ZPath using the given parameter * * @param p1 the parameter * @return ZPath */ ModelSpec resolved(P1 p1); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec from(ModelSpecBuilder builder, TypedZPath path) { return p1 -> builder.withPath(path.resolved(p1)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath zPath = TypedZPath.from(pathWithIds); return p1 -> builder.withPath(zPath.resolved(p1)).build(); } } TypedModelSpec0.java000066400000000000000000000042541442004423600371310ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link TypedModelSpec}, but with 0 parameters */ @FunctionalInterface public interface TypedModelSpec0 { ModelSpec resolved(); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved()} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec0 from(ModelSpecBuilder builder, TypedZPath0 path) { return () -> builder.withPath(path.resolved()).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved()} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec0 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath0 zPath = TypedZPath0.from(pathWithIds); return () -> builder.withPath(zPath.resolved()).build(); } } TypedModelSpec10.java000066400000000000000000000055711442004423600372150ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 10 parameters */ @FunctionalInterface public interface TypedModelSpec10 { ModelSpec resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec10 from(ModelSpecBuilder builder, TypedZPath10 path) { return (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec10 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath10 zPath = TypedZPath10.from(pathWithIds); return (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)).build(); } } TypedModelSpec2.java000066400000000000000000000045151442004423600371330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 2 parameters */ @FunctionalInterface public interface TypedModelSpec2 { ModelSpec resolved(P1 p1, P2 p2); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec2 from(ModelSpecBuilder builder, TypedZPath2 path) { return (p1, p2) -> builder.withPath(path.resolved(p1, p2)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec2 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath2 zPath = TypedZPath2.from(pathWithIds); return (p1, p2) -> builder.withPath(zPath.resolved(p1, p2)).build(); } } TypedModelSpec3.java000066400000000000000000000046201442004423600371310ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 3 parameters */ @FunctionalInterface public interface TypedModelSpec3 { ModelSpec resolved(P1 p1, P2 p2, P3 p3); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec3 from(ModelSpecBuilder builder, TypedZPath3 path) { return (p1, p2, p3) -> builder.withPath(path.resolved(p1, p2, p3)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec3 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath3 zPath = TypedZPath3.from(pathWithIds); return (p1, p2, p3) -> builder.withPath(zPath.resolved(p1, p2, p3)).build(); } } TypedModelSpec4.java000066400000000000000000000047231442004423600371360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 4 parameters */ @FunctionalInterface public interface TypedModelSpec4 { ModelSpec resolved(P1 p1, P2 p2, P3 p3, P4 p4); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec4 from(ModelSpecBuilder builder, TypedZPath4 path) { return (p1, p2, p3, p4) -> builder.withPath(path.resolved(p1, p2, p3, p4)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec4 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath4 zPath = TypedZPath4.from(pathWithIds); return (p1, p2, p3, p4) -> builder.withPath(zPath.resolved(p1, p2, p3, p4)).build(); } } TypedModelSpec5.java000066400000000000000000000050261442004423600371340ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 5 parameters */ @FunctionalInterface public interface TypedModelSpec5 { ModelSpec resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec5 from(ModelSpecBuilder builder, TypedZPath5 path) { return (p1, p2, p3, p4, p5) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec5 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath5 zPath = TypedZPath5.from(pathWithIds); return (p1, p2, p3, p4, p5) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5)).build(); } } TypedModelSpec6.java000066400000000000000000000051311442004423600371320ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 6 parameters */ @FunctionalInterface public interface TypedModelSpec6 { ModelSpec resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec6 from(ModelSpecBuilder builder, TypedZPath6 path) { return (p1, p2, p3, p4, p5, p6) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec6 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath6 zPath = TypedZPath6.from(pathWithIds); return (p1, p2, p3, p4, p5, p6) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6)).build(); } } TypedModelSpec7.java000066400000000000000000000052341442004423600371370ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 7 parameters */ @FunctionalInterface public interface TypedModelSpec7 { ModelSpec resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec7 from(ModelSpecBuilder builder, TypedZPath7 path) { return (p1, p2, p3, p4, p5, p6, p7) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6, p7)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec7 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath7 zPath = TypedZPath7.from(pathWithIds); return (p1, p2, p3, p4, p5, p6, p7) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6, p7)).build(); } } TypedModelSpec8.java000066400000000000000000000053371442004423600371440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 8 parameters */ @FunctionalInterface public interface TypedModelSpec8 { ModelSpec resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec8 from(ModelSpecBuilder builder, TypedZPath8 path) { return (p1, p2, p3, p4, p5, p6, p7, p8) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6, p7, p8)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec8 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath8 zPath = TypedZPath8.from(pathWithIds); return (p1, p2, p3, p4, p5, p6, p7, p8) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6, p7, p8)).build(); } } TypedModelSpec9.java000066400000000000000000000054421442004423600371420ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModelSpecBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModelSpec}, but with 9 parameters */ @FunctionalInterface public interface TypedModelSpec9 { ModelSpec resolved(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9); /** * Return a new TypedModelSpec using the given model spec builder and typed path. When * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param path typed path * @return new TypedModelSpec */ static TypedModelSpec9 from(ModelSpecBuilder builder, TypedZPath9 path) { return (p1, p2, p3, p4, p5, p6, p7, p8, p9) -> builder.withPath(path.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9)).build(); } /** * Return a new TypedModelSpec using the given model spec builder and path. A TypedZPath * is created from the given full path and When * {@link #resolved(Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual model spec is generated with the * resolved path * * @param builder model spec builder * @param pathWithIds typed path * @return new TypedModelSpec */ static TypedModelSpec9 from(ModelSpecBuilder builder, String pathWithIds) { TypedZPath9 zPath = TypedZPath9.from(pathWithIds); return (p1, p2, p3, p4, p5, p6, p7, p8, p9) -> builder.withPath(zPath.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9)).build(); } } TypedModeledFramework.java000066400000000000000000000102161442004423600404200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** *

* Abstraction that allows the construction of ModeledFrameworks using strongly typed parameter replacements. * For example, given a ModeledFramework with a ModelSpec that has a path such as * "/root/registry/people/{id}" where "id" should be PersonId. *

* *

*


 * // Step 1. Create a typed ZPath
 * TypedZPath<PersonId> typedPath = TypedZPath.from("/root/registry/people/{id}");
 *
 * // Step 2. Create a typed ModelSpec (see TypedModelSpec for details)
 * TypedModelSpec<Person, PersonId> typedModelSpec = TypedModelSpec.from(builder, path);
 *
 * // Step 3. Create a ModeledFramework builder (do not build at this point)
 * ModeledFrameworkBuilder<Person> builder = ModeledFramework.builder()... // add any other needed options
 *
 * // Step 4. Create a typed TypedModeledFramework using the typed ZPath, typed ModelSpec, and ModeledFramework builder
 * TypedModeledFramework<Person, PersonId> clientSpec = TypedModeledFramework.from(builder, modelSpec);
 *
 * // later on the TypedModelSpec can be resolved into a useable ModeledFramework
 * ModeledFramework<Person> client = clientSpec.resolve(personId);
 * 
*

*/ @FunctionalInterface public interface TypedModeledFramework { /** * Resolve into a ModeledFramework using the given parameter * * @param client the curator instance to use * @param p1 the parameter * @return ZPath */ ModeledFramework resolved(AsyncCuratorFramework client, P1 p1); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec modelSpec) { return (client, p1) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec typedModelSpec = TypedModelSpec.from(modelSpecBuilder, pathWithIds); return (client, p1) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1)).build(); } } TypedModeledFramework0.java000066400000000000000000000055071442004423600405070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link TypedModeledFramework}, but with 0 parameters */ @FunctionalInterface public interface TypedModeledFramework0 { ModeledFramework resolved(AsyncCuratorFramework client); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(org.apache.curator.x.async.AsyncCuratorFramework)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework0 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec0 modelSpec) { return (client) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved()).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(org.apache.curator.x.async.AsyncCuratorFramework)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework0 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec0 typedModelSpec = TypedModelSpec0.from(modelSpecBuilder, pathWithIds); return (client) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved()).build(); } } TypedModeledFramework10.java000066400000000000000000000067501442004423600405710ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 10 parameters */ @FunctionalInterface public interface TypedModeledFramework10 { ModeledFramework resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9, P10 p10); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework10 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec10 modelSpec) { return (client, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework10 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec10 typedModelSpec = TypedModelSpec10.from(modelSpecBuilder, pathWithIds); return (client, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)).build(); } } TypedModeledFramework2.java000066400000000000000000000056741442004423600405160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 2 parameters */ @FunctionalInterface public interface TypedModeledFramework2 { ModeledFramework resolved(AsyncCuratorFramework client, P1 p1, P2 p2); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework2 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec2 modelSpec) { return (client, p1, p2) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework2 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec2 typedModelSpec = TypedModelSpec2.from(modelSpecBuilder, pathWithIds); return (client, p1, p2) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2)).build(); } } TypedModeledFramework3.java000066400000000000000000000057771442004423600405230ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 3 parameters */ @FunctionalInterface public interface TypedModeledFramework3 { ModeledFramework resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework3 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec3 modelSpec) { return (client, p1, p2, p3) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework3 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec3 typedModelSpec = TypedModelSpec3.from(modelSpecBuilder, pathWithIds); return (client, p1, p2, p3) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3)).build(); } } TypedModeledFramework4.java000066400000000000000000000061021442004423600405030ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 4 parameters */ @FunctionalInterface public interface TypedModeledFramework4 { ModeledFramework resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework4 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec4 modelSpec) { return (client, p1, p2, p3, p4) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework4 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec4 typedModelSpec = TypedModelSpec4.from(modelSpecBuilder, pathWithIds); return (client, p1, p2, p3, p4) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4)).build(); } } TypedModeledFramework5.java000066400000000000000000000062051442004423600405100ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 5 parameters */ @FunctionalInterface public interface TypedModeledFramework5 { ModeledFramework resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework5 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec5 modelSpec) { return (client, p1, p2, p3, p4, p5) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework5 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec5 typedModelSpec = TypedModelSpec5.from(modelSpecBuilder, pathWithIds); return (client, p1, p2, p3, p4, p5) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5)).build(); } } TypedModeledFramework6.java000066400000000000000000000063101442004423600405060ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 6 parameters */ @FunctionalInterface public interface TypedModeledFramework6 { ModeledFramework resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework6 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec6 modelSpec) { return (client, p1, p2, p3, p4, p5, p6) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework6 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec6 typedModelSpec = TypedModelSpec6.from(modelSpecBuilder, pathWithIds); return (client, p1, p2, p3, p4, p5, p6) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6)).build(); } } TypedModeledFramework7.java000066400000000000000000000064131442004423600405130ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 7 parameters */ @FunctionalInterface public interface TypedModeledFramework7 { ModeledFramework resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework7 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec7 modelSpec) { return (client, p1, p2, p3, p4, p5, p6, p7) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6, p7)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework7 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec7 typedModelSpec = TypedModelSpec7.from(modelSpecBuilder, pathWithIds); return (client, p1, p2, p3, p4, p5, p6, p7) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6, p7)).build(); } } TypedModeledFramework8.java000066400000000000000000000065161442004423600405200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 8 parameters */ @FunctionalInterface public interface TypedModeledFramework8 { ModeledFramework resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework8 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec8 modelSpec) { return (client, p1, p2, p3, p4, p5, p6, p7, p8) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework8 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec8 typedModelSpec = TypedModelSpec8.from(modelSpecBuilder, pathWithIds); return (client, p1, p2, p3, p4, p5, p6, p7, p8) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8)).build(); } } TypedModeledFramework9.java000066400000000000000000000066211442004423600405160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.modeled.ModelSpecBuilder; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ModeledFrameworkBuilder; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedModeledFramework}, but with 9 parameters */ @FunctionalInterface public interface TypedModeledFramework9 { ModeledFramework resolved(AsyncCuratorFramework client, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9); /** * Return a new TypedModeledFramework using the given modeled framework builder and typed model spec. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpec TypedModelSpec * @return new TypedModeledFramework */ static TypedModeledFramework9 from(ModeledFrameworkBuilder frameworkBuilder, TypedModelSpec9 modelSpec) { return (client, p1, p2, p3, p4, p5, p6, p7, p8, p9) -> frameworkBuilder.withClient(client).withModelSpec(modelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9)).build(); } /** * Return a new TypedModeledFramework using the given modeled framework builder, model spec builder and a path with ids. * When {@link #resolved(AsyncCuratorFramework, Object, Object, Object, Object, Object, Object, Object, Object, Object)} is called the actual ModeledFramework is generated with the * resolved model spec and resolved path * * @param frameworkBuilder ModeledFrameworkBuilder * @param modelSpecBuilder model spec builder * @param pathWithIds path with {XXXX} parameters * @return new TypedModeledFramework */ static TypedModeledFramework9 from(ModeledFrameworkBuilder frameworkBuilder, ModelSpecBuilder modelSpecBuilder, String pathWithIds) { TypedModelSpec9 typedModelSpec = TypedModelSpec9.from(modelSpecBuilder, pathWithIds); return (client, p1, p2, p3, p4, p5, p6, p7, p8, p9) -> frameworkBuilder.withClient(client).withModelSpec(typedModelSpec.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9)).build(); } } TypedZPath.java000066400000000000000000000051111442004423600362150ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** *

* Abstraction that allows the construction of ZPaths using strongly typed parameter replacements. * For example, given a path such as "/root/registry/people/{id}" where "id" should be PersonId. *

* *

*


 * TypedZPath<PersonId> typedPath = TypedZPath.from("/root/registry/people/{id}");
 *
 * ...
 *
 * ZPath path = typedPath.resolved(personId);
 * 
*

* *

* Additionally, if you have a model/class that implements {@link org.apache.curator.x.async.modeled.NodeName} * you can pass that when resolving. E.g. *

* *

*


 * public class MyModel implements NodeName {
 *     ...
 *     public String nodeName() {
 *         return modelId;
 *     }
 * }
 *
 * TypedZPath<MyModel> typedPath = TypedZPath.from("/foo/bar/{id}");
 *
 * MyModel model = ...
 * ZPath path = typedPath.resolved(model);
 * 
*

*/ @FunctionalInterface public interface TypedZPath { /** * Resolve into a ZPath using the given parameter * * @param p1 the parameter * @return ZPath */ ZPath resolved(T p1); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath from(ZPath path) { return path::resolved; } } TypedZPath0.java000066400000000000000000000031021442004423600362730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link TypedZPath}, but with 0 parameters. */ @FunctionalInterface public interface TypedZPath0 { ZPath resolved(); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath0 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath0 from(ZPath path) { return path::resolved; } } TypedZPath10.java000066400000000000000000000037301442004423600363630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 10 parameters */ @FunctionalInterface public interface TypedZPath10 { ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8, T9 p9, T10 p10); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath10 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath10 from(ZPath path) { return (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) -> path.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } } TypedZPath2.java000066400000000000000000000032631442004423600363050ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 2 parameters */ @FunctionalInterface public interface TypedZPath2 { ZPath resolved(T1 p1, T2 p2); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath2 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath2 from(ZPath path) { return (p1, p2) -> path.resolved(p1, p2); } } TypedZPath3.java000066400000000000000000000033261442004423600363060ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 3 parameters */ @FunctionalInterface public interface TypedZPath3 { ZPath resolved(T1 p1, T2 p2, T3 p3); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath3 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath3 from(ZPath path) { return (p1, p2, p3) -> path.resolved(p1, p2, p3); } } TypedZPath4.java000066400000000000000000000033711442004423600363070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 4 parameters */ @FunctionalInterface public interface TypedZPath4 { ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath4 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath4 from(ZPath path) { return (p1, p2, p3, p4) -> path.resolved(p1, p2, p3, p4); } } TypedZPath5.java000066400000000000000000000034341442004423600363100ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 5 parameters */ @FunctionalInterface public interface TypedZPath5 { ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath5 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath5 from(ZPath path) { return (p1, p2, p3, p4, p5) -> path.resolved(p1, p2, p3, p4, p5); } } TypedZPath6.java000066400000000000000000000034771442004423600363200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 6 parameters */ @FunctionalInterface public interface TypedZPath6 { ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath6 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath6 from(ZPath path) { return (p1, p2, p3, p4, p5, p6) -> path.resolved(p1, p2, p3, p4, p5, p6); } } TypedZPath7.java000066400000000000000000000035421442004423600363120ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 7 parameters */ @FunctionalInterface public interface TypedZPath7 { ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath7 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath7 from(ZPath path) { return (p1, p2, p3, p4, p5, p6, p7) -> path.resolved(p1, p2, p3, p4, p5, p6, p7); } } TypedZPath8.java000066400000000000000000000036051442004423600363130ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 8 parameters */ @FunctionalInterface public interface TypedZPath8 { ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath8 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath8 from(ZPath path) { return (p1, p2, p3, p4, p5, p6, p7, p8) -> path.resolved(p1, p2, p3, p4, p5, p6, p7, p8); } } TypedZPath9.java000066400000000000000000000036501442004423600363140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/typed/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.typed; import org.apache.curator.x.async.modeled.ZPath; /** * Same as {@link org.apache.curator.x.async.modeled.typed.TypedZPath}, but with 9 parameters */ @FunctionalInterface public interface TypedZPath9 { ZPath resolved(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8, T9 p9); /** * Return a TypedZPath using {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * * @param pathWithIds path to pass to {@link org.apache.curator.x.async.modeled.ZPath#parseWithIds} * @return TypedZPath */ static TypedZPath9 from(String pathWithIds) { return from(ZPath.parseWithIds(pathWithIds)); } /** * Return a TypedZPath * * @param path path to use * @return TypedZPath */ static TypedZPath9 from(ZPath path) { return (p1, p2, p3, p4, p5, p6, p7, p8, p9) -> path.resolved(p1, p2, p3, p4, p5, p6, p7, p8, p9); } } versioned/000077500000000000000000000000001442004423600341715ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeledVersioned.java000066400000000000000000000035141442004423600367750ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.versioned; /** * A container for a model instance and a version. Can be used with the * {@link org.apache.curator.x.async.modeled.ModeledFramework#versioned()} APIs */ @FunctionalInterface public interface Versioned { /** * Returns the contained model * * @return model */ T model(); /** * Returns the version of the model when it was read * * @return version */ default int version() { return -1; } /** * Return a new Versioned wrapper for the given model and version * * @param model model * @param version version * @return new Versioned wrapper */ static Versioned from(T model, int version) { return new Versioned() { @Override public int version() { return version; } @Override public T model() { return model; } }; } } VersionedModeledFramework.java000066400000000000000000000036771442004423600421570ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/versioned/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.versioned; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.x.async.AsyncStage; import org.apache.zookeeper.data.Stat; public interface VersionedModeledFramework { /** * @see org.apache.curator.x.async.modeled.ModeledFramework#set(Object) */ AsyncStage set(Versioned model); /** * @see org.apache.curator.x.async.modeled.ModeledFramework#set(Object, org.apache.zookeeper.data.Stat) */ AsyncStage set(Versioned model, Stat storingStatIn); /** * @see org.apache.curator.x.async.modeled.ModeledFramework#read() */ AsyncStage> read(); /** * @see org.apache.curator.x.async.modeled.ModeledFramework#read(org.apache.zookeeper.data.Stat) */ AsyncStage> read(Stat storingStatIn); /** * @see org.apache.curator.x.async.modeled.ModeledFramework#updateOp(Object) */ AsyncStage update(Versioned model); /** * @see org.apache.curator.x.async.modeled.ModeledFramework#updateOp(Object) */ CuratorOp updateOp(Versioned model); } curator-apache-curator-5.5.0/curator-x-async/src/site/000077500000000000000000000000001442004423600226655ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/site/confluence/000077500000000000000000000000001442004423600250065ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/site/confluence/async.confluence000066400000000000000000000147771442004423600302060ustar00rootroot00000000000000h1. Curator Async With this DSL you can do asynchronous tasks in a more natural, functional way using [Java 8 lambdas|https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html]. For example: {code} // let "client" be a CuratorFramework instance AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.checkExists().forPath(somePath).thenAccept(stat -> mySuccessOperation(stat)); {code} h2. Usage Note: To use Curator Async, you should be familiar with Java 8's lambdas, CompletedFuture and CompletionStage. Create a [[CuratorFramework|../curator\-framework/index.html]] instance in the normal way. You then wrap this instance using AsyncCuratorFramework. i.e. {code} // let "client" be a CuratorFramework instance AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); {code} AsyncCuratorFramework has most of the same builder methods that CuratorFramework does with some important differences: * AsyncCuratorFramework builders return {{AsyncStage}} instances * AsyncCuratorFramework builders have no checked exceptions * Many of the builder methods have been simplified/clarified * All builders invoke the asynchronous versions of ZooKeeper APIs * Watchers also use CompletionStages \- see below for details h4. AsyncStage AsyncStage instances extend Java 8's CompletionStage. CompletionStage objects can be "completed" with a success value or an exception. The parameterized type of the AsyncStage will be whatever the builder used would naturally return as a success value. E.g. the async getData() builder's AsyncStage is parameterized with "byte\[\]". h4. Watchers ZooKeeper watchers also get the CompletionStage treatment in Curator Async. To add a watcher, call watched() prior to starting the appropriate builders. E.g. {code} async.watched().getData().forPath(path) ... {code} Thus, a data watcher will be set on the specified path. You access the CompletionStage for the watcher by using the event() method of AsyncStage. Here is a complete example: {code} async.watched().getData().forPath(path).event().thenAccept(watchedEvent -> watchWasTriggered(watchedEvent)); {code} ZooKeeper calls watchers when there is a connection loss. This can make using the CompletionStage somewhat complicated (see AsyncEventException below). If you are not interested in watcher connection problems, you can tell Curator Async to not send them by calling: {code} // only complete the CompletionStage when the watcher is successfully triggered // i.e. don't complete on connection issues async.with(WatchMode.successOnly).watched()... {code} h4. AsyncEventException When an async watcher fails the exception set in the CompletionStage will be of type {{AsyncEventException}}. This exception allows you to see the KeeperState that caused the trigger and allows you to reset the completion stage. Reset is needed because ZooKeeper temporarily triggers watchers when there is a connection event (unless {{WatchMode.successOnly}} is used). However, the watcher stays set for the original operation. Use {{AsyncEventException#reset}} to start a new completion stage that will wait on the next trigger of the watcher. E.g. {code} AsyncStage stage = ... stage.event().exceptionally(e -> { AsyncEventException asyncEx = (AsyncEventException)e; ... note a connection problem ... asyncEx.reset().thenAccept(watchedEvent -> watchWasTriggered(watchedEvent)); }); {code} h4. AsyncResult As a convenience, you can use {{AsyncResult}} to combine ZooKeeper method value, the ZooKeeper result code and any exception in one object allowing you to not worry about exceptional completions. i.e. the {{CompletionStage}} returned by {{AsyncResult.of()}} always completes successfully with an AsyncResult object. AsyncResult has methods to get either the method result (a path, Stat, etc.), a KeeperException code or a general exception: {code} Optional getValue(); KeeperException.Code getCode(); Optional getException(); {code} Use AsyncResult by wrapping an {{AsyncStage}} value. i.e. {code} CompletionStage> resultStage = AsyncResult.of(async.checkExists().forPath(path)); resultStage.thenAccept(result -> { if ( result.getValue().isPresent() ) { // ... } else if ( result.getCode() == KeeperException.Code.NOAUTH ) { // ... } // etc. }); {code} h2. Examples h4. Create a sequential ZNode Create a sequential ZNode and, once successfully completed, set a watcher on the ZNode. Note: this code does not deal with errors. Should a connection problem occur or another exception occur, the completion lambda will never be called. {code} async.create().withMode(PERSISTENT_SEQUENTIAL).forPath(path).thenAccept(actualPath -> async.watched().getData().forPath(actualPath).thenApply(() -> watchTriggered())); {code} ---- h4. AsyncStage canonical usage This is the canonical way to deal with AsyncStage. Use the handle() method which provides both the success value and the exception. The exception will be non\-null on error. {code} async.create().withOptions(EnumSet.of(doProtected)).forPath(path).handle((actualPath, exception) -> { if ( exception != null ) { // handle problem } else { // actualPath is the path created } return null; }); {code} ---- h4. Simplified usage via AsyncResult {code} AsyncResult.of(async.create().withOptions(EnumSet.of(doProtected)).forPath(path)).thenAccept(result -> { if ( result.getRawValue() != null ) { // result.getRawValue() is the path created } else { // ... } }); {code} ---- h4. Using executors Your completion routines can operate in a separate thread if you provide an executor. {code} async.create().withOptions(EnumSet.of(createParentsIfNeeded)).forPath("/a/b/c") .thenAcceptAsync(path -> handleCreate(path), executor); {code} ---- h4. Separate handlers This example shows specifying separate completion handlers for success and exception. {code} AsyncStage stage = async.getData().forPath("/my/path"); stage.exceptionally(e -> { if ( e instanceof KeeperException.NoNodeException ) { // handle no node } else { // handle other } return null; }); stage.thenAccept(data -> processData(data)); {code} ---- h4. Synchronous usage CompletionStage provides a blocking method as well so that you can block to get the result of an operation. i.e. this makes it possible to use the async APIs in a synchronous way. {code} // NOTE: get() specifies a checked exception async.create().forPath("/foo").toCompletableFuture().get(); {code} curator-apache-curator-5.5.0/curator-x-async/src/site/confluence/index.confluence000066400000000000000000000044411442004423600301630ustar00rootroot00000000000000h1. Curator Async h2. Packaging Curator Async is in its own package in Maven Central: curator\-x\-async h2. What Is Curator Async? Curator Async is a [DSL|https://en.wikipedia.org/wiki/Domain-specific_language] that wraps existing {{CuratorFramework}} instances. This DSL is entirely asynchronous and uses [Java 8's CompletionStage|https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html] mechanism for chaining, composing, etc. Additionally, Curator's original DSL has been cleaned up and simplified, in particular for operations such as {{create()}}. With this DSL you can do asynchronous tasks in a more natural, functional way using [Java 8 lambdas|https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html]. The Curator Async package also contains a strongly typed DSL and strongly typed Cache Recipe wrappers that allows you to map a ZooKeeper path to a serializable class as opposed to raw byte arrays. h2. [[Curator Async|async.html]] With this DSL you can do asynchronous tasks in a more natural, functional way using [Java 8 lambdas|https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html]. For example: {code} // let "client" be a CuratorFramework instance AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.checkExists().forPath(somePath).thenAccept(stat -> mySuccessOperation(stat)); {code} See [[Curator Async|async.html]] for details. h2. [[Modeled Curator|modeled.html]] This is a strongly typed DSL that allows you to map a Curator\-style client to: * A ZooKeeper path (supporting parameterized substitutions) * A serializer for the data stored at the path * Options for how nodes should be created (sequential, compressed data, ttl, etc.) * ACLs for the nodes at the path * Options for how to delete nodes (guaranteed, deleting children, etc.) For example: {code} ModeledFramework modeled = ModeledFramework.wrap(client, fooModelSpec); modeled.set(new Foo()); {code} See [[Modeled Curator|modeled.html]] for details. h2. [[Migrations|migrations.html]] Curator Migrations allow you pre\-apply transactions in a staged manner so that you can ensure a consistent state for parts of your ZooKeeper node hierarchy in a manner similar to database migration utilities. See [[Migrations|migrations.html]] for details. curator-apache-curator-5.5.0/curator-x-async/src/site/confluence/migrations.confluence000066400000000000000000000077011442004423600312320ustar00rootroot00000000000000h1. Migrations Curator Migrations allow you pre\-apply transactions in a staged manner so that you can ensure a consistent state for parts of your ZooKeeper node hierarchy in a manner similar to database migration utilities. h2. Background and Usage Note: To use Migrations, you should be familiar with Java 8's lambdas, CompletedFuture and CompletionStage. A "migration" is a set of operations to be performed in a transaction. A "migration set" is a list of migrations. Combined, this can be used to ensure an initial state for your ZooKeeper nodes as well as supporting upgrading/modifying existing state. For example, given a brand new ZooKeeper instance you might want to populate a few nodes and data. E.g. {code} CuratorOp op1 = client.transactionOp().create().forPath("/parent"); CuratorOp op2 = client.transactionOp().create().forPath("/parent/one"); CuratorOp op3 = client.transactionOp().create().forPath("/parent/two"); CuratorOp op4 = client.transactionOp().create().forPath("/parent/three"); CuratorOp op5 = client.transactionOp().create().forPath("/main", someData); {code} All 5 of these operations would be combined into a migration and set: {code} Migration migration = () -> Arrays.asList(op1, op2, op3, op4, op5); MigrationSet set = MigrationSet.build("main", Collections.singletonList(migration)); {code} This set can then be passed to a {{MigrationManager}} for processing. The MigrationManager checks to see if the migration has been applied already and, if not, processes the transaction. At a future date, the migration set could be expanded to update/modify things. E.g. {code} CuratorOp newOp1 = client.transactionOp().create().forPath("/new"); CuratorOp newOp2 = client.transactionOp().delete().forPath("/main"); // maybe this is no longer needed {code} This would be combined with the previous migration: {code} Migration initialMigration = () -> Arrays.asList(op1, op2, op3, op4, op5); Migration newMigration = () -> Arrays.asList(newOp1, newOp2); MigrationSet set = MigrationSet.build("main", Arrays.asList(initialMigration, newMigration)); {code} When this set is run, the MigrationManager will perform both migration operations on new ZooKeeper databases but only the second "newMigration" on ZK databases that already have the first migration applied. h2. Details/Reference _Migration_ A Migration is a wrapper around a list of operations that constitute one stage in a migration set and are applied as a single transaction. _MigrationSet_ A MigrationSet is an ordered list of Migrations. Curator keeps track of which migrations in a set have been previously applied and only processes un\-applied migrations. Each migration set must have a unique identifier. Create a MigrationSet via its builder: {code} MigrationSet set = MigrationSet.build(migrationId, migrations); {code} _MigrationManager_ The MigrationManager processes MigrationSets. Usually, you'd run this only on new ZooKeeper databases or as part of a maintenance operation to update the ZooKeeper database. E.g. {code} MigrationManager manager = new MigrationManager(client, lockPath, // base path for locks used by the manager metaDataPath, // base path to store the meta data executor, // the executor to use lockMax // max time to wait for locks ); manager.migrate(set).exceptionally(e -> { if ( e instanceof MigrationException ) { // migration checksum failed, etc. } else { // some other kind of error } return null; }); {code} * Each migration in the set is applied in a single transaction - i.e. all operations that comprise a migration set (the sum of all individual migration operations) are sent to ZooKeeper as a single transaction. * MigrationManager stores a hash of all operations in a migration so that it can be compared for future operations. i.e. if, in the future, a migration set is attempted but the hash of one of the previous migrations does not match, the stage completes exceptionally with {{MigrationException}}. curator-apache-curator-5.5.0/curator-x-async/src/site/confluence/modeled-components.confluence000066400000000000000000000135171442004423600326540ustar00rootroot00000000000000h1. Modeled Curator \- Components Modeled Curator components are intended to allow you to model your ZooKeeper usage early in your application so that the majority of the code that interacts with ZooKeeper doesn't need to be concerned with paths, byte arrays, ACLs, options, etc. The [[Pub\-Sub Example|https://github.com/apache/curator/tree/master/curator-examples/src/main/java/pubsub]] can give you some ideas on how to accomplish this. h2. ZPath Instead of using raw string paths, Modeled Curator defines the {{ZPath}} interface that abstracts ZooKeeper paths. ZPaths can be simple static paths or can contain parameters that can be replaced as needed. To build a simple static path, use: {code} ZPath path = ZPath.parse("/my/static/path"); {code} To build a path with parameters, use. {{ZPath.parseWithIds()}} using the value "\{XXXX\}" to denote a parameter. You can then use the {{resolve()}} method to replace the parameters. The value between "\{\}" can be any value. E.g. {code} ZPath path = ZPath.parseWithIds("/foo/{first param}/bar/{second param}"); ... ZPath resolvedPath = path.resolve(param1, param2); {code} h3. NodeName Parameters are resolved by calling {{toString()}} on the parameter. You can use {{NodeName}} to change this behavior. If a parameter implements {{NodeName}} the {{nodeName()}} method is used as the parameter value. h3. Partial Resolution Note: ZPaths can be partially resolved. E.g. {code} ZPath path = ZPath.parseWithIds("/foo/{type}/bar/{id}"); ... ZPath partial = path.resolve("standard"); // partial is now "/foo/standard/bar/{id}" {code} ModeledFramework takes advantage of this. [[See below|#ModeledFramework]] for details. h2. ModelSpec A {{ModelSpec}} contains all the metadata needed to operate on a ZooKeeper path: * A ZPath * A serializer for the data stored at the path * Options for how nodes should be created (sequential, compressed data, ttl, etc.) * ACLs for the nodes at the path * Options for how to delete nodes (guaranteed, deleting children, etc.) ModelSpec instances are created via a builder. The builder sets defaults that should be useful for most applications but you can alter any of these as needed. {code} // a standard model spec for the given path and serializer // the model spec will have no ACLs and the options: // * createParentsAsContainers // * setDataIfExists // * DeleteOption.guaranteed ModelSpec spec = ModelSpec.builder(path, JacksonModelSerializer.build(MyModel.class)).build(); {code} As a convenience, ModelSpec provides {{resolve()}} methods in case the ZPath used has parameters. E.g. {code} ZPath path = ZPath.parseWithIds("/foo/{id}/bar/{id}"); ModelSpec spec = ModelSpec.builder(path, JacksonModelSerializer.build(MyModel.class)).build(); ... ModelSpec resolvedSpec = spec.resolve(param1, param2); {code} h3. JacksonModelSerializer A Jackson serializer, {{JacksonModelSerializer}}, is included. However, the Jackson dependency for it is specified as "provided" in the curator\-x\-async Maven POM file to avoid adding a new dependency to Curator. Therefore, if you wish to use the JacksonModelSerializer you must manually add the dependency to your build system. E.g. for Maven: {code} com.fasterxml.jackson.core jackson-databind XXXX {code} h2. ModeledFramework {{ModeledFramework}} ties together all the metadata into a Curator\-style instance that is used to perform ZooKeeper operations. E.g. {code} ModeledFramework modeledClient = ModeledFramework.wrap(client, myModelSpec); ... MyModel instance = ... modeledClient.set(instance); {code} The "set" call in the above example is the equivalent of: {code} MyModel instance = ... String path = "/foo/bar/" + instance.getId(); byte[] data = serializer.serialize(instance); client.create() .withOptions(Sets.newHashSet(CreateOption.createParentsAsContainers, CreateOption.setDataIfExists)) .forPath(path, data); {code} To get a value: {code} ModeledFramework modeledClient = ModeledFramework.wrap(client, myModelSpec); ... modeledClient.read().whenComplete((value, e) -> { if ( e != null ) { // handle the error } else { // "value" is the MyModel instance } }); {code} The "read" call in the above example is the equivalent of: {code} String path = "/foo/bar/" + instanceId; client.getData().forPath(path).whenComplete((data, e) -> { if ( e != null ) { // handle the error } else { // NOTE: you must deal with possible deserialization problems // caused by clients that write bad data // If all of your code uses ModeledFramework you can guarantee that // the data is always correctly written MyModel model = serializer.deserialize(data); // ... } }); {code} h3. Partially Resolved ZPaths and Set/Update ModeledFramework's various {{set}} and {{update}} methods check for unresolved ZPaths. If the current modelSpec has an unresolved ZPath when set/update is called, it is automatically resolved using the model instance being set/updated. E.g. {code} ZPath path = ZPath.parseWithIds("/root/{type}/instance/{id}"); ModelSpec modelSpec = ModelSpec.builder(path, serializer); ModeledFramework modeledClient = ModeledFramework.wrap(modelSpec, client, modelSpec); ... String currentType = ... MyModel model = ... modeledClient.resolved(currentType).set(model); // internally, ModeledFramework calls ZPath.resolved() // using "model" as the argument to get the actual ZPath {code} h2. Caching and Typed Parameters In addition to the above features, Modeled Curator supports [[Integrated Caching|modeled-typed.html]], [[Typed Parameters|modeled-typed.html]] and [[Versioning|modeled-typed.html]]. See [[Caching and Typed Parameters|modeled-typed.html]] for details. curator-apache-curator-5.5.0/curator-x-async/src/site/confluence/modeled-typed.confluence000066400000000000000000000061141442004423600316070ustar00rootroot00000000000000h1. Modeled Curator \- Caching, Typed Parameters and Versioning In addition to its [[main features|modeled-components.html]] Modeled Curator also supports integrated caching, typed parameters and versioning. h2. Caching {{ModeledFramework}} instances can be wrapped with a facade that uses a Curator cache internally. All read operations use this cache instead of making direct ZooKeeper calls. You can also listen for node changes. E.g. {code} ModeledFramework modeledClient = ModeledFramework.wrap(client, myModelSpec); CachedModeledFramework cached = modeledClient.cached(); cached.start(); // reads come from the cache cached.read().whenComplete(...) ... cached.listenable.addListener((type, path, stat, model) -> { // type is NODE_ADDED, NODE_UPDATED, etc. }); {code} h3. Unresolved Paths and Caching If the last node in the ModelSpec's path is a parameter, CachedModeledFramework will automatically listen to the parent path. E.g. {code} ZPath path = ZPath.parseWithIds("/root/instance/{id}"); ModelSpec modelSpec = ModelSpec.builder(path, serializer); ModeledFramework modeledClient = ModeledFramework.wrap(modelSpec, client, modelSpec); CachedModeledFramework cached = modeledClient.cached(); cached.start(); // automatically listens to "/root/instance" and below {code} h2. Typed Parameters The "resolve" methods in ZPath et al consume untyped Objects. Ideally, we should be able to specify parameters in a strongly typed manner. Modeled Curator's "type" templates provide this. You can specify typed parameters for ZPaths, ModelSpecs and ModeledFramework. The [[Pub\-Sub Example|https://github.com/apache/curator/tree/master/curator-examples/src/main/java/pubsub]] shows how to use typed parameters with ModeledFramework. Typed interfaces are provided for up to 10 parameters and are named {{TypedZPath}}, {{TypedZPath2}}, {{TypedModelSpec}}, {{TypedModelSpec2}}, {{TypedModeledFramework}}, {{TypedModeledFramework2}}, etc. Here's an example of a TypedModeledFramework that models a Person and uses two parameters to generate the path, a Group and an Organization: {code} TypedModeledFramework2 clientTemplate = TypedModeledFramework2.from( ModeledFrameworkBuilder.build(), personModelSpec ); ... Group group = ... Organization organization = ... ModeledFramework modeledClient = clientTemplate.resolve(asyncClient, group, organization); client.set(person); {code} TypedZPath and TypedModelSpec work similarly. h2. Versioning Modeled Curator supports associating a ZNode version with a model object via the {{Versioned}} interface and the {{VersionedModeledFramework}} APIs. To read a model along with its ZNode version use: {code} ModeledFramework client = ... client.versioned().read().whenComplete((value, e) -> { if ( value != null ) { // value's type is Versioned Person personModel = value.model(); int znodeVersion = value.version(); } }); {code} {{VersionedModeledFramework}} has set/update APIs which automatically use the version from the {{Versioned}} instance. curator-apache-curator-5.5.0/curator-x-async/src/site/confluence/modeled.confluence000066400000000000000000000031241442004423600304620ustar00rootroot00000000000000h1. Modeled Curator This is a strongly typed DSL that allows you to map a Curator\-style client to: * A ZooKeeper path (supporting parameterized substitutions) * A serializer for the data stored at the path * Options for how nodes should be created (sequential, compressed data, ttl, etc.) * ACLs for the nodes at the path * Options for how to delete nodes (guaranteed, deleting children, etc.) For example: {code} ModeledFramework modeled = ModeledFramework.wrap(client, fooModelSpec); modeled.set(new Foo()); {code} This ModeledFramework instance knows the path to use, how to serialize the "Foo" instance, which create options and ACLs to use, etc. h2. Background and Usage Note: To use Modeled Curator, you should be familiar with Java 8's lambdas, CompletedFuture and CompletionStage. You should also be familiar with [[Curator Async|async.html]] as Modeled Curator is based on it. Modeled Curator consists of the components: * [[ZPath|modeled-components.html]] * [[ModelSpec|modeled-components.html]] * [[ModeledFramework|modeled-components.html]] Additional functionality is provided by: * [[CachedModeledFramework|modeled-typed.html]] * [[Typed Parameter Templates|modeled-typed.html]] * [[Versioning|modeled-typed.html]] h2. Example A complete example usage of Modeled Curator along with CachedModeledFramework and Typed Parameter Templates can be found here: [[https://github.com/apache/curator/tree/master/curator-examples/src/main/java/pubsub]]. h2. Details For more details see: * [[Components|modeled-components.html]] * [[Caching, Typed Parameters and Versioning|modeled-typed.html]] curator-apache-curator-5.5.0/curator-x-async/src/site/site.xml000066400000000000000000000035341442004423600243600ustar00rootroot00000000000000 ]]> curator-apache-curator-5.5.0/curator-x-async/src/test/000077500000000000000000000000001442004423600227005ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/000077500000000000000000000000001442004423600236215ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/000077500000000000000000000000001442004423600244105ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/000077500000000000000000000000001442004423600256315ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/000077500000000000000000000000001442004423600273105ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/framework/000077500000000000000000000000001442004423600313055ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/framework/imps/000077500000000000000000000000001442004423600322555ustar00rootroot00000000000000TestAddWatch.java000066400000000000000000000071721442004423600353670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.ZookeeperFactory; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.zookeeper.AddWatchMode; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; public class TestAddWatch extends CuratorTestBase { @Test public void testPersistentRecursiveWatch() throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); client.blockUntilConnected(); CountDownLatch latch = new CountDownLatch(5); Watcher watcher = event -> latch.countDown(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.addWatch().withMode(AddWatchMode.PERSISTENT_RECURSIVE).usingWatcher(watcher).forPath("/test").toCompletableFuture().get(); client.create().forPath("/test"); client.create().forPath("/test/a"); client.create().forPath("/test/a/b"); client.create().forPath("/test/a/b/c"); client.create().forPath("/test/a/b/c/d"); assertTrue(timing.awaitLatch(latch)); } } @Test public void testPersistentRecursiveDefaultWatch() throws Exception { CountDownLatch latch = new CountDownLatch(6); // 5 creates plus the initial sync ZookeeperFactory zookeeperFactory = (connectString, sessionTimeout, watcher, canBeReadOnly) -> { Watcher actualWatcher = event -> { watcher.process(event); latch.countDown(); }; return new ZooKeeper(connectString, sessionTimeout, actualWatcher); }; try (CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).zookeeperFactory(zookeeperFactory).build() ) { client.start(); client.blockUntilConnected(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.addWatch().withMode(AddWatchMode.PERSISTENT_RECURSIVE).forPath("/test"); client.create().forPath("/test"); client.create().forPath("/test/a"); client.create().forPath("/test/a/b"); client.create().forPath("/test/a/b/c"); client.create().forPath("/test/a/b/c/d"); assertTrue(timing.awaitLatch(latch)); } } } TestFramework.java000066400000000000000000000624511442004423600356460ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Lists; import org.apache.curator.framework.AuthInfo; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.BackgroundCallback; import org.apache.curator.framework.api.CuratorEventType; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ZKPaths; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.api.CreateOption; import org.apache.curator.x.async.api.DeleteOption; import org.apache.curator.x.async.api.ExistsOption; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @SuppressWarnings("deprecation") public class TestFramework extends BaseClassForTests { @BeforeEach @Override public void setup() throws Exception { System.setProperty("znode.container.checkIntervalMs", "1000"); super.setup(); } @AfterEach @Override public void teardown() throws Exception { System.clearProperty("znode.container.checkIntervalMs"); super.teardown(); } @Test public void testQuietDelete() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.delete().withOptions(EnumSet.of(DeleteOption.quietly)).forPath("/foo/bar"); final BlockingQueue rc = new LinkedBlockingQueue<>(); BackgroundCallback backgroundCallback = (client1, event) -> rc.add(event.getResultCode()); async.delete().withOptions(EnumSet.of(DeleteOption.quietly)).forPath("/foo/bar/hey").handle((v, e) -> { if ( e == null ) { rc.add(KeeperException.Code.OK.intValue()); } else { rc.add(((KeeperException)e).code().intValue()); } return null; }); Integer code = rc.poll(new Timing().milliseconds(), TimeUnit.MILLISECONDS); assertNotNull(code); assertEquals(code.intValue(), KeeperException.Code.OK.intValue()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testNamespaceWithWatcher() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).namespace("aisa").retryPolicy(new RetryOneTime(1)).build(); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); BlockingQueue queue = new LinkedBlockingQueue(); async.create().forPath("/base"). thenRun(() -> async.watched().getChildren().forPath("/base").event().handle((event, x) -> { try { queue.put(event.getPath()); } catch ( InterruptedException e ) { throw new Error(e); } return null; })) .thenRun(() -> async.create().forPath("/base/child")); String path = queue.take(); assertEquals(path, "/base"); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateACLSingleAuth() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder .connectString(server.getConnectString()) .authorization("digest", "me1:pass1".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { ACL acl = new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.AUTH_IDS); List aclList = Lists.newArrayList(acl); client.create().withACL(aclList).forPath("/test", "test".getBytes()); client.close(); // Try setting data with me1:pass1 client = builder .connectString(server.getConnectString()) .authorization("digest", "me1:pass1".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.setData().forPath("/test", "test".getBytes()).toCompletableFuture().get(); } catch ( ExecutionException e ) { fail("Auth failed"); } client.close(); // Try setting data with something:else client = builder .connectString(server.getConnectString()) .authorization("digest", "something:else".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.setData().forPath("/test", "test".getBytes()).toCompletableFuture().get(); fail("Should have failed with auth exception"); } catch ( ExecutionException e ) { // expected } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateACLMultipleAuths() throws Exception { // Add a few authInfos List authInfos = new ArrayList(); authInfos.add(new AuthInfo("digest", "me1:pass1".getBytes())); authInfos.add(new AuthInfo("digest", "me2:pass2".getBytes())); CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder .connectString(server.getConnectString()) .authorization(authInfos) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { ACL acl = new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.AUTH_IDS); List aclList = Lists.newArrayList(acl); client.create().withACL(aclList).forPath("/test", "test".getBytes()); client.close(); // Try setting data with me1:pass1 client = builder .connectString(server.getConnectString()) .authorization("digest", "me1:pass1".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.setData().forPath("/test", "test".getBytes()).toCompletableFuture().get(); } catch ( ExecutionException e ) { fail("Auth failed"); } client.close(); // Try setting data with me1:pass1 client = builder .connectString(server.getConnectString()) .authorization("digest", "me2:pass2".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.setData().forPath("/test", "test".getBytes()).toCompletableFuture().get(); } catch ( ExecutionException e ) { fail("Auth failed"); } client.close(); // Try setting data with something:else client = builder .connectString(server.getConnectString()) .authorization("digest", "something:else".getBytes()) .retryPolicy(new RetryOneTime(1)) .build(); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.setData().forPath("/test", "test".getBytes()).toCompletableFuture().get(); fail("Should have failed with auth exception"); } catch ( ExecutionException e ) { // expected } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateACLWithReset() throws Exception { Timing timing = new Timing(); CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder .connectString(server.getConnectString()) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()) .authorization("digest", "me:pass".getBytes()) .retryPolicy(new ExponentialBackoffRetry(100, 5)) .build(); client.start(); try { final CountDownLatch lostLatch = new CountDownLatch(1); ConnectionStateListener listener = (client1, newState) -> { if ( newState == ConnectionState.LOST ) { lostLatch.countDown(); } }; client.getConnectionStateListenable().addListener(listener); ACL acl = new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.AUTH_IDS); List aclList = Lists.newArrayList(acl); client.create().withACL(aclList).forPath("/test", "test".getBytes()); server.stop(); assertTrue(timing.awaitLatch(lostLatch)); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.checkExists().forPath("/").toCompletableFuture().get(); fail("Connection should be down"); } catch ( ExecutionException e ) { // expected } server.restart(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.setData().forPath("/test", "test".getBytes()).toCompletableFuture().get(); } catch ( ExecutionException e ) { fail("Auth failed", e); } } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateParents() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build(); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.create().withOptions(EnumSet.of(CreateOption.createParentsIfNeeded)).forPath("/one/two/three", "foo".getBytes()).toCompletableFuture().get(); byte[] data = async.getData().forPath("/one/two/three").toCompletableFuture().get(); assertArrayEquals(data, "foo".getBytes()); async.create().withOptions(EnumSet.of(CreateOption.createParentsIfNeeded)).forPath("/one/two/another", "bar".getBytes()); data = async.getData().forPath("/one/two/another").toCompletableFuture().get(); assertArrayEquals(data, "bar".getBytes()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateParentContainers() throws Exception { if ( !checkForContainers() ) { return; } CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build(); try { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.create().withOptions(EnumSet.of(CreateOption.createParentsAsContainers)).forPath("/one/two/three", "foo".getBytes()).toCompletableFuture().get(); byte[] data = async.getData().forPath("/one/two/three").toCompletableFuture().get(); assertArrayEquals(data, "foo".getBytes()); async.delete().forPath("/one/two/three").toCompletableFuture().get(); new Timing().sleepABit(); assertNull(async.checkExists().forPath("/one/two").toCompletableFuture().get()); new Timing().sleepABit(); assertNull(async.checkExists().forPath("/one").toCompletableFuture().get()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testCreateWithProtection() throws ExecutionException, InterruptedException { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); String path = async.create().withOptions(Collections.singleton(CreateOption.doProtected)).forPath("/yo").toCompletableFuture().get(); String node = ZKPaths.getNodeFromPath(path); // CURATOR-489: confirm that the node contains a valid UUID, eg '_c_53345f98-9423-4e0c-a7b5-9f819e3ec2e1-yo' assertTrue(ProtectedUtils.isProtectedZNode(node)); assertEquals(ProtectedUtils.normalize(node), "yo"); } finally { CloseableUtils.closeQuietly(client); } } private boolean checkForContainers() { if ( ZKPaths.getContainerCreateMode() == CreateMode.PERSISTENT ) { System.out.println("Not using CreateMode.CONTAINER enabled version of ZooKeeper"); return false; } return true; } @Test public void testCreatingParentsTheSame() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); assertNull(client.checkExists().forPath("/one/two")); async.create().withOptions(EnumSet.of(CreateOption.createParentsAsContainers)).forPath("/one/two/three").toCompletableFuture().get(); assertNotNull(async.checkExists().forPath("/one/two").toCompletableFuture().get()); async.delete().withOptions(EnumSet.of(DeleteOption.deletingChildrenIfNeeded)).forPath("/one").toCompletableFuture().get(); assertNull(client.checkExists().forPath("/one")); assertNull(async.checkExists().forPath("/one/two").toCompletableFuture().get()); async.checkExists().withOptions(EnumSet.of(ExistsOption.createParentsAsContainers)).forPath("/one/two/three").toCompletableFuture().get(); assertNotNull(async.checkExists().forPath("/one/two").toCompletableFuture().get()); assertNull(async.checkExists().forPath("/one/two/three").toCompletableFuture().get()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testExistsCreatingParents() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); try { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); assertNull(async.checkExists().forPath("/one/two").toCompletableFuture().get()); async.checkExists().withOptions(EnumSet.of(ExistsOption.createParentsAsContainers)).forPath("/one/two/three").toCompletableFuture().get(); assertNotNull(async.checkExists().forPath("/one/two").toCompletableFuture().get()); assertNull(async.checkExists().forPath("/one/two/three").toCompletableFuture().get()); assertNull(async.checkExists().withOptions(EnumSet.of(ExistsOption.createParentsAsContainers)).forPath("/one/two/three").toCompletableFuture().get()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testSyncNew() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.create().forPath("/head"); assertNotNull(client.checkExists().forPath("/head")); final CountDownLatch latch = new CountDownLatch(1); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.sync().forPath("/head").handle((v, e) -> { assertNull(v); assertNull(e); latch.countDown(); return null; }); assertTrue(latch.await(10, TimeUnit.SECONDS)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundDelete() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); CountDownLatch latch = new CountDownLatch(1); async.create().forPath("/head").thenRun(() -> async.delete().forPath("/head").handle((v, e) -> { assertNull(v); assertNull(e); latch.countDown(); return null; }) ); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertNull(client.checkExists().forPath("/head")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundDeleteWithChildren() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { client.getCuratorListenable().addListener ((client1, event) -> { if ( event.getType() == CuratorEventType.DELETE ) { assertEquals(event.getPath(), "/one/two"); ((CountDownLatch)event.getContext()).countDown(); } }); CountDownLatch latch = new CountDownLatch(1); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.create().withOptions(EnumSet.of(CreateOption.createParentsIfNeeded)).forPath("/one/two/three/four").thenRun(() -> async.delete().withOptions(EnumSet.of(DeleteOption.deletingChildrenIfNeeded)).forPath("/one/two").handle((v, e) -> { assertNull(v); assertNull(e); latch.countDown(); return null; }) ); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertNull(client.checkExists().forPath("/one/two")); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testDeleteGuaranteedWithChildren() throws Exception { CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder(); CuratorFramework client = builder.connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).build(); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.create().withOptions(EnumSet.of(CreateOption.createParentsIfNeeded)).forPath("/one/two/three/four/five/six", "foo".getBytes()).toCompletableFuture().get(); async.delete().withOptions(EnumSet.of(DeleteOption.guaranteed, DeleteOption.deletingChildrenIfNeeded)).forPath("/one/two/three/four/five").toCompletableFuture().get(); assertNull(async.checkExists().forPath("/one/two/three/four/five").toCompletableFuture().get()); async.delete().withOptions(EnumSet.of(DeleteOption.guaranteed, DeleteOption.deletingChildrenIfNeeded)).forPath("/one/two").toCompletableFuture().get(); assertNull(async.checkExists().forPath("/one/two").toCompletableFuture().get()); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testGetSequentialChildren() throws Exception { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { Semaphore semaphore = new Semaphore(0); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.create().forPath("/head").thenRun(() -> { for ( int i = 0; i < 10; ++i ) { async.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/head/child").thenRun(semaphore::release); } }); assertTrue(new Timing().acquireSemaphore(semaphore, 10)); List children = async.getChildren().forPath("/head").toCompletableFuture().get(); assertEquals(children.size(), 10); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testBackgroundGetDataWithWatch() throws Exception { final byte[] data1 = {1, 2, 3}; final byte[] data2 = {4, 5, 6, 7}; CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); try { AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); async.create().forPath("/test", data1).toCompletableFuture().get(); CountDownLatch watchedLatch = new CountDownLatch(1); CountDownLatch backgroundLatch = new CountDownLatch(1); AsyncStage stage = async.watched().getData().forPath("/test"); stage.event().handle((event, x) -> { assertEquals(event.getPath(), "/test"); watchedLatch.countDown(); return null; }); stage.handle((d, x) -> { assertArrayEquals(d, data1); backgroundLatch.countDown(); return null; }); assertTrue(backgroundLatch.await(10, TimeUnit.SECONDS)); async.setData().forPath("/test", data2); assertTrue(watchedLatch.await(10, TimeUnit.SECONDS)); byte[] checkData = async.getData().forPath("/test").toCompletableFuture().get(); assertArrayEquals(checkData, data2); } finally { CloseableUtils.closeQuietly(client); } } } TestFrameworkBackground.java000066400000000000000000000254351442004423600376470ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/framework/imps/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.framework.imps; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.api.UnhandledErrorListener; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.retry.RetryNTimes; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.ACL; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; public class TestFrameworkBackground extends BaseClassForTests { private final Logger log = LoggerFactory.getLogger(getClass()); @Test public void testErrorListener() throws Exception { //The first call to the ACL provider will return a reasonable //value. The second will throw an error. This is because the ACL //provider is accessed prior to the backgrounding call. final AtomicBoolean aclProviderCalled = new AtomicBoolean(false); ACLProvider badAclProvider = new ACLProvider() { @Override public List getDefaultAcl() { if(aclProviderCalled.getAndSet(true)) { throw new UnsupportedOperationException(); } else { return new ArrayList<>(); } } @Override public List getAclForPath(String path) { if(aclProviderCalled.getAndSet(true)) { throw new UnsupportedOperationException(); } else { return new ArrayList<>(); } } }; CuratorFramework client = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .aclProvider(badAclProvider) .build(); try { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); final CountDownLatch errorLatch = new CountDownLatch(1); UnhandledErrorListener listener = (message, e) -> { if ( e instanceof UnsupportedOperationException ) { errorLatch.countDown(); } }; async.with(listener).create().forPath("/foo"); assertTrue(new Timing().awaitLatch(errorLatch)); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testListenerConnectedAtStart() throws Exception { server.stop(); Timing timing = new Timing(2); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryNTimes(0, 0)); try { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); final CountDownLatch connectedLatch = new CountDownLatch(1); final AtomicBoolean firstListenerAction = new AtomicBoolean(true); final AtomicReference firstListenerState = new AtomicReference<>(); ConnectionStateListener listener = (client1, newState) -> { if ( firstListenerAction.compareAndSet(true, false) ) { firstListenerState.set(newState); System.out.println("First listener state is " + newState); } if ( newState == ConnectionState.CONNECTED ) { connectedLatch.countDown(); } }; client.getConnectionStateListenable().addListener(listener); // due to CURATOR-72, this was causing a LOST event to precede the CONNECTED event async.create().forPath("/foo"); server.restart(); assertTrue(timing.awaitLatch(connectedLatch)); assertFalse(firstListenerAction.get()); ConnectionState firstconnectionState = firstListenerState.get(); assertEquals(firstconnectionState, ConnectionState.CONNECTED, "First listener state MUST BE CONNECTED but is " + firstconnectionState); } finally { CloseableUtils.closeQuietly(client); } } @Test public void testRetries() throws Exception { final int SLEEP = 1000; final int TIMES = 5; Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryNTimes(TIMES, SLEEP)); try { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); client.getZookeeperClient().blockUntilConnectedOrTimedOut(); final CountDownLatch latch = new CountDownLatch(TIMES); final List times = Lists.newArrayList(); final AtomicLong start = new AtomicLong(System.currentTimeMillis()); ((CuratorFrameworkImpl)client).debugListener = data -> { if ( data.getOperation().getClass().getName().contains("CreateBuilderImpl") ) { long now = System.currentTimeMillis(); times.add(now - start.get()); start.set(now); latch.countDown(); } }; server.stop(); async.create().forPath("/one"); latch.await(); for ( long elapsed : times.subList(1, times.size()) ) // first one isn't a retry { assertTrue(elapsed >= SLEEP, elapsed + ": " + times); } } finally { CloseableUtils.closeQuietly(client); } } /** * Attempt a background operation while Zookeeper server is down. * Return code must be {@link org.apache.zookeeper.KeeperException.Code#CONNECTIONLOSS} */ @Test public void testCuratorCallbackOnError() throws Exception { Timing timing = new Timing(); final CountDownLatch latch = new CountDownLatch(1); try ( CuratorFramework client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).sessionTimeoutMs(timing.session()).connectionTimeoutMs(timing.connection()).retryPolicy(new RetryOneTime(1000)).build() ) { client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); // Stop the Zookeeper server server.stop(); // Attempt to retrieve children list async.getChildren().forPath("/").handle((children, e) -> { if ( e instanceof KeeperException.ConnectionLossException ) { latch.countDown(); } return null; }); // Check if the callback has been called with a correct return code assertTrue(timing.awaitLatch(latch), "Callback has not been called by curator !"); } } /** * CURATOR-126 * Shutdown the Curator client while there are still background operations running. */ @Test public void testShutdown() throws Exception { Timing timing = new Timing(); CuratorFramework client = CuratorFrameworkFactory .builder() .connectString(server.getConnectString()) .sessionTimeoutMs(timing.session()) .connectionTimeoutMs(timing.connection()).retryPolicy(new RetryOneTime(1)) .maxCloseWaitMs(timing.forWaiting().milliseconds()) .build(); try { final AtomicBoolean hadIllegalStateException = new AtomicBoolean(false); ((CuratorFrameworkImpl)client).debugUnhandledErrorListener = (message, e) -> { if ( e instanceof IllegalStateException ) { hadIllegalStateException.set(true); } }; client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); final CountDownLatch operationReadyLatch = new CountDownLatch(1); ((CuratorFrameworkImpl)client).debugListener = data -> { try { operationReadyLatch.await(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } }; // queue a background operation that will block due to the debugListener async.create().forPath("/hey"); timing.sleepABit(); // close the client while the background is still blocked client.close(); // unblock the background operationReadyLatch.countDown(); timing.sleepABit(); // should not generate an exception assertFalse(hadIllegalStateException.get()); } finally { CloseableUtils.closeQuietly(client); } } } curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/000077500000000000000000000000001442004423600275575ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/000077500000000000000000000000001442004423600306745ustar00rootroot00000000000000CompletableBaseClassForTests.java000066400000000000000000000055041442004423600371660ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.base.Throwables; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.compatibility.Timing2; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; public abstract class CompletableBaseClassForTests extends BaseClassForTests { protected static final Timing2 timing = new Timing2(); protected void joinThrowable(CompletionStage stage) throws Throwable { try { stage.toCompletableFuture().get(); } catch (Exception ex) { throw Throwables.getRootCause(ex); } } protected void exceptional(CompletionStage stage, Class throwable) { assertThrows(throwable, () -> { joinThrowable(stage); }); } protected void complete(CompletionStage stage) { complete(stage, (v, e) -> { if ( e != null ) { Throwables.propagate(e); } }); } protected void complete(CompletionStage stage, BiConsumer handler) { try { stage.handle((v, e) -> { handler.accept(v, e); return null; }).toCompletableFuture().get(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); } catch ( InterruptedException e ) { Thread.interrupted(); } catch ( ExecutionException e ) { if ( e.getCause() instanceof AssertionError ) { throw (AssertionError)e.getCause(); } fail("get() failed", e); } catch ( TimeoutException e ) { fail("get() timed out"); } } } TestAsyncWrappers.java000066400000000000000000000056171442004423600351320ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import org.apache.curator.retry.RetryOneTime; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestAsyncWrappers extends CompletableBaseClassForTests { @Test public void testBasic() { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); InterProcessMutex lock = new InterProcessMutex(client, "/one/two"); complete(AsyncWrappers.lockAsync(lock), (__, e) -> { assertNull(e); AsyncWrappers.release(lock); }); } } @Test public void testContention() throws Exception { try ( CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)) ) { client.start(); InterProcessMutex lock1 = new InterProcessMutex(client, "/one/two"); InterProcessMutex lock2 = new InterProcessMutex(client, "/one/two"); CountDownLatch latch = new CountDownLatch(1); AsyncWrappers.lockAsync(lock1).thenAccept(__ -> { latch.countDown(); // don't release the lock }); assertTrue(timing.awaitLatch(latch)); CountDownLatch latch2 = new CountDownLatch(1); AsyncWrappers.lockAsync(lock2, timing.forSleepingABit().milliseconds(), TimeUnit.MILLISECONDS).exceptionally(e -> { if ( e instanceof AsyncWrappers.TimeoutException ) { latch2.countDown(); // lock should still be held } return null; }); assertTrue(timing.awaitLatch(latch2)); } } } TestBasicOperations.java000066400000000000000000000174611442004423600354160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async; import static java.util.EnumSet.of; import static org.apache.curator.x.async.api.CreateOption.compress; import static org.apache.curator.x.async.api.CreateOption.setDataIfExists; import static org.apache.zookeeper.CreateMode.EPHEMERAL_SEQUENTIAL; import static org.apache.zookeeper.CreateMode.PERSISTENT_SEQUENTIAL; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.utils.CloseableUtils; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.CompletionStage; import java.util.concurrent.CountDownLatch; public class TestBasicOperations extends CompletableBaseClassForTests { private AsyncCuratorFramework client; @BeforeEach @Override public void setup() throws Exception { super.setup(); CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(timing.forSleepingABit().milliseconds())); client.start(); this.client = AsyncCuratorFramework.wrap(client); } @AfterEach @Override public void teardown() throws Exception { CloseableUtils.closeQuietly(client.unwrap()); super.teardown(); } @Test public void testCreateTransactionWithMode() throws Exception { complete(AsyncWrappers.asyncEnsureContainers(client, "/test")); CuratorOp op1 = client.transactionOp().create().withMode(PERSISTENT_SEQUENTIAL).forPath("/test/node-"); CuratorOp op2 = client.transactionOp().create().withMode(PERSISTENT_SEQUENTIAL).forPath("/test/node-"); complete(client.transaction().forOperations(Arrays.asList(op1, op2))); assertEquals(client.unwrap().getChildren().forPath("/test").size(), 2); } @Test public void testCrud() { AsyncStage createStage = client.create().forPath("/test", "one".getBytes()); complete(createStage, (path, e) -> assertEquals(path, "/test")); AsyncStage getStage = client.getData().forPath("/test"); complete(getStage, (data, e) -> assertArrayEquals(data, "one".getBytes())); CompletionStage combinedStage = client.setData().forPath("/test", "new".getBytes()).thenCompose( __ -> client.getData().forPath("/test")); complete(combinedStage, (data, e) -> assertArrayEquals(data, "new".getBytes())); CompletionStage combinedDelete = client.create().withMode(EPHEMERAL_SEQUENTIAL).forPath("/deleteme").thenCompose( path -> client.delete().forPath(path)); complete(combinedDelete, (v, e) -> assertNull(e)); CompletionStage setDataIfStage = client.create().withOptions(of(compress, setDataIfExists)).forPath("/test", "last".getBytes()) .thenCompose(__ -> client.getData().decompressed().forPath("/test")); complete(setDataIfStage, (data, e) -> assertArrayEquals(data, "last".getBytes())); } @Test public void testException() { CountDownLatch latch = new CountDownLatch(1); client.getData().forPath("/woop").exceptionally(e -> { assertTrue(e instanceof KeeperException); assertEquals(((KeeperException)e).code(), KeeperException.Code.NONODE); latch.countDown(); return null; }); assertTrue(timing.awaitLatch(latch)); } @Test public void testWatching() { CountDownLatch latch = new CountDownLatch(1); client.watched().checkExists().forPath("/test").event().whenComplete((event, exception) -> { assertNull(exception); assertEquals(event.getType(), Watcher.Event.EventType.NodeCreated); latch.countDown(); }); client.create().forPath("/test"); assertTrue(timing.awaitLatch(latch)); } @Test public void testWatchingWithServerLoss() throws Exception { AsyncStage stage = client.watched().checkExists().forPath("/test"); stage.thenRun(() -> { try { server.stop(); } catch ( IOException e ) { // ignore } }); CountDownLatch latch = new CountDownLatch(1); complete(stage.event(), (v, e) -> { assertTrue(e instanceof AsyncEventException); assertEquals(((AsyncEventException)e).getKeeperState(), Watcher.Event.KeeperState.Disconnected); ((AsyncEventException)e).reset().thenRun(latch::countDown); }); server.restart(); client.create().forPath("/test"); assertTrue(timing.awaitLatch(latch)); } @Test public void testResultWrapper() throws Exception { CompletionStage> resultStage = AsyncResult.of(client.create().forPath("/first")); complete(resultStage, (v, e) -> { assertNull(e); assertEquals(v.getRawValue(), "/first"); assertNull(v.getRawException()); assertEquals(v.getCode(), KeeperException.Code.OK); }); resultStage = AsyncResult.of(client.create().forPath("/foo/bar")); complete(resultStage, (v, e) -> { assertNull(e); assertNull(v.getRawValue()); assertNull(v.getRawException()); assertEquals(v.getCode(), KeeperException.Code.NONODE); }); resultStage = AsyncResult.of(client.create().forPath("illegal path")); complete(resultStage, (v, e) -> { assertNull(e); assertNull(v.getRawValue()); assertNotNull(v.getRawException()); assertTrue(v.getRawException() instanceof IllegalArgumentException); assertEquals(v.getCode(), KeeperException.Code.SYSTEMERROR); }); server.stop(); resultStage = AsyncResult.of(client.create().forPath("/second")); complete(resultStage, (v, e) -> { assertNull(e); assertNull(v.getRawValue()); assertNull(v.getRawException()); assertEquals(v.getCode(), KeeperException.Code.CONNECTIONLOSS); }); } @Test public void testGetDataWithStat() { complete(client.create().forPath("/test", "hey".getBytes())); Stat stat = new Stat(); complete(client.getData().storingStatIn(stat).forPath("/test")); assertEquals(stat.getDataLength(), "hey".length()); } } curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/migrations/000077500000000000000000000000001442004423600330505ustar00rootroot00000000000000TestMigrationManager.java000066400000000000000000000357621442004423600377350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/migrations/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.migrations; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.base.Throwables; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.transaction.CuratorOp; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.AsyncWrappers; import org.apache.curator.x.async.CompletableBaseClassForTests; import org.apache.curator.x.async.migrations.models.ModelV1; import org.apache.curator.x.async.migrations.models.ModelV2; import org.apache.curator.x.async.migrations.models.ModelV3; import org.apache.curator.x.async.modeled.JacksonModelSerializer; import org.apache.curator.x.async.modeled.ModelSpec; import org.apache.curator.x.async.modeled.ModeledFramework; import org.apache.curator.x.async.modeled.ZPath; import org.apache.zookeeper.KeeperException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CompletionStage; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class TestMigrationManager extends CompletableBaseClassForTests { private static final String LOCK_PATH = "/migrations/locks"; private static final String META_DATA_PATH = "/migrations/metadata"; private AsyncCuratorFramework client; private ModelSpec v1Spec; private ModelSpec v2Spec; private ModelSpec v3Spec; private ExecutorService executor; private CuratorOp v1opA; private CuratorOp v1opB; private CuratorOp v2op; private CuratorOp v3op; private MigrationManager manager; private final AtomicReference filterLatch = new AtomicReference<>(); private CountDownLatch filterIsSetLatch; @BeforeEach @Override public void setup() throws Exception { super.setup(); filterIsSetLatch = new CountDownLatch(1); CuratorFramework rawClient = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(100)); rawClient.start(); this.client = AsyncCuratorFramework.wrap(rawClient); ZPath modelPath = ZPath.parse("/test/it"); v1Spec = ModelSpec.builder(modelPath, JacksonModelSerializer.build(ModelV1.class)).build(); v2Spec = ModelSpec.builder(modelPath, JacksonModelSerializer.build(ModelV2.class)).build(); v3Spec = ModelSpec.builder(modelPath, JacksonModelSerializer.build(ModelV3.class)).build(); v1opA = client.unwrap().transactionOp().create().forPath(v1Spec.path().parent().fullPath()); v1opB = ModeledFramework.wrap(client, v1Spec).createOp(new ModelV1("Test")); v2op = ModeledFramework.wrap(client, v2Spec).updateOp(new ModelV2("Test 2", 10)); v3op = ModeledFramework.wrap(client, v3Spec).updateOp(new ModelV3("One", "Two", 30)); executor = Executors.newCachedThreadPool(); manager = new MigrationManager(client, LOCK_PATH, META_DATA_PATH, executor, Duration.ofMinutes(10)) { @Override protected List filter(MigrationSet set, List operationHashesInOrder) throws MigrationException { CountDownLatch localLatch = filterLatch.getAndSet(null); if ( localLatch != null ) { filterIsSetLatch.countDown(); try { localLatch.await(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); Throwables.propagate(e); } } return super.filter(set, operationHashesInOrder); } }; manager.debugCount = new AtomicInteger(); } @AfterEach @Override public void teardown() throws Exception { CloseableUtils.closeQuietly(client.unwrap()); executor.shutdownNow(); super.teardown(); } @Test public void testBasic() { Migration m1 = () -> Arrays.asList(v1opA, v1opB); Migration m2 = () -> Collections.singletonList(v2op); Migration m3 = () -> Collections.singletonList(v3op); MigrationSet migrationSet = MigrationSet.build("1", Arrays.asList(m1, m2, m3)); complete(manager.migrate(migrationSet)); ModeledFramework v3Client = ModeledFramework.wrap(client, v3Spec); complete(v3Client.read(), (m, e) -> { assertEquals(m.getAge(), 30); assertEquals(m.getFirstName(), "One"); assertEquals(m.getLastName(), "Two"); }); int count = manager.debugCount.get(); complete(manager.migrate(migrationSet)); assertEquals(manager.debugCount.get(), count); // second call should do nothing } @Test public void testStaged() { Migration m1 = () -> Arrays.asList(v1opA, v1opB); MigrationSet migrationSet = MigrationSet.build("1", Collections.singletonList(m1)); complete(manager.migrate(migrationSet)); ModeledFramework v1Client = ModeledFramework.wrap(client, v1Spec); complete(v1Client.read(), (m, e) -> assertEquals(m.getName(), "Test")); Migration m2 = () -> Collections.singletonList(v2op); migrationSet = MigrationSet.build("1", Arrays.asList(m1, m2)); complete(manager.migrate(migrationSet)); ModeledFramework v2Client = ModeledFramework.wrap(client, v2Spec); complete(v2Client.read(), (m, e) -> { assertEquals(m.getName(), "Test 2"); assertEquals(m.getAge(), 10); }); Migration m3 = () -> Collections.singletonList(v3op); migrationSet = MigrationSet.build("1", Arrays.asList(m1, m2, m3)); complete(manager.migrate(migrationSet)); ModeledFramework v3Client = ModeledFramework.wrap(client, v3Spec); complete(v3Client.read(), (m, e) -> { assertEquals(m.getAge(), 30); assertEquals(m.getFirstName(), "One"); assertEquals(m.getLastName(), "Two"); }); } @Test public void testDocExample() throws Exception { CuratorOp op1 = client.transactionOp().create().forPath("/parent"); CuratorOp op2 = client.transactionOp().create().forPath("/parent/one"); CuratorOp op3 = client.transactionOp().create().forPath("/parent/two"); CuratorOp op4 = client.transactionOp().create().forPath("/parent/three"); CuratorOp op5 = client.transactionOp().create().forPath("/main", "hey".getBytes()); Migration initialMigration = () -> Arrays.asList(op1, op2, op3, op4, op5); MigrationSet migrationSet = MigrationSet.build("main", Collections.singletonList(initialMigration)); complete(manager.migrate(migrationSet)); assertNotNull(client.unwrap().checkExists().forPath("/parent/three")); assertArrayEquals(client.unwrap().getData().forPath("/main"), "hey".getBytes()); CuratorOp newOp1 = client.transactionOp().create().forPath("/new"); CuratorOp newOp2 = client.transactionOp().delete().forPath("/main"); // maybe this is no longer needed Migration newMigration = () -> Arrays.asList(newOp1, newOp2); migrationSet = MigrationSet.build("main", Arrays.asList(initialMigration, newMigration)); complete(manager.migrate(migrationSet)); assertNull(client.unwrap().checkExists().forPath("/main")); } @Test public void testChecksumDataError() { CuratorOp op1 = client.transactionOp().create().forPath("/test"); CuratorOp op2 = client.transactionOp().create().forPath("/test/bar", "first".getBytes()); Migration migration = () -> Arrays.asList(op1, op2); MigrationSet migrationSet = MigrationSet.build("1", Collections.singletonList(migration)); complete(manager.migrate(migrationSet)); CuratorOp op2Changed = client.transactionOp().create().forPath("/test/bar", "second".getBytes()); migration = () -> Arrays.asList(op1, op2Changed); migrationSet = MigrationSet.build("1", Collections.singletonList(migration)); try { complete(manager.migrate(migrationSet)); fail("Should throw"); } catch ( Throwable e ) { assertTrue(Throwables.getRootCause(e) instanceof MigrationException); } } @Test public void testChecksumPathError() { CuratorOp op1 = client.transactionOp().create().forPath("/test2"); CuratorOp op2 = client.transactionOp().create().forPath("/test2/bar"); Migration migration = () -> Arrays.asList(op1, op2); MigrationSet migrationSet = MigrationSet.build("1", Collections.singletonList(migration)); complete(manager.migrate(migrationSet)); CuratorOp op2Changed = client.transactionOp().create().forPath("/test/bar"); migration = () -> Arrays.asList(op1, op2Changed); migrationSet = MigrationSet.build("1", Collections.singletonList(migration)); try { complete(manager.migrate(migrationSet)); fail("Should throw"); } catch ( Throwable e ) { assertTrue(Throwables.getRootCause(e) instanceof MigrationException); } } @Test public void testPartialApplyForBadOps() throws Exception { CuratorOp op1 = client.transactionOp().create().forPath("/test", "something".getBytes()); CuratorOp op2 = client.transactionOp().create().forPath("/a/b/c"); Migration m1 = () -> Collections.singletonList(op1); Migration m2 = () -> Collections.singletonList(op2); MigrationSet migrationSet = MigrationSet.build("1", Arrays.asList(m1, m2)); try { complete(manager.migrate(migrationSet)); fail("Should throw"); } catch ( Throwable e ) { assertTrue(Throwables.getRootCause(e) instanceof KeeperException.NoNodeException); } assertNull(client.unwrap().checkExists().forPath("/test")); // should be all or nothing } @Test public void testTransactionForBadOps() throws Exception { CuratorOp op1 = client.transactionOp().create().forPath("/test2", "something".getBytes()); CuratorOp op2 = client.transactionOp().create().forPath("/a/b/c/d"); Migration migration = () -> Arrays.asList(op1, op2); MigrationSet migrationSet = MigrationSet.build("1", Collections.singletonList(migration)); try { complete(manager.migrate(migrationSet)); fail("Should throw"); } catch ( Throwable e ) { assertTrue(Throwables.getRootCause(e) instanceof KeeperException.NoNodeException); } assertNull(client.unwrap().checkExists().forPath("/test")); } @Test public void testConcurrency1() throws Exception { CuratorOp op1 = client.transactionOp().create().forPath("/test"); CuratorOp op2 = client.transactionOp().create().forPath("/test/bar", "first".getBytes()); Migration migration = () -> Arrays.asList(op1, op2); MigrationSet migrationSet = MigrationSet.build("1", Collections.singletonList(migration)); CountDownLatch latch = new CountDownLatch(1); filterLatch.set(latch); CompletionStage first = manager.migrate(migrationSet); assertTrue(timing.awaitLatch(filterIsSetLatch)); MigrationManager manager2 = new MigrationManager(client, LOCK_PATH, META_DATA_PATH, executor, Duration.ofMillis(timing.forSleepingABit().milliseconds())); try { complete(manager2.migrate(migrationSet)); fail("Should throw"); } catch ( Throwable e ) { assertTrue(Throwables.getRootCause(e) instanceof AsyncWrappers.TimeoutException, "Should throw AsyncWrappers.TimeoutException, was: " + Throwables.getStackTraceAsString(Throwables.getRootCause(e))); } latch.countDown(); complete(first); assertArrayEquals(client.unwrap().getData().forPath("/test/bar"), "first".getBytes()); } @Test public void testConcurrency2() throws Exception { CuratorOp op1 = client.transactionOp().create().forPath("/test"); CuratorOp op2 = client.transactionOp().create().forPath("/test/bar", "first".getBytes()); Migration migration = () -> Arrays.asList(op1, op2); MigrationSet migrationSet = MigrationSet.build("1", Collections.singletonList(migration)); CountDownLatch latch = new CountDownLatch(1); filterLatch.set(latch); CompletionStage first = manager.migrate(migrationSet); assertTrue(timing.awaitLatch(filterIsSetLatch)); CompletionStage second = manager.migrate(migrationSet); try { second.toCompletableFuture().get(timing.forSleepingABit().milliseconds(), TimeUnit.MILLISECONDS); fail("Should throw"); } catch ( Throwable e ) { assertTrue(Throwables.getRootCause(e) instanceof TimeoutException, "Should throw TimeoutException, was: " + Throwables.getStackTraceAsString(Throwables.getRootCause(e))); } latch.countDown(); complete(first); assertArrayEquals(client.unwrap().getData().forPath("/test/bar"), "first".getBytes()); complete(second); assertEquals(manager.debugCount.get(), 1); } } models/000077500000000000000000000000001442004423600342545ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/migrationsModelV1.java000066400000000000000000000021211442004423600363620ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/migrations/models/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.migrations.models; public class ModelV1 { private final String name; public ModelV1() { this(""); } public ModelV1(String name) { this.name = name; } public String getName() { return name; } } ModelV2.java000066400000000000000000000023111442004423600363640ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/migrations/models/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.migrations.models; public class ModelV2 { private final String name; private final int age; public ModelV2() { this("", 0); } public ModelV2(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } ModelV3.java000066400000000000000000000026071442004423600363750ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/migrations/models/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.migrations.models; public class ModelV3 { private final String firstName; private final String lastName; private final int age; public ModelV3() { this("", "", 0); } public ModelV3(String firstName, String lastName, int age) { this.firstName = firstName; this.lastName = lastName; this.age = age; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } } curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/000077500000000000000000000000001442004423600323055ustar00rootroot00000000000000TestCachedModeledFramework.java000066400000000000000000000271271442004423600402610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Sets; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.x.async.modeled.cached.CachedModeledFramework; import org.apache.curator.x.async.modeled.cached.ModeledCacheListener; import org.apache.curator.x.async.modeled.models.TestModel; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.IOException; import java.math.BigInteger; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestCachedModeledFramework extends TestModeledFrameworkBase { @Test public void testDownServer() throws IOException { Timing timing = new Timing(); TestModel model = new TestModel("a", "b", "c", 1, BigInteger.ONE); CachedModeledFramework client = ModeledFramework.wrap(async, modelSpec).cached(); Semaphore semaphore = new Semaphore(0); client.listenable().addListener((t, p, s, m) -> semaphore.release()); client.start(); try { client.child(model).set(model); assertTrue(timing.acquireSemaphore(semaphore)); CountDownLatch latch = new CountDownLatch(1); rawClient.getConnectionStateListenable().addListener((__, state) -> { if ( state == ConnectionState.LOST ) { latch.countDown(); } }); server.stop(); assertTrue(timing.awaitLatch(latch)); complete(client.child(model).read().whenComplete((value, e) -> { assertNotNull(value); assertNull(e); })); } finally { client.close(); } } @Test public void testPostInitializedFilter() { TestModel model1 = new TestModel("a", "b", "c", 1, BigInteger.ONE); TestModel model2 = new TestModel("d", "e", "f", 1, BigInteger.ONE); CachedModeledFramework client = ModeledFramework.wrap(async, modelSpec).cached(); Semaphore semaphore = new Semaphore(0); ModeledCacheListener listener = (t, p, s, m) -> semaphore.release(); client.listenable().addListener(listener.postInitializedOnly()); complete(client.child("1").set(model1)); // set before cache is started client.start(); try { assertFalse(timing.forSleepingABit().acquireSemaphore(semaphore)); client.child("2").set(model2); // set before cache is started assertTrue(timing.acquireSemaphore(semaphore)); } finally { client.close(); } } @Test public void testChildren() { TestModel parent = new TestModel("a", "b", "c", 20, BigInteger.ONE); TestModel child1 = new TestModel("d", "e", "f", 1, BigInteger.ONE); TestModel child2 = new TestModel("g", "h", "i", 1, BigInteger.ONE); TestModel grandChild1 = new TestModel("j", "k", "l", 10, BigInteger.ONE); TestModel grandChild2 = new TestModel("m", "n", "0", 5, BigInteger.ONE); try (CachedModeledFramework client = ModeledFramework.wrap(async, modelSpec).cached()) { CountDownLatch latch = new CountDownLatch(5); client.listenable().addListener((t, p, s, m) -> latch.countDown()); client.start(); complete(client.child("p").set(parent)); complete(client.child("p").child("c1").set(child1)); complete(client.child("p").child("c2").set(child2)); complete(client.child("p").child("c1").child("g1").set(grandChild1)); complete(client.child("p").child("c2").child("g2").set(grandChild2)); assertTrue(timing.awaitLatch(latch)); complete(client.child("p").children(), (v, e) -> { List paths = Arrays.asList( client.child("p").child("c1").modelSpec().path(), client.child("p").child("c2").modelSpec().path() ); assertEquals(v, paths); }); complete(client.child("p").childrenAsZNodes(), (v, e) -> { Set cachedModels = toSet(v.stream(), ZNode::model); assertEquals(cachedModels, Sets.newHashSet(child1, child2)); // verify that the same nodes are returned from the uncached method complete(ModeledFramework.wrap(async, modelSpec).child("p").childrenAsZNodes(), (v2, e2) -> { Set uncachedModels = toSet(v2.stream(), ZNode::model); assertEquals(cachedModels, uncachedModels); }); }); complete(client.child("p").child("c1").childrenAsZNodes(), (v, e) -> assertEquals(toSet(v.stream(), ZNode::model), Sets.newHashSet(grandChild1))); complete(client.child("p").child("c2").childrenAsZNodes(), (v, e) -> assertEquals(toSet(v.stream(), ZNode::model), Sets.newHashSet(grandChild2))); } } // note: CURATOR-546 @Test public void testAccessCacheDirectly() { TestModel model = new TestModel("a", "b", "c", 20, BigInteger.ONE); try (CachedModeledFramework client = ModeledFramework.wrap(async, modelSpec).cached()) { CountDownLatch latch = new CountDownLatch(1); client.listenable().addListener((t, p, s, m) -> latch.countDown()); client.start(); complete(client.child("m").set(model)); assertTrue(timing.awaitLatch(latch)); // call 2 times in a row to validate CURATOR-546 Optional> optZNode = client.cache().currentData(modelSpec.path().child("m")); assertEquals(optZNode.orElseThrow(() -> new AssertionError("node is missing")).model(), model); optZNode = client.cache().currentData(modelSpec.path().child("m")); assertEquals(optZNode.orElseThrow(() -> new AssertionError("node is missing")).model(), model); } } // Verify the CachedModeledFramework does not attempt to deserialize empty ZNodes on deletion using the Jackson // model serializer. // See: CURATOR-609 @Test public void testEmptyNodeJacksonDeserialization() { final TestModel model = new TestModel("a", "b", "c", 20, BigInteger.ONE); verifyEmptyNodeDeserialization(model, modelSpec); } // Verify the CachedModeledFramework does not attempt to deserialize empty ZNodes on deletion using the raw // model serializer. // See: CURATOR-609 @Test public void testEmptyNodeRawDeserialization() { final byte[] byteModel = {0x01, 0x02, 0x03}; final ModelSpec byteModelSpec = ModelSpec.builder(path, ModelSerializer.raw).build(); verifyEmptyNodeDeserialization(byteModel, byteModelSpec); } private void verifyEmptyNodeDeserialization(T model, ModelSpec parentModelSpec) { // The sub-path is the ZNode that will be removed that does not contain any model data. Their should be no // attempt to deserialize this empty ZNode. final String subPath = parentModelSpec.path().toString() + "/sub"; final String testModelPath = subPath + "/test"; final String signalModelPath = subPath + "/signal"; final CountDownLatch latch = new CountDownLatch(1); final AtomicBoolean modelWasNull = new AtomicBoolean(false); final AtomicReference caughtHandleException = new AtomicReference<>(null); // Create a custom listener to signal the end of the test and ensure that nothing is thrown. final ModeledCacheListener listener = new ModeledCacheListener() { @Override public void accept(Type t, ZPath p, Stat s, T m) { // We don't expect the handler to be called with a null model. if (m == null) { modelWasNull.set(true); } if (t == ModeledCacheListener.Type.NODE_ADDED && p.toString().equals(signalModelPath)) { latch.countDown(); } } public void handleException(Exception e) { caughtHandleException.set(e); } }; final ModelSerializer serializer = parentModelSpec.serializer(); // Create a cache client to watch the parent path. try (CachedModeledFramework cacheClient = ModeledFramework.wrap(async, parentModelSpec).cached()) { cacheClient.listenable().addListener(listener); ModelSpec testModelSpec = ModelSpec.builder(ZPath.parse(testModelPath), serializer).build(); ModeledFramework testModelClient = ModeledFramework.wrap(async, testModelSpec); ModelSpec signalModelSpec = ModelSpec.builder(ZPath.parse(signalModelPath), serializer).build(); ModeledFramework signalModelClient = ModeledFramework.wrap(async, signalModelSpec); cacheClient.start(); // Create the test model (with sub-path), creating the initial parent path structure. complete(testModelClient.set(model)); // Delete the test model, then delete the sub-path. As the sub-path ZNode is empty, we expect that this // should not throw an exception. complete(testModelClient.delete()); complete(testModelClient.unwrap().delete().forPath(subPath)); // Finally, create the signal model purely to signal the end of the test. complete(signalModelClient.set(model)); assertTrue(timing.awaitLatch(latch)); assertFalse(modelWasNull.get(), "Listener should never be called with a null model"); assertNull(caughtHandleException.get(), "Exception should not have been handled by listener"); } } private Set toSet(Stream stream, Function mapper) { return stream.map(mapper).collect(Collectors.toSet()); } } TestModeledFramework.java000066400000000000000000000327421442004423600371700ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Sets; import java.math.BigInteger; import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.concurrent.CompletionException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; import org.apache.curator.framework.schema.Schema; import org.apache.curator.framework.schema.SchemaSet; import org.apache.curator.framework.schema.SchemaViolation; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.AsyncStage; import org.apache.curator.x.async.api.CreateOption; import org.apache.curator.x.async.api.DeleteOption; import org.apache.curator.x.async.modeled.models.TestModel; import org.apache.curator.x.async.modeled.models.TestNewerModel; import org.apache.curator.x.async.modeled.versioned.Versioned; import org.apache.curator.x.async.modeled.versioned.VersionedModeledFramework; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; import org.junit.jupiter.api.Test; public class TestModeledFramework extends TestModeledFrameworkBase { @Test public void testCrud() { TestModel rawModel = new TestModel("John", "Galt", "1 Galt's Gulch", 42, BigInteger.valueOf(1)); TestModel rawModel2 = new TestModel("Wayne", "Rooney", "Old Trafford", 10, BigInteger.valueOf(1)); ModeledFramework client = ModeledFramework.wrap(async, modelSpec); AsyncStage stage = client.set(rawModel); assertNull(stage.event()); complete(stage, (s, e) -> assertNotNull(s)); complete(client.read(), (model, e) -> assertEquals(model, rawModel)); complete(client.update(rawModel2)); complete(client.read(), (model, e) -> assertEquals(model, rawModel2)); complete(client.delete()); complete(client.checkExists(), (stat, e) -> assertNull(stat)); } @Test public void testBackwardCompatibility() { TestNewerModel rawNewModel = new TestNewerModel("John", "Galt", "1 Galt's Gulch", 42, BigInteger.valueOf(1), 100); ModeledFramework clientForNew = ModeledFramework.wrap(async, newModelSpec); complete(clientForNew.set(rawNewModel), (s, e) -> assertNotNull(s)); ModeledFramework clientForOld = ModeledFramework.wrap(async, modelSpec); complete(clientForOld.read(), (model, e) -> assertTrue(rawNewModel.equalsOld(model))); } @Test public void testWatched() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); ModeledFramework client = ModeledFramework.builder(async, modelSpec).watched().build(); client.checkExists().event().whenComplete((event, ex) -> latch.countDown()); timing.sleepABit(); assertEquals(latch.getCount(), 1); client.set(new TestModel()); assertTrue(timing.awaitLatch(latch)); } @Test public void testGetChildren() { TestModel model = new TestModel("John", "Galt", "1 Galt's Gulch", 42, BigInteger.valueOf(1)); ModeledFramework client = ModeledFramework.builder(async, modelSpec).build(); complete(client.child("one").set(model)); complete(client.child("two").set(model)); complete(client.child("three").set(model)); Set expected = Sets.newHashSet(path.child("one"), path.child("two"), path.child("three")); complete(client.children(), (children, e) -> assertEquals(Sets.newHashSet(children), expected)); } @Test public void testDelete() { ModeledFramework client = ModeledFramework.wrap(async, modelSpec); complete(client.set(new TestModel())); Stat stat = new Stat(); client.child("a").set(new TestModel(), stat); exceptional(client.child("a").delete(stat.getVersion() + 1), KeeperException.BadVersionException.class); complete(client.child("a").delete(stat.getVersion())); client.child("b").set(new TestModel()); complete(client.child("b").delete(-1)); client.child("c").set(new TestModel()); exceptional(client.delete(), KeeperException.NotEmptyException.class); ModelSpec deleteChildren = ModelSpec .builder(modelSpec.path(), modelSpec.serializer()) .withDeleteOptions(Collections.singleton(DeleteOption.deletingChildrenIfNeeded)) .build(); complete(ModeledFramework.wrap(async, deleteChildren).delete()); exceptional(ModeledFramework.wrap(async, deleteChildren).delete(), KeeperException.NoNodeException.class); exceptional(client.read(), KeeperException.NoNodeException.class); ModelSpec quietly = ModelSpec .builder(modelSpec.path(), modelSpec.serializer()) .withDeleteOptions(Collections.singleton(DeleteOption.quietly)) .build(); complete(ModeledFramework.wrap(async, quietly).delete()); } @Test public void testBadNode() { complete(async.create().forPath(modelSpec.path().fullPath(), "fubar".getBytes()), (v, e) -> { }); // ignore error ModeledFramework client = ModeledFramework.builder(async, modelSpec).watched().build(); complete(client.read(), (model, e) -> assertTrue(e instanceof KeeperException.NoNodeException)); } @Test public void testSchema() throws Exception { Schema schema = modelSpec.schema(); try (CuratorFramework schemaClient = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).schemaSet(new SchemaSet(Collections.singletonList(schema), false)).build()) { schemaClient.start(); try { schemaClient.create().forPath(modelSpec.path().fullPath(), "asflasfas".getBytes()); fail("Should've thrown SchemaViolation"); } catch ( SchemaViolation dummy ) { // expected } ModeledFramework modeledSchemaClient = ModeledFramework.wrap(AsyncCuratorFramework.wrap(schemaClient), modelSpec); complete(modeledSchemaClient.set(new TestModel("one", "two", "three", 4, BigInteger.ONE)), (dummy, e) -> assertNull(e)); } } @Test public void testVersioned() { ModeledFramework client = ModeledFramework.wrap(async, modelSpec); TestModel model = new TestModel("John", "Galt", "Galt's Gulch", 21, BigInteger.valueOf(1010101)); complete(client.set(model)); complete(client.set(model)); // so that version goes to 1 VersionedModeledFramework versioned = client.versioned(); complete(versioned.read().whenComplete((v, e) -> { assertNull(e); assertTrue(v.version() > 0); }).thenCompose(versioned::set), (s, e) -> assertNull(e)); // read version is correct; set moves version to 2 Versioned badVersion = Versioned.from(model, 100000); complete(versioned.set(badVersion), (v, e) -> assertTrue(e instanceof KeeperException.BadVersionException)); complete(versioned.update(badVersion), (v, e) -> assertTrue(e instanceof KeeperException.BadVersionException)); final Versioned goodVersion = Versioned.from(model, 2); complete(versioned.update(goodVersion).whenComplete((v, e) -> { assertNull(e); assertEquals(3, v.getVersion()); })); final Stat stat = new Stat(); complete(client.read(stat)); // wrong version, needs to fail complete(client.delete(stat.getVersion() + 1), (v, e) -> assertTrue(e instanceof KeeperException.BadVersionException)); // correct version complete(client.delete(stat.getVersion())); } @Test public void testAcl() throws NoSuchAlgorithmException { List aclList = Collections.singletonList(new ACL(ZooDefs.Perms.WRITE, new Id("digest", DigestAuthenticationProvider.generateDigest("test:test")))); ModelSpec aclModelSpec = ModelSpec.builder(modelSpec.path(), modelSpec.serializer()).withAclList(aclList).build(); ModeledFramework client = ModeledFramework.wrap(async, aclModelSpec); complete(client.set(new TestModel("John", "Galt", "Galt's Gulch", 21, BigInteger.valueOf(1010101)))); complete(client.update(new TestModel("John", "Galt", "Galt's Gulch", 54, BigInteger.valueOf(88))), (__, e) -> assertNotNull(e, "Should've gotten an auth failure")); try (CuratorFramework authCurator = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(1)).authorization("digest", "test:test".getBytes()).build()) { authCurator.start(); ModeledFramework authClient = ModeledFramework.wrap(AsyncCuratorFramework.wrap(authCurator), aclModelSpec); complete(authClient.update(new TestModel("John", "Galt", "Galt's Gulch", 42, BigInteger.valueOf(66))), (__, e) -> assertNull(e, "Should've succeeded")); } } @Test public void testExceptionHandling() throws Exception { final List writeAcl = Collections.singletonList(new ACL(ZooDefs.Perms.WRITE, new Id("digest", DigestAuthenticationProvider.generateDigest("test:test")))); // An ACLProvider is used to get the Write ACL (for the test user) for any path "/test/**". final ACLProvider aclProvider = new ACLProvider() { @Override public List getDefaultAcl() { return ZooDefs.Ids.READ_ACL_UNSAFE; } @Override public List getAclForPath(String path) { // Any sub-path "/test/**" should only be writeable by the test user. return path.startsWith("/test") ? writeAcl : getDefaultAcl(); } }; try (CuratorFramework authorizedFramework = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .aclProvider(aclProvider) .authorization("digest", "test:test".getBytes()) .build()) { authorizedFramework.start(); // Create the parent path using the authorized framework, which will initially set the ACL accordingly. authorizedFramework.create().withMode(CreateMode.PERSISTENT).forPath("/test"); } // Now attempt to set the sub-node using an unauthorized client. try (CuratorFramework unauthorizedFramework = CuratorFrameworkFactory.builder() .connectString(server.getConnectString()) .retryPolicy(new RetryOneTime(1)) .aclProvider(aclProvider) .build()) { unauthorizedFramework.start(); // I overrode the TestModel provided path with a multi-component path under the "/test" parent path // (which was previously created with ACL protection). ModelSpec aclModelSpec = ModelSpec.builder(ZPath.parse("/test/foo/bar"), modelSpec.serializer()) .withCreateOptions(EnumSet.of(CreateOption.createParentsIfNeeded, CreateOption.createParentsAsContainers)) .build(); ModeledFramework noAuthClient = ModeledFramework.wrap(AsyncCuratorFramework.wrap(unauthorizedFramework), aclModelSpec); noAuthClient.set(new TestModel("John", "Galt", "Galt's Gulch", 42, BigInteger.valueOf(66))) .toCompletableFuture() .get(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); fail("expect to throw a NoAuth KeeperException"); } catch (ExecutionException | CompletionException e) { assertTrue(e.getCause() instanceof KeeperException.NoAuthException); } } } TestModeledFrameworkBase.java000066400000000000000000000050111442004423600377500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.async.AsyncCuratorFramework; import org.apache.curator.x.async.CompletableBaseClassForTests; import org.apache.curator.x.async.modeled.models.TestModel; import org.apache.curator.x.async.modeled.models.TestNewerModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; public class TestModeledFrameworkBase extends CompletableBaseClassForTests { protected static final ZPath path = ZPath.parse("/test/path"); protected CuratorFramework rawClient; protected ModelSpec modelSpec; protected ModelSpec newModelSpec; protected AsyncCuratorFramework async; @BeforeEach @Override public void setup() throws Exception { super.setup(); rawClient = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); rawClient.start(); async = AsyncCuratorFramework.wrap(rawClient); JacksonModelSerializer serializer = JacksonModelSerializer.build(TestModel.class); JacksonModelSerializer newSerializer = JacksonModelSerializer.build(TestNewerModel.class); modelSpec = ModelSpec.builder(path, serializer).build(); newModelSpec = ModelSpec.builder(path, newSerializer).build(); } @AfterEach @Override public void teardown() throws Exception { CloseableUtils.closeQuietly(rawClient); super.teardown(); } } TestZPath.java000066400000000000000000000131741442004423600347650ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled; import static org.apache.curator.x.async.modeled.ZPath.parameter; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.utils.ZKPaths; import org.apache.curator.x.async.modeled.details.ZPathImpl; import org.junit.jupiter.api.Test; public class TestZPath { @Test public void testRoot() { assertEquals(ZPath.root.nodeName(), ZKPaths.PATH_SEPARATOR); assertEquals(ZPath.root, ZPathImpl.root); assertTrue(ZPath.root.isRoot()); assertEquals(ZPath.root.child("foo").parent(), ZPath.root); assertTrue(ZPath.root.child("foo").parent().isRoot()); } @Test public void testBasic() { ZPath path = ZPath.root.child("one").child("two"); assertFalse(path.isRoot()); assertEquals(path, ZPath.root.child("one").child("two")); assertNotEquals(path, ZPath.root.child("onex").child("two")); assertEquals(path.nodeName(), "two"); assertEquals(path.fullPath(), "/one/two"); assertEquals(path.parent().fullPath(), "/one"); assertEquals(path.fullPath(), "/one/two"); // call twice to test the internal cache assertEquals(path.parent().fullPath(), "/one"); // call twice to test the internal cache assertTrue(path.startsWith(ZPath.root.child("one"))); assertFalse(path.startsWith(ZPath.root.child("two"))); // Despite these paths containing elements which appear to be parameters, ZPath.parse() always returns // a ZPath which is considered fully resolved. This allows users to include parameter-like elements in their // ZPath's that aren't treated as parameters. ZPath checkIdLike = ZPath.parse("/one/{two}/three"); assertTrue(checkIdLike.isResolved(), "parse method always returns a fully resolved ZPath"); checkIdLike = ZPath.parse("/one/" + ZPath.parameter() + "/three"); assertTrue(checkIdLike.isResolved(), "parse method always returns a fully resolved ZPath"); checkIdLike = ZPath.parse("/one/" + ZPath.parameter("others") + "/three"); assertTrue(checkIdLike.isResolved(), "parse method always returns a fully resolved ZPath"); } @Test public void testParsing() { assertEquals(ZPath.parse("/"), ZPath.root); assertEquals(ZPath.parse("/one/two/three"), ZPath.root.child("one").child("two").child("three")); assertEquals(ZPath.parse("/one/two/three"), ZPath.from("one", "two", "three")); assertEquals(ZPath.parseWithIds("/one/{id}/two/{id}"), ZPath.from("one", parameter(), "two", parameter())); } @Test public void testUnresolvedPath() { assertThrows(IllegalStateException.class, ()->{ ZPath path = ZPath.from("one", parameter(), "two"); path.fullPath(); }); } @Test public void testResolvedPath() { ZPath path = ZPath.from("one", parameter(), "two", parameter()); assertEquals(path.resolved("a", "b"), ZPath.from("one", "a", "two", "b")); } @Test public void testSchema() { ZPath path = ZPath.from("one", parameter(), "two", parameter()); assertEquals(path.toSchemaPathPattern().toString(), "/one/.*/two/.*"); path = ZPath.parse("/one/two/three"); assertEquals(path.toSchemaPathPattern().toString(), "/one/two/three"); path = ZPath.parseWithIds("/one/{id}/three"); assertEquals(path.toSchemaPathPattern().toString(), "/one/.*/three"); path = ZPath.parseWithIds("/{id}/{id}/three"); assertEquals(path.toSchemaPathPattern().toString(), "/.*/.*/three"); } @Test public void testCustomIds() { assertEquals(ZPath.parseWithIds("/a/{a}/bee/{bee}/c/{c}").toString(), "/a/{a}/bee/{bee}/c/{c}"); assertEquals(ZPath.from("a", parameter(), "b", parameter()).toString(), "/a/{id}/b/{id}"); assertEquals(ZPath.from("a", parameter("foo"), "b", parameter("bar")).toString(), "/a/{foo}/b/{bar}"); } @Test public void testPartialResolution() { ZPath path = ZPath.parseWithIds("/one/{1}/two/{2}"); assertFalse(path.parent().isResolved()); assertFalse(path.parent().parent().isResolved()); assertTrue(path.parent().parent().parent().isResolved()); assertFalse(path.isResolved()); path = path.resolved("p1"); assertFalse(path.isResolved()); assertTrue(path.parent().isResolved()); assertEquals(path.toString(), "/one/p1/two/{2}"); path = path.resolved("p2"); assertTrue(path.isResolved()); assertEquals(path.toString(), "/one/p1/two/p2"); } } models/000077500000000000000000000000001442004423600335115ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/modeledTestModel.java000066400000000000000000000057631442004423600362670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.models; import java.math.BigInteger; import java.util.Objects; public class TestModel { private final String firstName; private final String lastName; private final String address; private final int age; private final BigInteger salary; public TestModel() { this("", "", "", 0, BigInteger.ZERO); } public TestModel(String firstName, String lastName, String address, int age, BigInteger salary) { this.firstName = Objects.requireNonNull(firstName, "firstName cannot be null"); this.lastName = Objects.requireNonNull(lastName, "lastName cannot be null"); this.address = Objects.requireNonNull(address, "address cannot be null"); this.age = Objects.requireNonNull(age, "age cannot be null"); this.salary = salary; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public String getAddress() { return address; } public int getAge() { return age; } public BigInteger getSalary() { return salary; } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } TestModel testModel = (TestModel)o; if ( age != testModel.age ) { return false; } if ( !firstName.equals(testModel.firstName) ) { return false; } if ( !lastName.equals(testModel.lastName) ) { return false; } //noinspection SimplifiableIfStatement if ( !address.equals(testModel.address) ) { return false; } return salary.equals(testModel.salary); } @Override public int hashCode() { int result = firstName.hashCode(); result = 31 * result + lastName.hashCode(); result = 31 * result + address.hashCode(); result = 31 * result + age; result = 31 * result + salary.hashCode(); return result; } } TestNewerModel.java000066400000000000000000000071341442004423600372620ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.models; import java.math.BigInteger; import java.util.Objects; public class TestNewerModel { private final String firstName; private final String lastName; private final String address; private final int age; private final BigInteger salary; private final long newField; public TestNewerModel() { this("", "", "", 0, BigInteger.ZERO, 0); } public TestNewerModel(String firstName, String lastName, String address, int age, BigInteger salary, long newField) { this.firstName = Objects.requireNonNull(firstName, "firstName cannot be null"); this.lastName = Objects.requireNonNull(lastName, "lastName cannot be null"); this.address = Objects.requireNonNull(address, "address cannot be null"); this.age = Objects.requireNonNull(age, "age cannot be null"); this.salary = salary; this.newField = newField; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public String getAddress() { return address; } public int getAge() { return age; } public BigInteger getSalary() { return salary; } public long getNewField() { return newField; } public boolean equalsOld(TestModel model) { return firstName.equals(model.getFirstName()) && lastName.equals(model.getLastName()) && address.equals(model.getAddress()) && salary.equals(model.getSalary()) && age == model.getAge() ; } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } TestNewerModel that = (TestNewerModel)o; if ( age != that.age ) { return false; } if ( newField != that.newField ) { return false; } if ( !firstName.equals(that.firstName) ) { return false; } if ( !lastName.equals(that.lastName) ) { return false; } //noinspection SimplifiableIfStatement if ( !address.equals(that.address) ) { return false; } return salary.equals(that.salary); } @Override public int hashCode() { int result = firstName.hashCode(); result = 31 * result + lastName.hashCode(); result = 31 * result + address.hashCode(); result = 31 * result + age; result = 31 * result + salary.hashCode(); result = 31 * result + (int)(newField ^ (newField >>> 32)); return result; } } TestSimpleModel.java000066400000000000000000000040711442004423600374300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.async.modeled.models; import java.util.Objects; public class TestSimpleModel { private final String name; private final int age; public TestSimpleModel() { this("", 0); } public TestSimpleModel(String name, int age) { this.name = Objects.requireNonNull(name, "name cannot be null"); this.age = Objects.requireNonNull(age, "age cannot be null"); } public String getName() { return name; } public int getAge() { return age; } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } TestSimpleModel that = (TestSimpleModel)o; //noinspection SimplifiableIfStatement if ( age != that.age ) { return false; } return name.equals(that.name); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + age; return result; } @Override public String toString() { return "TestSimpleModel{" + "name='" + name + '\'' + ", age=" + age + '}'; } } curator-apache-curator-5.5.0/curator-x-async/src/test/resources/000077500000000000000000000000001442004423600247125ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-async/src/test/resources/log4j.properties000066400000000000000000000021071442004423600300470ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. log4j.rootLogger=ERROR, console log4j.logger.org.apache.curator=DEBUG, console log4j.additivity.org.apache.curator=false log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n curator-apache-curator-5.5.0/curator-x-discovery-server/000077500000000000000000000000001442004423600233305ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/LICENSE000066400000000000000000000261361442004423600243450ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. curator-apache-curator-5.5.0/curator-x-discovery-server/NOTICE000066400000000000000000000002501442004423600242310ustar00rootroot00000000000000Apache Curator Copyright 2013-2023 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). curator-apache-curator-5.5.0/curator-x-discovery-server/README.txt000066400000000000000000000061501442004423600250300ustar00rootroot00000000000000==== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==== == Service Discovery Server == A REST server for use with Curator Service Discovery. This server can be used for non-Java applications that need to participate in the Curator Service Discovery. Full documentation: http://curator.apache.org/curator-x-discovery-server/index.html == JSON specifications for REST entities == = ServiceInstance = FIELD TYPE REQUIRED DESCRIPTION ------------------------------------------------------------------------------------------------------- name string Y Service Name id string Y Instance ID address string Y Hostname/IP port int * Instance port (port and/or sslPort must be present) sslPort int * Instance SSL port (port and/or sslPort must be present) payload user-defined N Instance payload registrationTimeUTC long N Time of the registration in UTC serviceType string Y Either "STATIC" or "PERMANENT". STATIC will get purged after the defined threshold has elapsed. PERMANENT must be manually purged. Example: { "name": "test", "id": "ca2fff8e-d756-480c-b59e-8297ff88624b", "address": "10.20.30.40", "port": 1234, "payload": "From Test", "registrationTimeUTC": 1325129459728, "serviceType": "STATIC" } = ServiceInstances = A list of ServiceInstance entities. Example: [ { "name": "test", "id": "ca2fff8e-d756-480c-b59e-8297ff88624b", "address": "10.20.30.40", "port": 1234, "payload": "From Test", "registrationTimeUTC": 1325129459728, "serviceType": "STATIC" }, { "name": "foo", "id": "bd4fff8e-c234-480c-f6ee-8297ff813765", "address": "10.20.30.40", "sslPort": 1235, "payload": "foo-bar", "registrationTimeUTC": 1325129459728, "serviceType": "STATIC" } ] = ServiceNames = A list of strings (service names). Example: [ { "name": "foo" }, { "name": "bar" } ] curator-apache-curator-5.5.0/curator-x-discovery-server/pom.xml000066400000000000000000000127301442004423600246500ustar00rootroot00000000000000 4.0.0 org.apache.curator apache-curator 5.5.0 curator-x-discovery-server 5.5.0 bundle Curator Service Discovery Server Bridges non-Java or legacy applications with the Curator Service Discovery. 2011 org.apache.zookeeper.*;version="[3.4,4.0)", * org.apache.curator.x.discovery.server.*;version="${project.version}";-noimport:=true org.apache.curator curator-x-discovery javax.ws.rs jsr311-api org.apache.curator curator-test test org.junit.jupiter junit-jupiter-api test com.sun.jersey jersey-server test com.sun.jersey jersey-servlet test com.sun.jersey jersey-client test com.sun.jersey jersey-core test org.eclipse.jetty jetty-webapp test net.sf.scannotation scannotation test org.jboss.resteasy resteasy-jaxrs test org.slf4j slf4j-log4j12 test org.apache.maven.plugins maven-surefire-plugin none jdk-9-plus [1.9,) javax.xml.bind jaxb-api test com.sun.xml.bind jaxb-core test com.sun.xml.bind jaxb-impl test javax.activation activation test curator-apache-curator-5.5.0/curator-x-discovery-server/src/000077500000000000000000000000001442004423600241175ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/000077500000000000000000000000001442004423600250435ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/000077500000000000000000000000001442004423600257645ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/000077500000000000000000000000001442004423600265535ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/000077500000000000000000000000001442004423600277745ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/000077500000000000000000000000001442004423600314535ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/000077500000000000000000000000001442004423600317225ustar00rootroot00000000000000discovery/000077500000000000000000000000001442004423600336525ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/xserver/000077500000000000000000000000001442004423600351605ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discoverycontexts/000077500000000000000000000000001442004423600370275ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/serverGenericDiscoveryContext.java000066400000000000000000000065331442004423600445120ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/contexts/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.contexts; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.reflect.TypeToken; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; /** * For convenience, a version of {@link DiscoveryContext} that uses any generic type as the payload */ @Provider public class GenericDiscoveryContext implements DiscoveryContext, ContextResolver> { private final ServiceDiscovery serviceDiscovery; private final ProviderStrategy providerStrategy; private final int instanceRefreshMs; private final TypeToken payloadType; public GenericDiscoveryContext(ServiceDiscovery serviceDiscovery, ProviderStrategy providerStrategy, int instanceRefreshMs, Class payloadType) { this(serviceDiscovery, providerStrategy, instanceRefreshMs, TypeToken.of(payloadType)); } public GenericDiscoveryContext(ServiceDiscovery serviceDiscovery, ProviderStrategy providerStrategy, int instanceRefreshMs, TypeToken payloadType) { this.serviceDiscovery = serviceDiscovery; this.providerStrategy = providerStrategy; this.instanceRefreshMs = instanceRefreshMs; this.payloadType = payloadType; } @Override public ProviderStrategy getProviderStrategy() { return providerStrategy; } @Override public int getInstanceRefreshMs() { return instanceRefreshMs; } @Override public ServiceDiscovery getServiceDiscovery() { return serviceDiscovery; } @Override public void marshallJson(ObjectNode node, String fieldName, T payload) throws Exception { if ( payload == null ) { //noinspection unchecked payload = (T)payloadType.getRawType().newInstance(); } node.putPOJO(fieldName, payload); } @Override public T unMarshallJson(JsonNode node) throws Exception { T payload; ObjectMapper mapper = new ObjectMapper(); //noinspection unchecked payload = (T)mapper.readValue(node.toString(), payloadType.getRawType()); return payload; } @Override public DiscoveryContext getContext(Class type) { return this; } } IntegerDiscoveryContext.java000066400000000000000000000054071442004423600445320ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/contexts/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.contexts; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; /** * For convenience, a version of {@link DiscoveryContext} that uses an int as the * payload */ @Provider public class IntegerDiscoveryContext implements DiscoveryContext, ContextResolver> { private final ServiceDiscovery serviceDiscovery; private final ProviderStrategy providerStrategy; private final int instanceRefreshMs; public IntegerDiscoveryContext(ServiceDiscovery serviceDiscovery, ProviderStrategy providerStrategy, int instanceRefreshMs) { this.serviceDiscovery = serviceDiscovery; this.providerStrategy = providerStrategy; this.instanceRefreshMs = instanceRefreshMs; } @Override public ProviderStrategy getProviderStrategy() { return providerStrategy; } @Override public int getInstanceRefreshMs() { return instanceRefreshMs; } @Override public ServiceDiscovery getServiceDiscovery() { return serviceDiscovery; } @Override public void marshallJson(ObjectNode node, String fieldName, Integer payload) throws Exception { if ( payload != null ) { node.put(fieldName, payload.toString()); } } @Override public Integer unMarshallJson(JsonNode node) throws Exception { if ( node != null ) { return Integer.parseInt(node.asText()); } return null; } @Override public DiscoveryContext getContext(Class type) { return this; } } MapDiscoveryContext.java000066400000000000000000000033321442004423600436450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/contexts/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.contexts; import com.google.common.reflect.TypeToken; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; import java.util.Map; /** * For convenience, a version of {@link DiscoveryContext} that uses a String-to-String map as the * payload */ @Provider public class MapDiscoveryContext extends GenericDiscoveryContext> implements ContextResolver>> { public MapDiscoveryContext(ServiceDiscovery> serviceDiscovery, ProviderStrategy> providerStrategy, int instanceRefreshMs) { super(serviceDiscovery, providerStrategy, instanceRefreshMs, new TypeToken>(){}); } } StringDiscoveryContext.java000066400000000000000000000052561442004423600444050ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/contexts/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.contexts; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; /** * For convenience, a version of {@link DiscoveryContext} that uses a string as the * payload */ @Provider public class StringDiscoveryContext implements DiscoveryContext, ContextResolver> { private final ServiceDiscovery serviceDiscovery; private final ProviderStrategy providerStrategy; private final int instanceRefreshMs; public StringDiscoveryContext(ServiceDiscovery serviceDiscovery, ProviderStrategy providerStrategy, int instanceRefreshMs) { this.serviceDiscovery = serviceDiscovery; this.providerStrategy = providerStrategy; this.instanceRefreshMs = instanceRefreshMs; } @Override public ProviderStrategy getProviderStrategy() { return providerStrategy; } @Override public int getInstanceRefreshMs() { return instanceRefreshMs; } @Override public ServiceDiscovery getServiceDiscovery() { return serviceDiscovery; } @Override public void marshallJson(ObjectNode node, String fieldName, String payload) throws Exception { if ( payload != null ) { node.put(fieldName, payload); } } @Override public String unMarshallJson(JsonNode node) throws Exception { return (node != null) ? node.asText() : null; } @Override public DiscoveryContext getContext(Class type) { return this; } } entity/000077500000000000000000000000001442004423600364745ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/serverJsonServiceInstanceMarshaller.java000066400000000000000000000136601442004423600452770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/entity/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.entity; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceInstanceBuilder; import org.apache.curator.x.discovery.ServiceType; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; /** * Message body reader/writer. Inject this as appropriate for the JAX-RS implementation you are using */ @Provider @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public class JsonServiceInstanceMarshaller implements MessageBodyReader>, MessageBodyWriter> { private final static ObjectMapper mapper = new ObjectMapper(); private final DiscoveryContext context; public JsonServiceInstanceMarshaller(DiscoveryContext context) { this.context = context; } static ServiceInstance readInstance(JsonNode node, DiscoveryContext context) throws Exception { ServiceInstanceBuilder builder = ServiceInstance.builder(); builder.name(node.get("name").asText()); builder.id(node.get("id").asText()); builder.address(node.get("address").asText()); builder.registrationTimeUTC(node.get("registrationTimeUTC").asLong()); builder.serviceType(ServiceType.valueOf(node.get("serviceType").asText())); builder.payload(context.unMarshallJson(node.get("payload"))); Integer port = getInteger(node, "port"); Integer sslPort = getInteger(node, "sslPort"); if ( port != null ) { builder.port(port); } if ( sslPort != null ) { builder.sslPort(sslPort); } return builder.build(); } static ObjectNode writeInstance(ObjectMapper mapper, ServiceInstance instance, DiscoveryContext context) { ObjectNode node = mapper.createObjectNode(); node.put("name", instance.getName()); node.put("id", instance.getId()); node.put("address", instance.getAddress()); putInteger(node, "port", instance.getPort()); putInteger(node, "sslPort", instance.getSslPort()); node.put("registrationTimeUTC", instance.getRegistrationTimeUTC()); node.put("serviceType", instance.getServiceType().name()); try { context.marshallJson(node, "payload", instance.getPayload()); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new WebApplicationException(e); } return node; } private static Integer getInteger(JsonNode node, String fieldName) { JsonNode intNode = node.get(fieldName); return (intNode != null) ? intNode.asInt() : null; } private static void putInteger(ObjectNode node, String fieldName, Integer value) { if ( value != null ) { node.put(fieldName, value); } } @Override public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return isWriteable(type, genericType, annotations, mediaType); } @Override public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return ServiceInstance.class.isAssignableFrom(type) && mediaType.equals(MediaType.APPLICATION_JSON_TYPE); } @Override public long getSize(ServiceInstance serviceInstance, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } @Override public ServiceInstance readFrom(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { try { JsonNode node = mapper.readTree(entityStream); return readInstance(node, context); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new WebApplicationException(e); } } @Override public void writeTo(ServiceInstance serviceInstance, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { ObjectNode node = writeInstance(mapper, serviceInstance, context); mapper.writeValue(entityStream, node); } } JsonServiceInstancesMarshaller.java000066400000000000000000000111221442004423600454510ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/entity/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.entity; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Lists; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.List; /** * Message body reader/writer. Inject this as appropriate for the JAX-RS implementation you are using */ @Provider @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public class JsonServiceInstancesMarshaller implements MessageBodyReader>, MessageBodyWriter> { private final DiscoveryContext context; public JsonServiceInstancesMarshaller(DiscoveryContext context) { this.context = context; } @Override public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return isWriteable(type, genericType, annotations, mediaType); } @Override public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return ServiceInstances.class.isAssignableFrom(type) && mediaType.equals(MediaType.APPLICATION_JSON_TYPE); } @Override public long getSize(ServiceInstances serviceInstances, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } @Override public ServiceInstances readFrom(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { try { List> instances = Lists.newArrayList(); ObjectMapper mapper = new ObjectMapper(); JsonNode tree = mapper.reader().readTree(entityStream); for ( int i = 0; i < tree.size(); ++i ) { JsonNode node = tree.get(i); ServiceInstance instance = JsonServiceInstanceMarshaller.readInstance(node, context); instances.add(instance); } return new ServiceInstances(instances); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); throw new WebApplicationException(e); } } @Override public void writeTo(ServiceInstances serviceInstances, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { ObjectMapper mapper = new ObjectMapper(); ArrayNode arrayNode = mapper.createArrayNode(); List> instanceList = serviceInstances.getServices(); for ( ServiceInstance instance : instanceList ) { ObjectNode node = JsonServiceInstanceMarshaller.writeInstance(mapper, instance, context); arrayNode.add(node); } mapper.writer().writeValue(entityStream, arrayNode); } } JsonServiceNamesMarshaller.java000066400000000000000000000075331442004423600446000ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/entity/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.entity; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Lists; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; /** * Message body reader/writer. Inject this as appropriate for the JAX-RS implementation you are using */ @Provider @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public class JsonServiceNamesMarshaller implements MessageBodyReader, MessageBodyWriter { @Override public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return allow(type, mediaType); } @Override public ServiceNames readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { List names = Lists.newArrayList(); ObjectMapper mapper = new ObjectMapper(); JsonNode tree = mapper.reader().readTree(entityStream); for ( int i = 0; i < tree.size(); ++i ) { JsonNode node = tree.get(i); names.add(node.get("name").asText()); } return new ServiceNames(names); } @Override public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return allow(type, mediaType); } @Override public long getSize(ServiceNames serviceNames, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } @Override public void writeTo(ServiceNames serviceNames, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { ObjectMapper mapper = new ObjectMapper(); ArrayNode arrayNode = mapper.createArrayNode(); for ( String name : serviceNames.getNames() ) { ObjectNode node = mapper.createObjectNode(); node.put("name", name); arrayNode.add(node); } mapper.writer().writeValue(entityStream, arrayNode); } private static boolean allow(Class type, MediaType mediaType) { return ServiceNames.class.isAssignableFrom(type) && mediaType.equals(MediaType.APPLICATION_JSON_TYPE); } } ServiceInstances.java000066400000000000000000000030251442004423600426070ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/entity/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.entity; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.curator.x.discovery.ServiceInstance; import java.util.Collection; import java.util.List; /** * Raw generic lists don't work well in JAX-RS. Thus, this wrapper is needed. */ public class ServiceInstances { private final List> services; public ServiceInstances() { services = Lists.newArrayList(); } public ServiceInstances(Collection> c) { services = Lists.newArrayList(c); } public List> getServices() { return ImmutableList.copyOf(services); } } ServiceNames.java000066400000000000000000000026341442004423600417300ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/entity/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.entity; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.Collection; import java.util.List; /** * Raw generic lists don't work well in JAX-RS. Thus, this wrapper is needed. */ public class ServiceNames { private final List names; public ServiceNames() { names = Lists.newArrayList(); } public ServiceNames(Collection c) { names = Lists.newArrayList(c); } public List getNames() { return ImmutableList.copyOf(names); } } rest/000077500000000000000000000000001442004423600361355ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/serverDiscoveryContext.java000066400000000000000000000044501442004423600423170ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/rest/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.rest; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceDiscovery; /** * Bridge between the specifics of your needs and the generic implementation */ public interface DiscoveryContext { /** * Return the threshold in milliseconds to consider a registration stale * * @return number of milliseconds */ public int getInstanceRefreshMs(); /** * Return the service singleton * * @return service */ public ServiceDiscovery getServiceDiscovery(); /** * Serialize your payload * * @param node the node to serialize into * @param fieldName field name to use * @param payload the payload value (can be null) * @throws Exception any errors */ public void marshallJson(ObjectNode node, String fieldName, T payload) throws Exception; /** * Deserialize your payload * * @param node the node that has the payload * @return the payload or null * @throws Exception any errors */ public T unMarshallJson(JsonNode node) throws Exception; /** * Return the provider strategy to use for {@link DiscoveryResource#getAny(String)} * * @return strategy */ public ProviderStrategy getProviderStrategy(); } DiscoveryResource.java000066400000000000000000000204411442004423600424600ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/rest/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.rest; import com.google.common.collect.Lists; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceType; import org.apache.curator.x.discovery.details.InstanceProvider; import org.apache.curator.x.discovery.server.entity.ServiceInstances; import org.apache.curator.x.discovery.server.entity.ServiceNames; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.Collection; import java.util.Collections; import java.util.List; /** *

* The JAX-RS web service. Due to how most JAX-RS implementations are written, you must * create a concrete class that extends this using your payload type. The concrete class should * have the base path that you'd like to use. *

* *

* Because the JAX-RS implementation can create a new instance of the resource for every request, * your concrete class must use a context resolver to access the DiscoveryContext. Or, if you * are using an IoC framework, you can access it that way. *

* *

* Here's a version that has no payload (i.e. * a Void payload): *

*
 * @Path("/")
 * public class MyResource extends DiscoveryResource<Void> {
 *     public MyResource(@Context ContextResolver<DiscoveryContext<Void>> resolver) {
 *         // note: this may not work with all JAX-RS implementations
 *         super(resolver.getContext(DiscoveryContext.class));
 *     }
 * }
 * 
*/ public abstract class DiscoveryResource { private static final Logger log = LoggerFactory.getLogger(DiscoveryResource.class); private final DiscoveryContext context; public DiscoveryResource(DiscoveryContext context) { this.context = context; } @PUT @Path("v1/service/{name}/{id}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response putService(ServiceInstance instance, @PathParam("name") String name, @PathParam("id") String id) { if ( !instance.getId().equals(id) || !instance.getName().equals(name) ) { log.info("Request where path id and/or name doesn't match entity"); return Response.status(Response.Status.BAD_REQUEST).build(); } if ( instance.getServiceType().isDynamic() ) { log.info("Service type cannot be dynamic"); return Response.status(Response.Status.BAD_REQUEST).build(); } try { context.getServiceDiscovery().registerService(instance); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Trying to register service", e); return Response.serverError().build(); } return Response.status(Response.Status.CREATED).build(); } @DELETE @Path("v1/service/{name}/{id}") public Response removeService(@PathParam("name") String name, @PathParam("id") String id) { try { ServiceInstance instance = context.getServiceDiscovery().queryForInstance(name, id); if ( instance != null ) { //noinspection unchecked context.getServiceDiscovery().unregisterService(instance); } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("Trying to delete service", e); return Response.serverError().build(); } return Response.ok().build(); } @Deprecated @GET @Path("{name}/{id}") @Produces(MediaType.APPLICATION_JSON) public Response getDeprecated(@PathParam("name") String name, @PathParam("id") String id) { return internalGet(name, id, true); } @GET @Path("v1/service/{name}/{id}") @Produces(MediaType.APPLICATION_JSON) public Response get(@PathParam("name") String name, @PathParam("id") String id) { return internalGet(name, id, false); } @GET @Path("v1/service") @Produces(MediaType.APPLICATION_JSON) public Response getAllNames() { try { List instances = Lists.newArrayList(context.getServiceDiscovery().queryForNames()); Collections.sort(instances); return Response.ok(new ServiceNames(instances)).build(); } catch ( Exception e ) { log.error("Trying to get service names", e); return Response.serverError().build(); } } @GET @Path("v1/service/{name}") @Produces(MediaType.APPLICATION_JSON) public Response getAll(@PathParam("name") String name) { try { Collection> instances = context.getServiceDiscovery().queryForInstances(name); return Response.ok(new ServiceInstances(instances)).build(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error(String.format("Trying to get instances from service (%s)", name), e); return Response.serverError().build(); } } @GET @Path("v1/anyservice/{name}") @Produces(MediaType.APPLICATION_JSON) public Response getAny(@PathParam("name") String name) { try { final List> instances = Lists.newArrayList(context.getServiceDiscovery().queryForInstances(name)); ServiceInstance randomInstance = context.getProviderStrategy().getInstance ( new InstanceProvider() { @Override public List> getInstances() throws Exception { return instances; } } ); if ( randomInstance == null ) { return Response.status(Response.Status.NOT_FOUND).build(); } return Response.ok(randomInstance).build(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error(String.format("Trying to get any instance from service (%s)", name), e); return Response.serverError().build(); } } private Response internalGet(String name, String id, boolean addDeprecationHeader) { try { ServiceInstance instance = context.getServiceDiscovery().queryForInstance(name, id); if ( instance == null ) { return Response.status(Response.Status.NOT_FOUND).build(); } Response.ResponseBuilder builder = Response.ok(instance); if ( addDeprecationHeader ) { builder = builder.header("Warning", "This API has been deprecated. Please see the updated spec for the replacement API."); } return builder.build(); } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error(String.format("Trying to get instance (%s) from service (%s)", id, name), e); return Response.serverError().build(); } } } InstanceCleanup.java000066400000000000000000000101261442004423600420540ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/main/java/org/apache/curator/x/discovery/server/rest/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.rest; import com.google.common.base.Preconditions; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.util.Collection; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * A background task that purges stale registrations. You should allocate a singleton * of this class, call {@link #start()} and then call {@link #close()} when your application * is shutting down. */ public class InstanceCleanup implements Closeable { private static final Logger log = LoggerFactory.getLogger(InstanceCleanup.class); private final ServiceDiscovery discovery; private final int instanceRefreshMs; private final ScheduledExecutorService service = ThreadUtils.newSingleThreadScheduledExecutor("InstanceCleanup"); /** * @param discovery the service being monitored * @param instanceRefreshMs time in milliseconds to consider a registration stale */ public InstanceCleanup(ServiceDiscovery discovery, int instanceRefreshMs) { //noinspection unchecked this.discovery = (ServiceDiscovery)discovery; // this cast is safe - this class never accesses the payload this.instanceRefreshMs = instanceRefreshMs; } /** * Start the task */ public void start() { Preconditions.checkArgument(!service.isShutdown(), "already started"); service.scheduleWithFixedDelay ( new Runnable() { @Override public void run() { doWork(); } }, instanceRefreshMs, instanceRefreshMs, TimeUnit.MILLISECONDS ); } @Override public void close() throws IOException { Preconditions.checkArgument(!service.isShutdown(), "not started"); service.shutdownNow(); } private void doWork() { try { for ( String name : discovery.queryForNames() ) { checkService(name); } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error("GC for service names", e); } } private void checkService(String name) { try { Collection> instances = discovery.queryForInstances(name); for ( ServiceInstance instance : instances ) { if ( instance.getServiceType() == ServiceType.STATIC ) { if ( (System.currentTimeMillis() - instance.getRegistrationTimeUTC()) > instanceRefreshMs ) { discovery.unregisterService(instance); } } } } catch ( Exception e ) { ThreadUtils.checkInterrupted(e); log.error(String.format("GC for service: %s", name), e); } } } curator-apache-curator-5.5.0/curator-x-discovery-server/src/site/000077500000000000000000000000001442004423600250635ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/site/confluence/000077500000000000000000000000001442004423600272045ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/site/confluence/index.confluence000066400000000000000000000073421442004423600323640ustar00rootroot00000000000000h1. Service Discovery Server h2. Packaging Curator Service Discovery is in its own package in Maven Central: curator\-x\-discovery\-server h2. Description The Service Discovery Server bridges non\-Java or legacy applications with the Curator [[Service Discovery|../curator-x-discovery/index.html]]. It exposes RESTful web services to register, remove, query, etc. services. The Service Discovery Server provides JAX\-RS components that can be incorporated into a container of your choice (Tomcat, Jetty, etc.). You can also choose any JAX\-RS provider (Jersey, RESTEasy, etc.). h2. Deploying the Server The server must be combined with a JAX\-RS implementation (Jersey, etc.) and a container (Tomcat, Jetty, etc.). Several singletons need to be injected: * ServiceDiscovery * DiscoveryContext * JsonServiceInstanceMarshaller * JsonServiceInstancesMarshaller * JsonServiceNamesMarshaller Additionally the JAX\-RS Resource class must be injected. Due to how most JAX\-RS implementations are written, you must create a concrete class that extends this using your payload type. The concrete class should have the base path that you'd like to use. Because the JAX\-RS implementation can create a new instance of the resource for every request, your concrete class must use a context resolver to access the DiscoveryContext. Or, if you are using an IoC framework, you can access it that way. Here's a version that has no payload (i.e. a Void payload): {code} @Path("/") public class MyResource extends DiscoveryResource { public MyResource(@Context ContextResolver> resolver) { // note: this may not work with all JAX-RS implementations super(resolver.getContext(DiscoveryContext.class)); } } {code} h2. REST Clients must make appropriate REST calls to register themselves and send periodic heartbeats. They can also find services via REST calls: h2. putService *Method:* PUT \\ *Path:* v1/service/{name}/{id} \\ *Request Entity:* ServiceInstance \\ *Response Entity:* n/a \\ *Description:* {name} is the service name, {id} is the instance id. The request entity is a _ServiceInstance_. This method registers a service instance. If the ServiceType is STATIC, the instance is registered only for the pre\-defined period (defined in the DiscoveryContext). STATIC services must call putService at least once per period. PERMANENT services are registered until they are manually deleted. h2. removeService *Method:* DELETE \\ *Path:* v1/service/{name}/{id} \\ *Request Entity:* n/a \\ *Response Entity:* n/a \\ *Description:* {name} is the service name, {id} is the instance id. The specified service is deleted/unregistered. h2. get *Method:* GET \\ *Path:* v1/service/{name}/{id} \\ *Request Entity:* n/a \\ *Response Entity:* ServiceInstance \\ *Description:* {name} is the service name, {id} is the instance id. Returns the complete _ServiceInstance_ for the specified service. 404 is returned if not found. h2. getAllNames *Method:* GET \\ *Path:* v1/service \\ *Request Entity:* n/a \\ *Response Entity:* ServiceNames \\ *Description:* Returns all currently registered service names. h2. getAll *Method:* GET \\ *Path:* v1/service/{name} \\ *Request Entity:* n/a \\ *Response Entity:* ServiceInstances \\ *Description:* {name} is the service name. Returns all service instances for the named service. h2. getAny *Method:* GET \\ *Path:* v1/anyservice/{name} \\ *Request Entity:* n/a \\ *Response Entity:* ServiceInstance \\ *Description:* {name} is the service name. Return a random instance from the given service or 404. h2. JSON specs The JSON specifications for the REST entities are documented here: [[https://git-wip-us.apache.org/repos/asf?p=curator.git;a=blob_plain;f=curator-x-discovery-server/README.txt;hb=HEAD]] curator-apache-curator-5.5.0/curator-x-discovery-server/src/site/site.xml000066400000000000000000000026651442004423600265620ustar00rootroot00000000000000 ]]> curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/000077500000000000000000000000001442004423600250765ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/000077500000000000000000000000001442004423600260175ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/000077500000000000000000000000001442004423600266065ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/000077500000000000000000000000001442004423600300275ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/000077500000000000000000000000001442004423600315065ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/000077500000000000000000000000001442004423600317555ustar00rootroot00000000000000discovery/000077500000000000000000000000001442004423600337055ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/xserver/000077500000000000000000000000001442004423600352135ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discoveryjetty_jersey/000077500000000000000000000000001442004423600377335ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/serverMapDiscoveryResource.java000066400000000000000000000025721442004423600447210ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_jersey/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_jersey; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import org.apache.curator.x.discovery.server.rest.DiscoveryResource; import javax.ws.rs.Path; import javax.ws.rs.core.Context; import javax.ws.rs.ext.ContextResolver; import java.util.Map; @Path("/") public class MapDiscoveryResource extends DiscoveryResource> { public MapDiscoveryResource(@Context ContextResolver>> resolver) { super(resolver.getContext(DiscoveryContext.class)); } } ServiceDetails.java000066400000000000000000000044551442004423600435140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_jersey/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_jersey; import com.fasterxml.jackson.annotation.JsonRootName; import java.util.HashMap; import java.util.Map; /** * Service payload describing details of a service. */ @JsonRootName("details") public class ServiceDetails { private Map data; private String description; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public ServiceDetails() { this(new HashMap()); } public ServiceDetails(Map data) { this.data = data; } public void setData(Map data) { this.data = data; } public Map getData() { return data; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((data == null) ? 0 : data.hashCode()); result = prime * result + ((description == null) ? 0 : description.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ServiceDetails other = (ServiceDetails) obj; if (data == null) { if (other.data != null) return false; } else if (!data.equals(other.data)) return false; if (description == null) { if (other.description != null) return false; } else if (!description.equals(other.description)) return false; return true; } } ServiceDetailsDiscoveryContext.java000066400000000000000000000033431442004423600467440ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_jersey/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_jersey; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.server.contexts.GenericDiscoveryContext; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; /** * A DiscoveryContext that maps a concrete payload object of ServiceDetails */ @Provider public class ServiceDetailsDiscoveryContext extends GenericDiscoveryContext implements DiscoveryContext, ContextResolver> { public ServiceDetailsDiscoveryContext(ServiceDiscovery serviceDiscovery, ProviderStrategy providerStrategy, int instanceRefreshMs) { super(serviceDiscovery, providerStrategy, instanceRefreshMs, ServiceDetails.class); } } ServiceDetailsDiscoveryResource.java000066400000000000000000000025611442004423600471100ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_jersey/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_jersey; import javax.ws.rs.Path; import javax.ws.rs.core.Context; import javax.ws.rs.ext.ContextResolver; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import org.apache.curator.x.discovery.server.rest.DiscoveryResource; @Path("/") public class ServiceDetailsDiscoveryResource extends DiscoveryResource { public ServiceDetailsDiscoveryResource(@Context ContextResolver> resolver) { super(resolver.getContext(DiscoveryContext.class)); } } StringDiscoveryResource.java000066400000000000000000000025201442004423600454430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_jersey/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_jersey; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import org.apache.curator.x.discovery.server.rest.DiscoveryResource; import javax.ws.rs.Path; import javax.ws.rs.core.Context; import javax.ws.rs.ext.ContextResolver; @Path("/") public class StringDiscoveryResource extends DiscoveryResource { public StringDiscoveryResource(@Context ContextResolver> resolver) { super(resolver.getContext(DiscoveryContext.class)); } } TestMapsWithJersey.java000066400000000000000000000155511442004423600443630ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_jersey/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_jersey; import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.config.ClientConfig; import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.api.core.DefaultResourceConfig; import com.sun.jersey.spi.container.servlet.ServletContainer; import org.apache.curator.test.InstanceSpec; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceType; import org.apache.curator.x.discovery.server.contexts.MapDiscoveryContext; import org.apache.curator.x.discovery.server.entity.JsonServiceInstanceMarshaller; import org.apache.curator.x.discovery.server.entity.JsonServiceInstancesMarshaller; import org.apache.curator.x.discovery.server.entity.JsonServiceNamesMarshaller; import org.apache.curator.x.discovery.server.entity.ServiceInstances; import org.apache.curator.x.discovery.server.entity.ServiceNames; import org.apache.curator.x.discovery.server.mocks.MockServiceDiscovery; import org.apache.curator.x.discovery.strategies.RandomStrategy; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import java.util.Map; import java.util.Set; public class TestMapsWithJersey { private static final String HOST = "127.0.0.1"; private Server server; private JsonServiceNamesMarshaller serviceNamesMarshaller; private JsonServiceInstanceMarshaller> serviceInstanceMarshaller; private JsonServiceInstancesMarshaller> serviceInstancesMarshaller; private MapDiscoveryContext context; private int port; @BeforeEach public void setup() throws Exception { context = new MapDiscoveryContext(new MockServiceDiscovery>(), new RandomStrategy>(), 1000); serviceNamesMarshaller = new JsonServiceNamesMarshaller(); serviceInstanceMarshaller = new JsonServiceInstanceMarshaller>(context); serviceInstancesMarshaller = new JsonServiceInstancesMarshaller>(context); Application application = new DefaultResourceConfig() { @Override public Set> getClasses() { Set> classes = Sets.newHashSet(); classes.add(MapDiscoveryResource.class); return classes; } @Override public Set getSingletons() { Set singletons = Sets.newHashSet(); singletons.add(context); singletons.add(serviceNamesMarshaller); singletons.add(serviceInstanceMarshaller); singletons.add(serviceInstancesMarshaller); return singletons; } }; ServletContainer container = new ServletContainer(application); port = InstanceSpec.getRandomPort(); server = new Server(port); ServletContextHandler root = new ServletContextHandler(ServletContextHandler.SESSIONS); root.setContextPath("/"); final ServletHolder servletHolder = new ServletHolder(container); root.addServlet(servletHolder, "/*"); servletHolder.setInitOrder(1); server.setHandler(root); server.start(); } @AfterEach public void teardown() throws Exception { server.stop(); server.join(); } @Test public void testRegisterService() throws Exception { Map payload = Maps.newHashMap(); payload.put("one", "1"); payload.put("two", "2"); payload.put("three", "3"); ServiceInstance> service = ServiceInstance.>builder() .name("test") .payload(payload) .serviceType(ServiceType.STATIC) .build(); ClientConfig config = new DefaultClientConfig() { @Override public Set getSingletons() { Set singletons = Sets.newHashSet(); singletons.add(context); singletons.add(serviceNamesMarshaller); singletons.add(serviceInstanceMarshaller); singletons.add(serviceInstancesMarshaller); return singletons; } }; Client client = Client.create(config); WebResource resource = client.resource("http://" + HOST + ":" + port); resource.path("/v1/service/test/" + service.getId()).type(MediaType.APPLICATION_JSON_TYPE).put(service); ServiceNames names = resource.path("/v1/service").get(ServiceNames.class); assertEquals(names.getNames(), Lists.newArrayList("test")); GenericType>> type = new GenericType>>(){}; ServiceInstances> instances = resource.path("/v1/service/test").get(type); assertEquals(instances.getServices().size(), 1); assertEquals(instances.getServices().get(0), service); assertEquals(instances.getServices().get(0).getPayload(), payload); // Retrieve a single instance GenericType>> singleInstanceType = new GenericType>>(){}; ServiceInstance> instance = resource.path("/v1/service/test/" + service.getId()).get(singleInstanceType); assertEquals(instance, service); } } TestObjectPayloadWithJersey.java000066400000000000000000000154421442004423600462020ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_jersey/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_jersey; import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.config.ClientConfig; import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.api.core.DefaultResourceConfig; import com.sun.jersey.spi.container.servlet.ServletContainer; import org.apache.curator.test.InstanceSpec; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceType; import org.apache.curator.x.discovery.server.entity.JsonServiceInstanceMarshaller; import org.apache.curator.x.discovery.server.entity.JsonServiceInstancesMarshaller; import org.apache.curator.x.discovery.server.entity.JsonServiceNamesMarshaller; import org.apache.curator.x.discovery.server.entity.ServiceInstances; import org.apache.curator.x.discovery.server.entity.ServiceNames; import org.apache.curator.x.discovery.server.mocks.MockServiceDiscovery; import org.apache.curator.x.discovery.strategies.RandomStrategy; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import java.util.Set; public class TestObjectPayloadWithJersey { private static final String HOST = "127.0.0.1"; private Server server; private JsonServiceNamesMarshaller serviceNamesMarshaller; private JsonServiceInstanceMarshaller serviceInstanceMarshaller; private JsonServiceInstancesMarshaller serviceInstancesMarshaller; private ServiceDetailsDiscoveryContext context; private int port; @BeforeEach public void setup() throws Exception { context = new ServiceDetailsDiscoveryContext(new MockServiceDiscovery(), new RandomStrategy(), 1000); serviceNamesMarshaller = new JsonServiceNamesMarshaller(); serviceInstanceMarshaller = new JsonServiceInstanceMarshaller(context); serviceInstancesMarshaller = new JsonServiceInstancesMarshaller(context); Application application = new DefaultResourceConfig() { @Override public Set> getClasses() { Set> classes = Sets.newHashSet(); classes.add(ServiceDetailsDiscoveryResource.class); return classes; } @Override public Set getSingletons() { Set singletons = Sets.newHashSet(); singletons.add(context); singletons.add(serviceNamesMarshaller); singletons.add(serviceInstanceMarshaller); singletons.add(serviceInstancesMarshaller); return singletons; } }; ServletContainer container = new ServletContainer(application); port = InstanceSpec.getRandomPort(); server = new Server(port); ServletContextHandler root = new ServletContextHandler(ServletContextHandler.SESSIONS); root.setContextPath("/"); final ServletHolder servletHolder = new ServletHolder(container); root.addServlet(servletHolder, "/*"); servletHolder.setInitOrder(1); server.setHandler(root); server.start(); } @AfterEach public void teardown() throws Exception { server.stop(); server.join(); } @Test public void testRegisterService() throws Exception { ServiceDetails payload = new ServiceDetails(); payload.setDescription("Example description for test"); payload.getData().put("one", "1"); payload.getData().put("two", "2"); payload.getData().put("three", "3"); ServiceInstance service = ServiceInstance.builder() .name("test") .payload(payload) .serviceType(ServiceType.STATIC) .build(); ClientConfig config = new DefaultClientConfig() { @Override public Set getSingletons() { Set singletons = Sets.newHashSet(); singletons.add(context); singletons.add(serviceNamesMarshaller); singletons.add(serviceInstanceMarshaller); singletons.add(serviceInstancesMarshaller); return singletons; } }; Client client = Client.create(config); WebResource resource = client.resource("http://" + HOST + ":" + port); resource.path("/v1/service/test/" + service.getId()).type(MediaType.APPLICATION_JSON_TYPE).put(service); ServiceNames names = resource.path("/v1/service").get(ServiceNames.class); assertEquals(names.getNames(), Lists.newArrayList("test")); GenericType> type = new GenericType>(){}; ServiceInstances instances = resource.path("/v1/service/test").get(type); assertEquals(instances.getServices().size(), 1); assertEquals(instances.getServices().get(0), service); assertEquals(instances.getServices().get(0).getPayload(), payload); // Retrieve a single instance GenericType> singleInstanceType = new GenericType>(){}; ServiceInstance instance = resource.path("/v1/service/test/" + service.getId()).get(singleInstanceType); assertEquals(instance, service); } } TestStringsWithJersey.java000066400000000000000000000163461442004423600451170ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_jersey/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_jersey; import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.config.ClientConfig; import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.api.core.DefaultResourceConfig; import com.sun.jersey.spi.container.servlet.ServletContainer; import org.apache.curator.test.InstanceSpec; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceType; import org.apache.curator.x.discovery.server.contexts.StringDiscoveryContext; import org.apache.curator.x.discovery.server.entity.JsonServiceInstanceMarshaller; import org.apache.curator.x.discovery.server.entity.JsonServiceInstancesMarshaller; import org.apache.curator.x.discovery.server.entity.JsonServiceNamesMarshaller; import org.apache.curator.x.discovery.server.entity.ServiceInstances; import org.apache.curator.x.discovery.server.entity.ServiceNames; import org.apache.curator.x.discovery.server.mocks.MockServiceDiscovery; import org.apache.curator.x.discovery.strategies.RandomStrategy; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import java.util.Set; public class TestStringsWithJersey { private static final String HOST = "127.0.0.1"; private Server server; private JsonServiceNamesMarshaller serviceNamesMarshaller; private JsonServiceInstanceMarshaller serviceInstanceMarshaller; private JsonServiceInstancesMarshaller serviceInstancesMarshaller; private StringDiscoveryContext context; private int port; @BeforeEach public void setup() throws Exception { context = new StringDiscoveryContext(new MockServiceDiscovery(), new RandomStrategy(), 1000); serviceNamesMarshaller = new JsonServiceNamesMarshaller(); serviceInstanceMarshaller = new JsonServiceInstanceMarshaller(context); serviceInstancesMarshaller = new JsonServiceInstancesMarshaller(context); Application application = new DefaultResourceConfig() { @Override public Set> getClasses() { Set> classes = Sets.newHashSet(); classes.add(StringDiscoveryResource.class); return classes; } @Override public Set getSingletons() { Set singletons = Sets.newHashSet(); singletons.add(context); singletons.add(serviceNamesMarshaller); singletons.add(serviceInstanceMarshaller); singletons.add(serviceInstancesMarshaller); return singletons; } }; ServletContainer container = new ServletContainer(application); port = InstanceSpec.getRandomPort(); server = new Server(port); ServletContextHandler root = new ServletContextHandler(ServletContextHandler.SESSIONS); root.setContextPath("/"); final ServletHolder servletHolder = new ServletHolder(container); root.addServlet(servletHolder, "/*"); servletHolder.setInitOrder(1); server.setHandler(root); server.start(); } @AfterEach public void teardown() throws Exception { server.stop(); server.join(); } @Test public void testRegisterService() throws Exception { ServiceInstance service = ServiceInstance.builder() .name("test") .payload("From Test") .serviceType(ServiceType.STATIC) .build(); ClientConfig config = new DefaultClientConfig() { @Override public Set getSingletons() { Set singletons = Sets.newHashSet(); singletons.add(context); singletons.add(serviceNamesMarshaller); singletons.add(serviceInstanceMarshaller); singletons.add(serviceInstancesMarshaller); return singletons; } }; Client client = Client.create(config); WebResource resource = client.resource("http://" + HOST + ":" + port); resource.path("/v1/service/test/" + service.getId()).type(MediaType.APPLICATION_JSON_TYPE).put(service); ServiceNames names = resource.path("/v1/service").get(ServiceNames.class); assertEquals(names.getNames(), Lists.newArrayList("test")); GenericType> type = new GenericType>(){}; ServiceInstances instances = resource.path("/v1/service/test").get(type); assertEquals(instances.getServices().size(), 1); assertEquals(instances.getServices().get(0), service); // Retrieve a single instance GenericType> singleInstanceType = new GenericType>(){}; ServiceInstance instance = resource.path("/v1/service/test/" + service.getId()).get(singleInstanceType); assertEquals(instance, service); } @Test public void testEmptyServiceNames() { ClientConfig config = new DefaultClientConfig() { @Override public Set getSingletons() { Set singletons = Sets.newHashSet(); singletons.add(context); singletons.add(serviceNamesMarshaller); singletons.add(serviceInstanceMarshaller); singletons.add(serviceInstancesMarshaller); return singletons; } }; Client client = Client.create(config); WebResource resource = client.resource("http://" + HOST + ":" + port); ServiceNames names = resource.path("/v1/service").get(ServiceNames.class); assertEquals(names.getNames(), Lists.newArrayList()); } } jetty_resteasy/000077500000000000000000000000001442004423600402715ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/serverRestEasyApplication.java000066400000000000000000000035221442004423600450610ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_resteasy/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_resteasy; import com.google.common.collect.Sets; import javax.ws.rs.core.Application; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; public class RestEasyApplication extends Application { public static final AtomicReference singletonsRef = new AtomicReference(new RestEasySingletons()); @Override public Set> getClasses() { Set> classes = Sets.newHashSet(); classes.add(StringDiscoveryResource.class); return classes; } @Override public Set getSingletons() { Set singletons = Sets.newHashSet(); singletons.add(singletonsRef.get().contextSingleton); singletons.add(singletonsRef.get().serviceNamesMarshallerSingleton); singletons.add(singletonsRef.get().serviceInstanceMarshallerSingleton); singletons.add(singletonsRef.get().serviceInstancesMarshallerSingleton); return singletons; } } RestEasySingletons.java000066400000000000000000000042411442004423600447420ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_resteasy/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_resteasy; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.server.entity.JsonServiceInstanceMarshaller; import org.apache.curator.x.discovery.server.entity.JsonServiceInstancesMarshaller; import org.apache.curator.x.discovery.server.entity.JsonServiceNamesMarshaller; import org.apache.curator.x.discovery.server.mocks.MockServiceDiscovery; import org.apache.curator.x.discovery.server.contexts.StringDiscoveryContext; import org.apache.curator.x.discovery.strategies.RandomStrategy; /** * For testing purposes only. You will inject these however is appropriate for your application */ public class RestEasySingletons { public final ServiceDiscovery serviceDiscoverySingleton = new MockServiceDiscovery(); public final StringDiscoveryContext contextSingleton = new StringDiscoveryContext(serviceDiscoverySingleton, new RandomStrategy(), 1000); public final JsonServiceInstanceMarshaller serviceInstanceMarshallerSingleton = new JsonServiceInstanceMarshaller(contextSingleton); public final JsonServiceInstancesMarshaller serviceInstancesMarshallerSingleton = new JsonServiceInstancesMarshaller(contextSingleton); public final JsonServiceNamesMarshaller serviceNamesMarshallerSingleton = new JsonServiceNamesMarshaller(); } StringDiscoveryResource.java000066400000000000000000000033021442004423600460000ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_resteasy/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_resteasy; import org.apache.curator.x.discovery.server.rest.DiscoveryContext; import org.apache.curator.x.discovery.server.rest.DiscoveryResource; import javax.ws.rs.Path; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Providers; @Path("/") public class StringDiscoveryResource extends DiscoveryResource { public StringDiscoveryResource(@Context Providers providers) { super(getContextFromProvider(providers)); } private static DiscoveryContext getContextFromProvider(Providers providers) { ContextResolver contextResolver = providers.getContextResolver(DiscoveryContext.class, MediaType.WILDCARD_TYPE); //noinspection unchecked return contextResolver.getContext(DiscoveryContext.class); } } TestStringsWithRestEasy.java000066400000000000000000000146101442004423600457430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/jetty_resteasy/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.jetty_resteasy; import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.common.collect.Lists; import com.google.common.io.ByteSource; import com.google.common.io.CharStreams; import org.apache.curator.test.InstanceSpec; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceType; import org.apache.curator.x.discovery.server.entity.ServiceInstances; import org.apache.curator.x.discovery.server.entity.ServiceNames; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher; import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import javax.ws.rs.core.MediaType; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; @SuppressWarnings("unchecked") public class TestStringsWithRestEasy { private static final String HOST = "127.0.0.1"; private Server server; private int port; @BeforeEach public void setup() throws Exception { RestEasyApplication.singletonsRef.set(new RestEasySingletons()); ResteasyProviderFactory.setInstance(new ResteasyProviderFactory()); HttpServletDispatcher dispatcher = new HttpServletDispatcher(); port = InstanceSpec.getRandomPort(); server = new Server(port); ServletContextHandler root = new ServletContextHandler(ServletContextHandler.SESSIONS); root.setContextPath("/"); root.setServer(server); root.setContextPath("/"); root.getInitParams().put("javax.ws.rs.Application", RestEasyApplication.class.getName()); root.addServlet(new ServletHolder(dispatcher), "/*"); root.addEventListener(new ResteasyBootstrap()); server.setHandler(root); server.start(); } @AfterEach public void teardown() throws Exception { server.stop(); server.join(); } @Test public void testRegisterService() throws Exception { RestEasySingletons restEasySingletons = RestEasyApplication.singletonsRef.get(); ServiceInstance service = ServiceInstance.builder() .name("test") .payload("From Test") .serviceType(ServiceType.STATIC) .build(); ByteArrayOutputStream out = new ByteArrayOutputStream(); restEasySingletons.serviceInstanceMarshallerSingleton.writeTo(service, null, null, null, null, null, out); getJson("http://" + HOST + ":" + port + "/v1/service/test/" + service.getId(), new String(out.toByteArray())); String json = getJson("http://" + HOST + ":" + port + "/v1/service", null); ServiceNames names = restEasySingletons.serviceNamesMarshallerSingleton.readFrom(ServiceNames.class, null, null, MediaType.APPLICATION_JSON_TYPE, null, new ByteArrayInputStream(json.getBytes())); assertEquals(names.getNames(), Lists.newArrayList("test")); json = getJson("http://" + HOST + ":" + port + "/v1/service/test", null); ServiceInstances instances = restEasySingletons.serviceInstancesMarshallerSingleton.readFrom(null, null, null, null, null, new ByteArrayInputStream(json.getBytes())); assertEquals(instances.getServices().size(), 1); assertEquals(instances.getServices().get(0), service); // Retrieve single instance json = getJson("http://" + HOST + ":" + port + "/v1/service/test/" + service.getId(), null); ServiceInstance instance = restEasySingletons.serviceInstanceMarshallerSingleton.readFrom(null, null, null, null, null, new ByteArrayInputStream(json.getBytes())); assertEquals(instance, service); } @Test public void testEmptyServiceNames() throws Exception { String json = getJson("http://" + HOST + ":" + port + "/v1/service", null); ServiceNames names = RestEasyApplication.singletonsRef.get().serviceNamesMarshallerSingleton.readFrom(ServiceNames.class, null, null, MediaType.APPLICATION_JSON_TYPE, null, new ByteArrayInputStream(json.getBytes())); assertEquals(names.getNames(), Lists.newArrayList()); } private String getJson(String urlStr, String body) throws IOException { URL url = new URL(urlStr); URLConnection urlConnection = url.openConnection(); urlConnection.addRequestProperty("Accept", "application/json"); if ( body != null ) { ((HttpURLConnection)urlConnection).setRequestMethod("PUT"); urlConnection.addRequestProperty("Content-Type", "application/json"); urlConnection.addRequestProperty("Content-Length", Integer.toString(body.length())); urlConnection.setDoOutput(true); OutputStream out = urlConnection.getOutputStream(); ByteSource.wrap(body.getBytes()).copyTo(out); } BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); try { return CharStreams.toString(in); } finally { in.close(); } } } mocks/000077500000000000000000000000001442004423600363275ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/serverMockServiceDiscovery.java000066400000000000000000000066131442004423600433020ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/java/org/apache/curator/x/discovery/server/mocks/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.server.mocks; import com.google.common.base.Supplier; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import org.apache.curator.x.discovery.ServiceCacheBuilder; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceProviderBuilder; import java.io.IOException; import java.util.Collection; public class MockServiceDiscovery implements ServiceDiscovery { private final Multimap> services = Multimaps.synchronizedMultimap ( Multimaps.newMultimap ( Maps.>>newHashMap(), new Supplier>>() { @Override public Collection> get() { return Lists.newArrayList(); } } ) ); @Override public void start() throws Exception { } @Override public void registerService(ServiceInstance service) throws Exception { services.put(service.getName(), service); } @Override public void unregisterService(ServiceInstance service) throws Exception { services.remove(service.getName(), service); } @Override public void updateService(ServiceInstance service) throws Exception { services.put(service.getName(), service); } @Override public ServiceCacheBuilder serviceCacheBuilder() { throw new UnsupportedOperationException(); } @Override public Collection queryForNames() throws Exception { return services.keys(); } @Override public Collection> queryForInstances(String name) throws Exception { return services.get(name); } @Override public ServiceInstance queryForInstance(String name, String id) throws Exception { Collection> instances = services.get(name); for ( ServiceInstance instance : instances ) { if ( instance.getId().equals(id) ) { return instance; } } return null; } @Override public ServiceProviderBuilder serviceProviderBuilder() { throw new UnsupportedOperationException(); } @Override public void close() throws IOException { } } curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/resources/000077500000000000000000000000001442004423600271105ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery-server/src/test/resources/log4j.properties000066400000000000000000000021071442004423600322450ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. log4j.rootLogger=ERROR, console log4j.logger.org.apache.curator=DEBUG, console log4j.additivity.org.apache.curator=false log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n curator-apache-curator-5.5.0/curator-x-discovery/000077500000000000000000000000001442004423600220245ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/LICENSE000066400000000000000000000261361442004423600230410ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. curator-apache-curator-5.5.0/curator-x-discovery/NOTICE000066400000000000000000000002501442004423600227250ustar00rootroot00000000000000Apache Curator Copyright 2013-2023 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). curator-apache-curator-5.5.0/curator-x-discovery/pom.xml000066400000000000000000000055541442004423600233520ustar00rootroot00000000000000 4.0.0 org.apache.curator apache-curator 5.5.0 curator-x-discovery 5.5.0 bundle Curator Service Discovery A service discovery recipe. 2011 org.apache.zookeeper.*;version="[3.4,4.0)", * org.apache.curator.x.discovery.*;version="${project.version}";-noimport:=true org.apache.curator curator-recipes com.fasterxml.jackson.core jackson-databind ${jackson-version} org.apache.curator curator-test test org.junit.jupiter junit-jupiter-api test org.slf4j slf4j-log4j12 test org.apache.commons commons-math test curator-apache-curator-5.5.0/curator-x-discovery/src/000077500000000000000000000000001442004423600226135ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/000077500000000000000000000000001442004423600235375ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/000077500000000000000000000000001442004423600244605ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/000077500000000000000000000000001442004423600252475ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/000077500000000000000000000000001442004423600264705ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/000077500000000000000000000000001442004423600301475ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/000077500000000000000000000000001442004423600304165ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/000077500000000000000000000000001442004423600324255ustar00rootroot00000000000000DownInstancePolicy.java000066400000000000000000000035341442004423600367720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import java.util.concurrent.TimeUnit; /** * Abstraction for values that determine when an instance is down */ public class DownInstancePolicy { private final long timeoutMs; private final int errorThreshold; private static final long DEFAULT_TIMEOUT_MS = 30000; private static final int DEFAULT_THRESHOLD = 2; /** * Policy with default values */ public DownInstancePolicy() { this(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS, DEFAULT_THRESHOLD); } /** * @param timeout window of time for down instances * @param unit time unit * @param errorThreshold number of errors within time window that denotes a down instance */ public DownInstancePolicy(long timeout, TimeUnit unit, int errorThreshold) { this.timeoutMs = unit.toMillis(timeout); this.errorThreshold = errorThreshold; } public long getTimeoutMs() { return timeoutMs; } public int getErrorThreshold() { return errorThreshold; } } InstanceFilter.java000066400000000000000000000017661442004423600361350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import com.google.common.base.Predicate; /** * Typedef for an Instance predicate */ public interface InstanceFilter extends Predicate> { } LocalIpFilter.java000066400000000000000000000021021442004423600356750ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; public interface LocalIpFilter { public boolean use(NetworkInterface networkInterface, InetAddress address) throws SocketException; } ProviderStrategy.java000066400000000000000000000024761442004423600365370ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import org.apache.curator.x.discovery.details.InstanceProvider; /** * A strategy for picking one from a set of instances */ public interface ProviderStrategy { /** * Given a source of instances, return one of them for a single use. * * @param instanceProvider the instance provider * @return the instance to use * @throws Exception any errors */ public ServiceInstance getInstance(InstanceProvider instanceProvider) throws Exception; } ServiceCache.java000066400000000000000000000034061442004423600355400ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import org.apache.curator.framework.listen.Listenable; import org.apache.curator.x.discovery.details.InstanceProvider; import org.apache.curator.x.discovery.details.ServiceCacheListener; import java.io.Closeable; import java.util.List; import java.util.concurrent.CountDownLatch; public interface ServiceCache extends Closeable, Listenable, InstanceProvider { /** * Return the current list of instances. NOTE: there is no guarantee of freshness. This is * merely the last known list of instances. However, the list is updated via a ZooKeeper watcher * so it should be fresh within a window of a second or two. * * @return the list */ public List> getInstances(); /** * The cache must be started before use * * @throws Exception errors */ public void start() throws Exception; CountDownLatch startImmediate() throws Exception; } ServiceCacheBuilder.java000066400000000000000000000042031442004423600370430ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; public interface ServiceCacheBuilder { /** * Return a new service cache with the current settings * * @return service cache */ public ServiceCache build(); /** * The name of the service to cache (required) * * @param name service name * @return this */ public ServiceCacheBuilder name(String name); /** * Optional thread factory to use for the cache's internal thread. The specified ExecutorService * overrides any prior ThreadFactory or ExecutorService set on the ServiceCacheBuilder. * * @param threadFactory factory * @return this * @deprecated use {@link #executorService(ExecutorService)} instead */ @Deprecated public ServiceCacheBuilder threadFactory(ThreadFactory threadFactory); /** * Optional ExecutorService to use for the cache's background thread. The specified ExecutorService * will be wrapped in a CloseableExecutorService and overrides any prior ThreadFactory or ExecutorService * set on the ServiceCacheBuilder. * * @param executorService executor service * @return this */ public ServiceCacheBuilder executorService(ExecutorService executorService); } ServiceDiscovery.java000066400000000000000000000057671442004423600365200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import org.apache.curator.x.discovery.strategies.RoundRobinStrategy; import java.io.Closeable; import java.util.Collection; public interface ServiceDiscovery extends Closeable { /** * The discovery must be started before use * * @throws Exception errors */ public void start() throws Exception; /** * Register/re-register a service * * @param service service to add * @throws Exception errors */ public void registerService(ServiceInstance service) throws Exception; /** * Update a service * * @param service service to update * @throws Exception errors */ public void updateService(ServiceInstance service) throws Exception; /** * Unregister/remove a service instance * * @param service the service * @throws Exception errors */ public void unregisterService(ServiceInstance service) throws Exception; /** * Allocate a new service cache builder. The refresh padding is defaulted to 1 second. * * @return new cache builder */ public ServiceCacheBuilder serviceCacheBuilder(); /** * Return the names of all known services * * @return list of service names * @throws Exception errors */ public Collection queryForNames() throws Exception; /** * Return all known instances for the given service * * @param name name of the service * @return list of instances (or an empty list) * @throws Exception errors */ public Collection> queryForInstances(String name) throws Exception; /** * Return a service instance POJO * * @param name name of the service * @param id ID of the instance * @return the instance or null if not found * @throws Exception errors */ public ServiceInstance queryForInstance(String name, String id) throws Exception; /** * Allocate a new builder. {@link ServiceProviderBuilder#providerStrategy} is set to {@link RoundRobinStrategy} * * @return the builder */ public ServiceProviderBuilder serviceProviderBuilder(); } ServiceDiscoveryBuilder.java000066400000000000000000000077731442004423600400260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.x.discovery.details.InstanceSerializer; import org.apache.curator.x.discovery.details.JsonInstanceSerializer; import org.apache.curator.x.discovery.details.ServiceDiscoveryImpl; public class ServiceDiscoveryBuilder { private CuratorFramework client; private String basePath; private InstanceSerializer serializer; private ServiceInstance thisInstance; private Class payloadClass; private boolean watchInstances = false; /** * Return a new builder. * * @param payloadClass the class of the payload of your service instance (you can use {@link Void} * if your instances don't need a payload) * @return new builder */ public static ServiceDiscoveryBuilder builder(Class payloadClass) { return new ServiceDiscoveryBuilder(payloadClass); } /** * Build a new service discovery with the currently set values. If not set, the builder will be * defaulted with a {@link JsonInstanceSerializer}. * * @return new service discovery */ public ServiceDiscovery build() { if ( serializer == null ) { serializer(new JsonInstanceSerializer(payloadClass)); } return new ServiceDiscoveryImpl(client, basePath, serializer, thisInstance, watchInstances); } /** * Required - set the client to use * * @param client client * @return this */ public ServiceDiscoveryBuilder client(CuratorFramework client) { this.client = client; return this; } /** * Required - set the base path to store in ZK * * @param basePath base path * @return this */ public ServiceDiscoveryBuilder basePath(String basePath) { this.basePath = basePath; return this; } /** * optional - change the serializer used (the default is {@link JsonInstanceSerializer} * * @param serializer the serializer * @return this */ public ServiceDiscoveryBuilder serializer(InstanceSerializer serializer) { this.serializer = serializer; return this; } /** * Optional - instance that represents the service that is running. The instance will get auto-registered * * @param thisInstance initial instance * @return this */ public ServiceDiscoveryBuilder thisInstance(ServiceInstance thisInstance) { this.thisInstance = thisInstance; return this; } /** * Optional - if true, watches for changes to locally registered instances * (via {@link #thisInstance(ServiceInstance)} or {@link ServiceDiscovery#registerService(ServiceInstance)}). * If the data for instances changes, they are reloaded. * * @param watchInstances true to watch instances * @return this */ public ServiceDiscoveryBuilder watchInstances(boolean watchInstances) { this.watchInstances = watchInstances; return this; } ServiceDiscoveryBuilder(Class payloadClass) { this.payloadClass = payloadClass; } } ServiceInstance.java000066400000000000000000000230601442004423600362770ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import java.net.InetAddress; import java.util.Collection; import java.util.Map; import java.util.UUID; /** * POJO that represents a service instance */ public class ServiceInstance { private final String name; private final String id; private final String address; private final Integer port; private final Integer sslPort; private final T payload; private final long registrationTimeUTC; private final ServiceType serviceType; private final UriSpec uriSpec; private final boolean enabled; /** * Return a new builder. The {@link #address} is set to the ip of the first * NIC in the system. The {@link #id} is set to a random UUID. * * @return builder * @throws Exception errors getting the local IP */ public static ServiceInstanceBuilderbuilder() throws Exception { String address = null; Collection ips = ServiceInstanceBuilder.getAllLocalIPs(); if ( ips.size() > 0 ) { address = ips.iterator().next().getHostAddress(); // default to the first address } String id = UUID.randomUUID().toString(); return new ServiceInstanceBuilder().address(address).id(id).registrationTimeUTC(System.currentTimeMillis()); } /** * @param name name of the service * @param id id of this instance (must be unique) * @param address address of this instance * @param port the port for this instance or null * @param sslPort the SSL port for this instance or null * @param payload the payload for this instance or null * @param registrationTimeUTC the time (in UTC) of the registration * @param serviceType type of the service * @param uriSpec the uri spec or null */ public ServiceInstance(String name, String id, String address, Integer port, Integer sslPort, T payload, long registrationTimeUTC, ServiceType serviceType, UriSpec uriSpec) { this(name, id, address, port, sslPort, payload, registrationTimeUTC, serviceType, uriSpec, true); } /** * IMPORTANT: Due to CURATOR-275 the enabled field is NOT supported * by default. If you wish to use the enabled field, you must set a {@link org.apache.curator.x.discovery.details.InstanceSerializer} * that serializes this field. The default serializer, {@link org.apache.curator.x.discovery.details.JsonInstanceSerializer} does not * serialize the field by default. You must use the alternate constructor {@link org.apache.curator.x.discovery.details.JsonInstanceSerializer#JsonInstanceSerializer(Class, boolean)} * passing false for compatibleSerializationMode. * * @param name name of the service * @param id id of this instance (must be unique) * @param address address of this instance * @param port the port for this instance or null * @param sslPort the SSL port for this instance or null * @param payload the payload for this instance or null * @param registrationTimeUTC the time (in UTC) of the registration * @param serviceType type of the service * @param uriSpec the uri spec or null * @param enabled true if the instance should be considered enabled */ public ServiceInstance(String name, String id, String address, Integer port, Integer sslPort, T payload, long registrationTimeUTC, ServiceType serviceType, UriSpec uriSpec, boolean enabled) { name = Preconditions.checkNotNull(name, "name cannot be null"); id = Preconditions.checkNotNull(id, "id cannot be null"); this.serviceType = serviceType; this.uriSpec = uriSpec; this.name = name; this.id = id; this.address = address; this.port = port; this.sslPort = sslPort; this.payload = payload; this.registrationTimeUTC = registrationTimeUTC; this.enabled = enabled; } /** * Inits to default values. Only exists for deserialization */ ServiceInstance() { this("", "", null, null, null, null, 0, ServiceType.DYNAMIC, null, true); } public String getName() { return name; } public String getId() { return id; } public String getAddress() { return address; } public Integer getPort() { return port; } public Integer getSslPort() { return sslPort; } @JsonTypeInfo(use= JsonTypeInfo.Id.CLASS, defaultImpl=Object.class) public T getPayload() { return payload; } public long getRegistrationTimeUTC() { return registrationTimeUTC; } public ServiceType getServiceType() { return serviceType; } public UriSpec getUriSpec() { return uriSpec; } /** * IMPORTANT: Due to CURATOR-275 the enabled field is NOT supported * by default. If you wish to use the enabled field, you must set a {@link org.apache.curator.x.discovery.details.InstanceSerializer} * that serializes this field. The default serializer, {@link org.apache.curator.x.discovery.details.JsonInstanceSerializer} does not * serialize the field by default. You must use the alternate constructor {@link org.apache.curator.x.discovery.details.JsonInstanceSerializer#JsonInstanceSerializer(Class, boolean)} * passing false for compatibleSerializationMode. * * @return true/false */ public boolean isEnabled() { return enabled; } public String buildUriSpec() { return buildUriSpec(Maps.newHashMap()); } public String buildUriSpec(Map variables) { return (uriSpec != null) ? uriSpec.build(this, variables) : ""; } @SuppressWarnings("RedundantIfStatement") @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } ServiceInstance that = (ServiceInstance)o; if ( registrationTimeUTC != that.registrationTimeUTC ) { return false; } if ( address != null ? !address.equals(that.address) : that.address != null ) { return false; } if ( id != null ? !id.equals(that.id) : that.id != null ) { return false; } if ( name != null ? !name.equals(that.name) : that.name != null ) { return false; } if ( payload != null ? !payload.equals(that.payload) : that.payload != null ) { return false; } if ( port != null ? !port.equals(that.port) : that.port != null ) { return false; } if ( serviceType != that.serviceType ) { return false; } if ( sslPort != null ? !sslPort.equals(that.sslPort) : that.sslPort != null ) { return false; } if ( uriSpec != null ? !uriSpec.equals(that.uriSpec) : that.uriSpec != null ) { return false; } if ( enabled != that.enabled ) { return false; } return true; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + (id != null ? id.hashCode() : 0); result = 31 * result + (address != null ? address.hashCode() : 0); result = 31 * result + (port != null ? port.hashCode() : 0); result = 31 * result + (sslPort != null ? sslPort.hashCode() : 0); result = 31 * result + (payload != null ? payload.hashCode() : 0); result = 31 * result + (int)(registrationTimeUTC ^ (registrationTimeUTC >>> 32)); result = 31 * result + (serviceType != null ? serviceType.hashCode() : 0); result = 31 * result + (uriSpec != null ? uriSpec.hashCode() : 0); result = 31 * result + (enabled ? 1 : 0); return result; } @Override public String toString() { return "ServiceInstance{" + "name='" + name + '\'' + ", id='" + id + '\'' + ", address='" + address + '\'' + ", port=" + port + ", sslPort=" + sslPort + ", payload=" + payload + ", registrationTimeUTC=" + registrationTimeUTC + ", serviceType=" + serviceType + ", uriSpec=" + uriSpec + ", enabled=" + enabled + '}'; } } ServiceInstanceBuilder.java000066400000000000000000000125721442004423600376140ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import com.google.common.collect.Lists; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.concurrent.atomic.AtomicReference; /** * Builder for service instances */ public class ServiceInstanceBuilder { private T payload; private String name; private String address; private Integer port; private Integer sslPort; private String id; private long registrationTimeUTC; private ServiceType serviceType = ServiceType.DYNAMIC; private UriSpec uriSpec; private boolean enabled = true; private static final AtomicReference localIpFilter = new AtomicReference ( new LocalIpFilter() { @Override public boolean use(NetworkInterface nif, InetAddress adr) throws SocketException { return (adr != null) && !adr.isLoopbackAddress() && (nif.isPointToPoint() || !adr.isLinkLocalAddress()); } } ); /** * Replace the default local ip filter used by {@link #getAllLocalIPs()} * * @param newLocalIpFilter the new local ip filter */ public static void setLocalIpFilter(LocalIpFilter newLocalIpFilter) { localIpFilter.set(newLocalIpFilter); } /** * Return the current local ip filter used by {@link #getAllLocalIPs()} * * @return ip filter */ public static LocalIpFilter getLocalIpFilter() { return localIpFilter.get(); } ServiceInstanceBuilder() { } /** * Return a new instance with the currently set values * * @return instance */ public ServiceInstance build() { return new ServiceInstance(name, id, address, port, sslPort, payload, registrationTimeUTC, serviceType, uriSpec, enabled); } public ServiceInstanceBuilder name(String name) { this.name = name; return this; } public ServiceInstanceBuilder address(String address) { this.address = address; return this; } public ServiceInstanceBuilder id(String id) { this.id = id; return this; } public ServiceInstanceBuilder port(int port) { this.port = port; return this; } public ServiceInstanceBuilder sslPort(int port) { this.sslPort = port; return this; } public ServiceInstanceBuilder payload(T payload) { this.payload = payload; return this; } public ServiceInstanceBuilder serviceType(ServiceType serviceType) { this.serviceType = serviceType; return this; } public ServiceInstanceBuilder registrationTimeUTC(long registrationTimeUTC) { this.registrationTimeUTC = registrationTimeUTC; return this; } public ServiceInstanceBuilder uriSpec(UriSpec uriSpec) { this.uriSpec = uriSpec; return this; } public ServiceInstanceBuilder enabled(boolean enabled) { this.enabled = enabled; return this; } /** * based on http://pastebin.com/5X073pUc *

* * Returns all available IP addresses. *

* In error case or if no network connection is established, we return * an empty list here. *

* Loopback addresses are excluded - so 127.0.0.1 will not be never * returned. *

* The "primary" IP might not be the first one in the returned list. * * @return Returns all IP addresses (can be an empty list in error case * or if network connection is missing). * @since 0.1.0 * @throws SocketException errors */ public static Collection getAllLocalIPs() throws SocketException { List listAdr = Lists.newArrayList(); Enumeration nifs = NetworkInterface.getNetworkInterfaces(); if (nifs == null) return listAdr; while (nifs.hasMoreElements()) { NetworkInterface nif = nifs.nextElement(); // We ignore subinterfaces - as not yet needed. Enumeration adrs = nif.getInetAddresses(); while ( adrs.hasMoreElements() ) { InetAddress adr = adrs.nextElement(); if ( localIpFilter.get().use(nif, adr) ) { listAdr.add(adr); } } } return listAdr; } } ServiceProvider.java000066400000000000000000000046521442004423600363330ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import org.apache.curator.x.discovery.details.InstanceProvider; import java.io.Closeable; import java.io.IOException; import java.util.Collection; /** * The main API for Discovery. This class is essentially a facade over a {@link ProviderStrategy} * paired with an {@link InstanceProvider} */ public interface ServiceProvider extends Closeable { /** * The provider must be started before use * * @throws Exception any errors */ public void start() throws Exception; /** * Return an instance for a single use. IMPORTANT: users * should not hold on to the instance returned. They should always get a fresh instance. * * @return the instance to use * @throws Exception any errors */ public ServiceInstance getInstance() throws Exception; /** * Return the current available set of instances IMPORTANT: users * should not hold on to the instance returned. They should always get a fresh list. * * @return all known instances * @throws Exception any errors */ public Collection> getAllInstances() throws Exception; /** * Take note of an error connecting to the given instance. The instance will potentially * be marked as "down" depending on the {@link DownInstancePolicy}. * * @param instance instance that had an error */ public void noteError(ServiceInstance instance); /** * Close the provider. Note: it's the provider's responsibility to close any caches it manages */ @Override void close() throws IOException; } ServiceProviderBuilder.java000066400000000000000000000061061442004423600376360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import org.apache.curator.x.discovery.strategies.RoundRobinStrategy; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; public interface ServiceProviderBuilder { /** * Allocate a new service provider based on the current builder settings * * @return provider */ ServiceProvider build(); /** * required - set the name of the service to be provided * * @param serviceName the name of the service * @return this */ ServiceProviderBuilder serviceName(String serviceName); /** * optional - set the provider strategy. The default is {@link RoundRobinStrategy} * * @param providerStrategy strategy to use * @return this */ ServiceProviderBuilder providerStrategy(ProviderStrategy providerStrategy); /** * optional - the thread factory to use for creating internal threads. The specified ThreadFactory overrides * any prior ThreadFactory or ClosableExecutorService set on the ServiceProviderBuilder * * @param threadFactory factory to use * @return this * @deprecated use {@link #executorService(ExecutorService)} instead */ @Deprecated ServiceProviderBuilder threadFactory(ThreadFactory threadFactory); /** * Set the down instance policy * * @param downInstancePolicy new policy * @return this */ ServiceProviderBuilder downInstancePolicy(DownInstancePolicy downInstancePolicy); /** * Add an instance filter. NOTE: this does not remove previously added filters. i.e. * a list is created of all added filters. Filters are called in the order they were * added. * * @param filter filter to add * @return this */ ServiceProviderBuilder additionalFilter(InstanceFilter filter); /** * Optional ExecutorService to use for the cache's background thread. The specified ExecutorService * will be wrapped in a CloseableExecutorService and overrides any prior ThreadFactory or CloseableExecutorService * set on the ServiceProviderBuilder. * * @param executorService executor service * @return this */ ServiceProviderBuilder executorService(ExecutorService executorService); } ServiceType.java000066400000000000000000000020111442004423600354450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; public enum ServiceType { DYNAMIC, STATIC, PERMANENT, DYNAMIC_SEQUENTIAL; public boolean isDynamic() { return this == DYNAMIC || this == DYNAMIC_SEQUENTIAL; } } UriSpec.java000066400000000000000000000251331442004423600345670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; /** *

* An abstraction for specifying a URI for an instance allowing for variable substitutions. *

* *

* A Uri spec is a string with optional replacement fields. A replacement field begins with * an open brace and ends with a close brace. The value between the braces is the name of the * field. e.g. "{scheme}://foo.com:{port}" has two replacement fields named "scheme" and "port". * Several pre-defined fields are listed as constants in this class (e.g. {@link #FIELD_SCHEME}). *

*/ public class UriSpec implements Iterable { private final Logger log = LoggerFactory.getLogger(getClass()); private final List parts = Lists.newArrayList(); /** * This defaults to "http". If a {@link ServiceInstance} is passed when building and an sslPort * is specified in the instance, the replacement is "https". */ public static final String FIELD_SCHEME = "scheme"; /** * If a {@link ServiceInstance} is passed when building, the replacement is {@link ServiceInstance#getName()} */ public static final String FIELD_NAME = "name"; /** * If a {@link ServiceInstance} is passed when building, the replacement is {@link ServiceInstance#getId()} */ public static final String FIELD_ID = "id"; /** * If a {@link ServiceInstance} is passed when building, the replacement is {@link ServiceInstance#getAddress()} */ public static final String FIELD_ADDRESS = "address"; /** * If a {@link ServiceInstance} is passed when building, the replacement is {@link ServiceInstance#getPort()} */ public static final String FIELD_PORT = "port"; /** * If a {@link ServiceInstance} is passed when building, the replacement is {@link ServiceInstance#getSslPort()} */ public static final String FIELD_SSL_PORT = "ssl-port"; /** * If a {@link ServiceInstance} is passed when building, the replacement is {@link ServiceInstance#getRegistrationTimeUTC()} */ public static final String FIELD_REGISTRATION_TIME_UTC = "registration-time-utc"; /** * If a {@link ServiceInstance} is passed when building, the replacement is {@link ServiceInstance#getServiceType()} */ public static final String FIELD_SERVICE_TYPE = "service-type"; /** * Always replaced with '{' - i.e. this is how to insert a literal '{' */ public static final String FIELD_OPEN_BRACE = "["; /** * Always replaced with '}' - i.e. this is how to insert a literal '}' */ public static final String FIELD_CLOSE_BRACE = "]"; /** * Represents one token in the Uri spec */ public static class Part { private final String value; private final boolean variable; /** * @param value the token value * @param isVariable if true, a replacement field. If false, a literal string */ public Part(String value, boolean isVariable) { this.value = value; this.variable = isVariable; } public Part() { value = ""; variable = false; } public String getValue() { return value; } public boolean isVariable() { return variable; } @SuppressWarnings("RedundantIfStatement") @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } Part part = (Part)o; if ( variable != part.variable ) { return false; } if ( !value.equals(part.value) ) { return false; } return true; } @Override public int hashCode() { int result = value.hashCode(); result = 31 * result + (variable ? 1 : 0); return result; } } public UriSpec() { // NOP } /** * @param rawSpec the spec to parse */ public UriSpec(String rawSpec) { boolean isInsideVariable = false; StringTokenizer tokenizer = new StringTokenizer(rawSpec, "{}", true); while ( tokenizer.hasMoreTokens() ) { String token = tokenizer.nextToken(); if ( token.equals("{") ) { Preconditions.checkState(!isInsideVariable, "{ is not allowed inside of a variable specification"); isInsideVariable = true; } else if ( token.equals("}") ) { Preconditions.checkState(isInsideVariable, "} must be preceded by {"); isInsideVariable = false; } else { if ( isInsideVariable ) { token = token.trim(); } add(new Part(token, isInsideVariable)); } } Preconditions.checkState(!isInsideVariable, "Final variable not closed - expected }"); } /** * Build into a UriSpec string * * @return UriSpec string */ public String build() { return build(null, Maps.newHashMap()); } /** * Build into a UriSpec string * * @param serviceInstance instance to use for pre-defined replacement fields * @return UriSpec string */ public String build(ServiceInstance serviceInstance) { return build(serviceInstance, Maps.newHashMap()); } /** * Build into a UriSpec string * * @param variables a mapping of field replacement names to values. Note: any fields listed * in this map override pre-defined fields * @return UriSpec string */ public String build(Map variables) { return build(null, variables); } /** * Build into a UriSpec string * * @param serviceInstance instance to use for pre-defined replacement fields * @param variables a mapping of field replacement names to values. Note: any fields listed * in this map override pre-defined fields * @return UriSpec string */ public String build(ServiceInstance serviceInstance, Map variables) { Map localVariables = Maps.newHashMap(); localVariables.put(FIELD_OPEN_BRACE, "{"); localVariables.put(FIELD_CLOSE_BRACE, "}"); localVariables.put(FIELD_SCHEME, "http"); if ( serviceInstance != null ) { localVariables.put(FIELD_NAME, nullCheck(serviceInstance.getName())); localVariables.put(FIELD_ID, nullCheck(serviceInstance.getId())); localVariables.put(FIELD_ADDRESS, nullCheck(serviceInstance.getAddress())); localVariables.put(FIELD_PORT, nullCheck(serviceInstance.getPort())); localVariables.put(FIELD_SSL_PORT, nullCheck(serviceInstance.getSslPort())); localVariables.put(FIELD_REGISTRATION_TIME_UTC, nullCheck(serviceInstance.getRegistrationTimeUTC())); localVariables.put(FIELD_SERVICE_TYPE, (serviceInstance.getServiceType() != null) ? serviceInstance.getServiceType().name().toLowerCase() : ""); if ( serviceInstance.getSslPort() != null ) { localVariables.put(FIELD_SCHEME, "https"); } } localVariables.putAll(variables); StringBuilder str = new StringBuilder(); for ( Part p : parts ) { if ( p.isVariable() ) { Object value = localVariables.get(p.getValue()); if ( value == null ) { log.debug("Variable not found: " + p.getValue()); } else { str.append(value); } } else { str.append(p.getValue()); } } return str.toString(); } @Override public Iterator iterator() { return Iterators.unmodifiableIterator(parts.iterator()); } /** * @return the parts */ public List getParts() { return ImmutableList.copyOf(parts); } /** * Add a part to the end of the list * * @param part part to add */ public void add(Part part) { parts.add(part); } /** * Remove the given part * * @param part the part */ public void remove(Part part) { parts.remove(part); } @SuppressWarnings("RedundantIfStatement") @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } UriSpec spec = (UriSpec)o; if ( !parts.equals(spec.parts) ) { return false; } return true; } @Override public int hashCode() { return parts.hashCode(); } private Object nullCheck(Object o) { return (o != null) ? o : ""; } } details/000077500000000000000000000000001442004423600337735ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discoveryDownInstanceManager.java000066400000000000000000000062571442004423600405370ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import com.google.common.collect.Maps; import org.apache.curator.x.discovery.DownInstancePolicy; import org.apache.curator.x.discovery.InstanceFilter; import org.apache.curator.x.discovery.ServiceInstance; import java.util.Iterator; import java.util.Map.Entry; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; class DownInstanceManager implements InstanceFilter { private final ConcurrentMap, Status> statuses = Maps.newConcurrentMap(); private final DownInstancePolicy downInstancePolicy; private final AtomicLong lastPurge = new AtomicLong(System.currentTimeMillis()); private static class Status { private final long startMs = System.currentTimeMillis(); private final AtomicInteger errorCount = new AtomicInteger(0); } DownInstanceManager(DownInstancePolicy downInstancePolicy) { this.downInstancePolicy = downInstancePolicy; } void add(ServiceInstance instance) { purge(); Status newStatus = new Status(); Status oldStatus = statuses.putIfAbsent(instance, newStatus); Status useStatus = (oldStatus != null) ? oldStatus : newStatus; useStatus.errorCount.incrementAndGet(); } @Override public boolean apply(ServiceInstance instance) { purge(); Status status = statuses.get(instance); return (status == null) || (status.errorCount.get() < downInstancePolicy.getErrorThreshold()); } private void purge() { long localLastPurge = lastPurge.get(); long ticksSinceLastPurge = System.currentTimeMillis() - localLastPurge; if ( ticksSinceLastPurge < (downInstancePolicy.getTimeoutMs() / 2) ) { return; } if ( !lastPurge.compareAndSet(localLastPurge, System.currentTimeMillis()) ) { return; } Iterator, Status>> it = statuses.entrySet().iterator(); while ( it.hasNext() ) { Entry, Status> entry = it.next(); long elapsedMs = System.currentTimeMillis() - entry.getValue().startMs; if ( elapsedMs >= downInstancePolicy.getTimeoutMs() ) { it.remove(); } } } } FilteredInstanceProvider.java000066400000000000000000000034251442004423600416000ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import org.apache.curator.x.discovery.InstanceFilter; import org.apache.curator.x.discovery.ServiceInstance; import java.util.List; class FilteredInstanceProvider implements InstanceProvider { private final InstanceProvider instanceProvider; private final Predicate> predicates; FilteredInstanceProvider(InstanceProvider instanceProvider, List> filters) { this.instanceProvider = instanceProvider; predicates = Predicates.and(filters); } @Override public List> getInstances() throws Exception { Iterable> filtered = Iterables.filter(instanceProvider.getInstances(), predicates); return ImmutableList.copyOf(filtered); } } InstanceProvider.java000066400000000000000000000024051442004423600401160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import org.apache.curator.x.discovery.ServiceInstance; import java.util.List; /** * Provides a set of available instances for a service so that a strategy can pick one of them */ public interface InstanceProvider { /** * Return the current available set of instances * @return instances * @throws Exception any errors */ public List> getInstances() throws Exception; } InstanceSerializer.java000066400000000000000000000027731442004423600404450ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import org.apache.curator.x.discovery.ServiceInstance; /** * Injectable serializer for service instances */ public interface InstanceSerializer { /** * Serialize an instance into bytes * * @param instance the instance * @return byte array representing the instance * @throws Exception any errors */ public byte[] serialize(ServiceInstance instance) throws Exception; /** * Deserialize a byte array into an instance * * @param bytes the bytes * @return service instance * @throws Exception any errors */ public ServiceInstance deserialize(byte[] bytes) throws Exception; } JsonInstanceSerializer.java000066400000000000000000000115221442004423600412670ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.annotations.VisibleForTesting; import org.apache.curator.x.discovery.ServiceInstance; /** * A serializer that uses Jackson to serialize/deserialize as JSON. IMPORTANT: The instance * payload must support Jackson */ public class JsonInstanceSerializer implements InstanceSerializer { private final ObjectMapper mapper; private final Class payloadClass; private final boolean compatibleSerializationMode; private final JavaType type; /** * CURATOR-275 introduced a new field into {@link org.apache.curator.x.discovery.ServiceInstance}. This caused a potential * {@link com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException} in older clients that * read newly serialized ServiceInstances. Therefore the default behavior of JsonInstanceSerializer * has been changed to NOT serialize the enabled field. If you wish to use that field, use the * alternate constructor {@link #JsonInstanceSerializer(Class, boolean)} and pass true for * compatibleSerializationMode. Note: future versions of Curator may change this * behavior. * * @param payloadClass used to validate payloads when deserializing */ public JsonInstanceSerializer(Class payloadClass) { this(payloadClass, true, false); } /** * CURATOR-275 introduced a new field into {@link org.apache.curator.x.discovery.ServiceInstance}. This caused a potential * {@link com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException} in older clients that * read newly serialized ServiceInstances. If you are susceptible to this you should set the * serializer to be an instance of {@link org.apache.curator.x.discovery.details.JsonInstanceSerializer} * with compatibleSerializationMode set to true. IMPORTANT: when this is done, the new enabled * field of ServiceInstance is not serialized. If however you do want * to use the enabled field, set compatibleSerializationMode to false. * * @param payloadClass used to validate payloads when deserializing * @param compatibleSerializationMode pass true to serialize in a manner that supports clients pre-CURATOR-275 */ public JsonInstanceSerializer(Class payloadClass, boolean compatibleSerializationMode) { this(payloadClass, compatibleSerializationMode, false); } @VisibleForTesting JsonInstanceSerializer(Class payloadClass, boolean compatibleSerializationMode, boolean failOnUnknownProperties) { this.payloadClass = payloadClass; this.compatibleSerializationMode = compatibleSerializationMode; mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties); type = mapper.getTypeFactory().constructType(ServiceInstance.class); } @SuppressWarnings({"unchecked"}) @Override public ServiceInstance deserialize(byte[] bytes) throws Exception { ServiceInstance rawServiceInstance = mapper.readValue(bytes, type); payloadClass.cast(rawServiceInstance.getPayload()); // just to verify that it's the correct type return (ServiceInstance)rawServiceInstance; } @Override public byte[] serialize(ServiceInstance instance) throws Exception { if ( compatibleSerializationMode ) { OldServiceInstance compatible = new OldServiceInstance(instance.getName(), instance.getId(), instance.getAddress(), instance.getPort(), instance.getSslPort(), instance.getPayload(), instance.getRegistrationTimeUTC(), instance.getServiceType(), instance.getUriSpec()); return mapper.writeValueAsBytes(compatible); } return mapper.writeValueAsBytes(instance); } } Latch.java000066400000000000000000000022351442004423600356730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; class Latch { private volatile boolean laden = false; synchronized void set() { laden = true; notifyAll(); } synchronized void await() throws InterruptedException { while ( !laden ) { wait(); } laden = false; } } OldServiceInstance.java000066400000000000000000000134331442004423600403660ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.google.common.base.Preconditions; import org.apache.curator.x.discovery.ServiceType; import org.apache.curator.x.discovery.UriSpec; /** * POJO that represents a service instance */ class OldServiceInstance { private final String name; private final String id; private final String address; private final Integer port; private final Integer sslPort; private final T payload; private final long registrationTimeUTC; private final ServiceType serviceType; private final UriSpec uriSpec; /** * @param name name of the service * @param id id of this instance (must be unique) * @param address address of this instance * @param port the port for this instance or null * @param sslPort the SSL port for this instance or null * @param payload the payload for this instance or null * @param registrationTimeUTC the time (in UTC) of the registration * @param serviceType type of the service * @param uriSpec the uri spec or null */ OldServiceInstance(String name, String id, String address, Integer port, Integer sslPort, T payload, long registrationTimeUTC, ServiceType serviceType, UriSpec uriSpec) { name = Preconditions.checkNotNull(name, "name cannot be null"); id = Preconditions.checkNotNull(id, "id cannot be null"); this.serviceType = serviceType; this.uriSpec = uriSpec; this.name = name; this.id = id; this.address = address; this.port = port; this.sslPort = sslPort; this.payload = payload; this.registrationTimeUTC = registrationTimeUTC; } OldServiceInstance() { this("", "", null, null, null, null, 0, ServiceType.DYNAMIC, null); } public String getName() { return name; } public String getId() { return id; } public String getAddress() { return address; } public Integer getPort() { return port; } public Integer getSslPort() { return sslPort; } @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, defaultImpl = Object.class) public T getPayload() { return payload; } public long getRegistrationTimeUTC() { return registrationTimeUTC; } public ServiceType getServiceType() { return serviceType; } public UriSpec getUriSpec() { return uriSpec; } @SuppressWarnings("RedundantIfStatement") @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } OldServiceInstance that = (OldServiceInstance)o; if ( registrationTimeUTC != that.registrationTimeUTC ) { return false; } if ( address != null ? !address.equals(that.address) : that.address != null ) { return false; } if ( id != null ? !id.equals(that.id) : that.id != null ) { return false; } if ( name != null ? !name.equals(that.name) : that.name != null ) { return false; } if ( payload != null ? !payload.equals(that.payload) : that.payload != null ) { return false; } if ( port != null ? !port.equals(that.port) : that.port != null ) { return false; } if ( serviceType != that.serviceType ) { return false; } if ( sslPort != null ? !sslPort.equals(that.sslPort) : that.sslPort != null ) { return false; } if ( uriSpec != null ? !uriSpec.equals(that.uriSpec) : that.uriSpec != null ) { return false; } return true; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + (id != null ? id.hashCode() : 0); result = 31 * result + (address != null ? address.hashCode() : 0); result = 31 * result + (port != null ? port.hashCode() : 0); result = 31 * result + (sslPort != null ? sslPort.hashCode() : 0); result = 31 * result + (payload != null ? payload.hashCode() : 0); result = 31 * result + (int)(registrationTimeUTC ^ (registrationTimeUTC >>> 32)); result = 31 * result + (serviceType != null ? serviceType.hashCode() : 0); result = 31 * result + (uriSpec != null ? uriSpec.hashCode() : 0); return result; } @Override public String toString() { return "ServiceInstance{" + "name='" + name + '\'' + ", id='" + id + '\'' + ", address='" + address + '\'' + ", port=" + port + ", sslPort=" + sslPort + ", payload=" + payload + ", registrationTimeUTC=" + registrationTimeUTC + ", serviceType=" + serviceType + ", uriSpec=" + uriSpec + '}'; } } ServiceCacheBuilderImpl.java000066400000000000000000000054041442004423600413160ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import org.apache.curator.x.discovery.ServiceCache; import org.apache.curator.x.discovery.ServiceCacheBuilder; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; /** * Builder for a service cache */ class ServiceCacheBuilderImpl implements ServiceCacheBuilder { private ServiceDiscoveryImpl discovery; private String name; private ThreadFactory threadFactory; private ExecutorService executorService; ServiceCacheBuilderImpl(ServiceDiscoveryImpl discovery) { this.discovery = discovery; } /** * Return a new service cache with the current settings * * @return service cache */ @Override public ServiceCache build() { if (threadFactory != null) { return new ServiceCacheImpl(discovery, name, threadFactory); } else { return new ServiceCacheImpl(discovery, name, executorService); } } /** * The name of the service to cache (required) * * @param name service name * @return this */ @Override public ServiceCacheBuilder name(String name) { this.name = name; return this; } /** * Optional thread factory to use for the cache's internal thread * * @param threadFactory factory * @return this */ @Override @Deprecated public ServiceCacheBuilder threadFactory(ThreadFactory threadFactory) { this.threadFactory = threadFactory; this.executorService = null; return this; } /** * Optional executor service to use for the cache's background thread * * @param executorService executor service * @return this */ @Override public ServiceCacheBuilder executorService(ExecutorService executorService) { this.executorService = executorService; return this; } } ServiceCacheImpl.java000066400000000000000000000166661442004423600400230ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.EnsureContainers; import org.apache.curator.framework.listen.StandardListenerManager; import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.CuratorCache; import org.apache.curator.framework.recipes.cache.CuratorCacheBridge; import org.apache.curator.framework.recipes.cache.CuratorCacheListener; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ZKPaths; import org.apache.curator.x.discovery.ServiceCache; import org.apache.curator.x.discovery.ServiceInstance; import java.util.List; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; public class ServiceCacheImpl implements ServiceCache, PathChildrenCacheListener { private final StandardListenerManager listenerContainer = StandardListenerManager.standard(); private final ServiceDiscoveryImpl discovery; private final AtomicReference state = new AtomicReference<>(State.LATENT); private final CuratorCacheBridge cache; private final ConcurrentMap> instances = Maps.newConcurrentMap(); private final EnsureContainers ensureContainers; private final CountDownLatch initializedLatch = new CountDownLatch(1); private enum State { LATENT, STARTED, STOPPED } private static ExecutorService convertThreadFactory(ThreadFactory threadFactory) { Preconditions.checkNotNull(threadFactory, "threadFactory cannot be null"); return Executors.newSingleThreadExecutor(threadFactory); } ServiceCacheImpl(ServiceDiscoveryImpl discovery, String name, ThreadFactory threadFactory) { this(discovery, name, convertThreadFactory(threadFactory)); } ServiceCacheImpl(ServiceDiscoveryImpl discovery, String name, ExecutorService executorService) { Preconditions.checkNotNull(discovery, "discovery cannot be null"); Preconditions.checkNotNull(name, "name cannot be null"); this.discovery = discovery; String path = discovery.pathForName(name); cache = CuratorCache.bridgeBuilder(discovery.getClient(), path) .withExecutorService(executorService) .withDataNotCached() .build(); CuratorCacheListener listener = CuratorCacheListener.builder() .forPathChildrenCache(path, discovery.getClient(), this) .forInitialized(this::initialized) .build(); cache.listenable().addListener(listener); ensureContainers = new EnsureContainers(discovery.getClient(), path); } @Override public List> getInstances() { return Lists.newArrayList(instances.values()); } @VisibleForTesting volatile CountDownLatch debugStartLatch = null; volatile CountDownLatch debugStartWaitLatch = null; @Override public void start() throws Exception { startImmediate().await(); } @Override public CountDownLatch startImmediate() throws Exception { Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once"); ensureContainers.ensure(); cache.start(); if ( debugStartLatch != null ) { initializedLatch.await(); debugStartLatch.countDown(); debugStartLatch = null; } if ( debugStartWaitLatch != null ) { debugStartWaitLatch.await(); debugStartWaitLatch = null; } return initializedLatch; } @Override public void close() { Preconditions.checkState(state.compareAndSet(State.STARTED, State.STOPPED), "Already closed or has not been started"); listenerContainer.forEach(l -> discovery.getClient().getConnectionStateListenable().removeListener(l)); listenerContainer.clear(); CloseableUtils.closeQuietly(cache); discovery.cacheClosed(this); } @Override public void addListener(ServiceCacheListener listener) { listenerContainer.addListener(listener); discovery.getClient().getConnectionStateListenable().addListener(listener); } @Override public void addListener(ServiceCacheListener listener, Executor executor) { listenerContainer.addListener(listener, executor); discovery.getClient().getConnectionStateListenable().addListener(listener, executor); } @Override public void removeListener(ServiceCacheListener listener) { listenerContainer.removeListener(listener); discovery.getClient().getConnectionStateListenable().removeListener(listener); } @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) { boolean notifyListeners = false; switch ( event.getType() ) { case CHILD_ADDED: case CHILD_UPDATED: { addInstance(event.getData()); notifyListeners = true; break; } case CHILD_REMOVED: { instances.remove(instanceIdFromData(event.getData())); notifyListeners = true; break; } } if ( notifyListeners && (initializedLatch.getCount() == 0) ) { listenerContainer.forEach(ServiceCacheListener::cacheChanged); } } private String instanceIdFromData(ChildData childData) { return ZKPaths.getNodeFromPath(childData.getPath()); } private void addInstance(ChildData childData) { try { String instanceId = instanceIdFromData(childData); ServiceInstance serviceInstance = discovery.getSerializer().deserialize(childData.getData()); instances.put(instanceId, serviceInstance); } catch ( Exception e ) { throw new RuntimeException(e); } } private void initialized() { discovery.cacheOpened(this); initializedLatch.countDown(); } } ServiceCacheListener.java000066400000000000000000000022241442004423600406700ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import org.apache.curator.framework.state.ConnectionStateListener; /** * Listener for changes to a service cache */ public interface ServiceCacheListener extends ConnectionStateListener { /** * Called when the cache has changed (instances added/deleted, etc.) */ public void cacheChanged(); } ServiceDiscoveryImpl.java000066400000000000000000000407221442004423600407550ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.CuratorCache; import org.apache.curator.framework.recipes.cache.CuratorCacheBridge; import org.apache.curator.framework.recipes.cache.CuratorCacheListener; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.framework.state.ConnectionStateListener; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.ExceptionAccumulator; import org.apache.curator.utils.ThreadUtils; import org.apache.curator.utils.ZKPaths; import org.apache.curator.x.discovery.ServiceCache; import org.apache.curator.x.discovery.ServiceCacheBuilder; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceProvider; import org.apache.curator.x.discovery.ServiceProviderBuilder; import org.apache.curator.x.discovery.strategies.RoundRobinStrategy; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.concurrent.ConcurrentMap; /** * A mechanism to register and query service instances using ZooKeeper */ @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") public class ServiceDiscoveryImpl implements ServiceDiscovery { private final Logger log = LoggerFactory.getLogger(getClass()); private final CuratorFramework client; private final String basePath; private final InstanceSerializer serializer; private final ConcurrentMap> services = Maps.newConcurrentMap(); private final Collection> caches = Sets.newSetFromMap(Maps., Boolean>newConcurrentMap()); private final Collection> providers = Sets.newSetFromMap(Maps., Boolean>newConcurrentMap()); private final boolean watchInstances; private final ConnectionStateListener connectionStateListener = new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { if ( (newState == ConnectionState.RECONNECTED) || (newState == ConnectionState.CONNECTED) ) { try { log.debug("Re-registering due to reconnection"); reRegisterServices(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } catch ( Exception e ) { log.error("Could not re-register instances after reconnection", e); } } } }; private static class Entry { private volatile ServiceInstance service; private volatile CuratorCacheBridge cache; private Entry(ServiceInstance service) { this.service = service; } } /** * @param client the client * @param basePath base path to store data * @param serializer serializer for instances (e.g. {@link JsonInstanceSerializer}) * @param thisInstance instance that represents the service that is running. The instance will get auto-registered * @param watchInstances if true, watches for changes to locally registered instances */ public ServiceDiscoveryImpl(CuratorFramework client, String basePath, InstanceSerializer serializer, ServiceInstance thisInstance, boolean watchInstances) { this.watchInstances = watchInstances; this.client = Preconditions.checkNotNull(client, "client cannot be null"); this.basePath = Preconditions.checkNotNull(basePath, "basePath cannot be null"); this.serializer = Preconditions.checkNotNull(serializer, "serializer cannot be null"); if ( thisInstance != null ) { Entry entry = new Entry(thisInstance); entry.cache = makeNodeCache(thisInstance); services.put(thisInstance.getId(), entry); } } /** * The discovery must be started before use * * @throws Exception errors */ @Override public void start() throws Exception { try { reRegisterServices(); } catch ( KeeperException e ) { log.error("Could not register instances - will try again later", e); } client.getConnectionStateListenable().addListener(connectionStateListener); } @Override public void close() throws IOException { ExceptionAccumulator accumulator = new ExceptionAccumulator(); for ( ServiceProvider provider : Lists.newArrayList(providers) ) { CloseableUtils.closeQuietly(provider); } for ( Entry entry : services.values() ) { try { internalUnregisterService(entry); } catch ( KeeperException.NoNodeException ignore ) { // ignore } catch ( Exception e ) { accumulator.add(e); log.error("Could not unregister instance: " + entry.service.getName(), e); } } client.getConnectionStateListenable().removeListener(connectionStateListener); accumulator.propagate(); } /** * Register/re-register/update a service instance * * @param service service to add * @throws Exception errors */ @Override public void registerService(ServiceInstance service) throws Exception { Entry newEntry = new Entry(service); Entry oldEntry = services.putIfAbsent(service.getId(), newEntry); Entry useEntry = (oldEntry != null) ? oldEntry : newEntry; synchronized(useEntry) { if ( useEntry == newEntry ) // i.e. is new { useEntry.cache = makeNodeCache(service); } internalRegisterService(service); } } @Override public void updateService(final ServiceInstance service) throws Exception { Entry entry = services.get(service.getId()); if ( entry == null ) { throw new Exception("Service not registered: " + service); } synchronized(entry) { entry.service = service; byte[] bytes = serializer.serialize(service); String path = pathForInstance(service.getName(), service.getId()); client.setData().forPath(path, bytes); } } @VisibleForTesting protected void internalRegisterService(ServiceInstance service) throws Exception { byte[] bytes = serializer.serialize(service); String path = pathForInstance(service.getName(), service.getId()); final int MAX_TRIES = 2; boolean isDone = false; for ( int i = 0; !isDone && (i < MAX_TRIES); ++i ) { try { CreateMode mode; switch (service.getServiceType()) { case DYNAMIC: mode = CreateMode.EPHEMERAL; break; case DYNAMIC_SEQUENTIAL: mode = CreateMode.EPHEMERAL_SEQUENTIAL; break; default: mode = CreateMode.PERSISTENT; break; } client.create().creatingParentContainersIfNeeded().withMode(mode).forPath(path, bytes); isDone = true; } catch ( KeeperException.NodeExistsException e ) { client.delete().forPath(path); // must delete then re-create so that watchers fire } } } /** * Unregister/remove a service instance * * @param service the service * @throws Exception errors */ @Override public void unregisterService(ServiceInstance service) throws Exception { Entry entry = services.remove(service.getId()); internalUnregisterService(entry); } /** * Allocate a new builder. {@link ServiceProviderBuilder#providerStrategy} is set to {@link RoundRobinStrategy} * * @return the builder */ @Override public ServiceProviderBuilder serviceProviderBuilder() { return new ServiceProviderBuilderImpl(this) .providerStrategy(new RoundRobinStrategy()) .threadFactory(ThreadUtils.newThreadFactory("ServiceProvider")); } /** * Allocate a new service cache builder. The refresh padding is defaulted to 1 second. * * @return new cache builder */ @Override public ServiceCacheBuilder serviceCacheBuilder() { return new ServiceCacheBuilderImpl(this); } /** * Return the names of all known services * * @return list of service names * @throws Exception errors */ @Override public Collection queryForNames() throws Exception { List names = client.getChildren().forPath(basePath); return ImmutableList.copyOf(names); } /** * Return all known instances for the given service * * @param name name of the service * @return list of instances (or an empty list) * @throws Exception errors */ @Override public Collection> queryForInstances(String name) throws Exception { return queryForInstances(name, null); } /** * Return a service instance POJO * * @param name name of the service * @param id ID of the instance * @return the instance or null if not found * @throws Exception errors */ @Override public ServiceInstance queryForInstance(String name, String id) throws Exception { String path = pathForInstance(name, id); try { byte[] bytes = client.getData().forPath(path); return serializer.deserialize(bytes); } catch ( KeeperException.NoNodeException ignore ) { // ignore } return null; } void cacheOpened(ServiceCache cache) { caches.add(cache); } void cacheClosed(ServiceCache cache) { caches.remove(cache); } void providerOpened(ServiceProvider provider) { providers.add(provider); } void providerClosed(ServiceProvider cache) { providers.remove(cache); } CuratorFramework getClient() { return client; } String pathForName(String name) { return ZKPaths.makePath(basePath, name); } InstanceSerializer getSerializer() { return serializer; } List> queryForInstances(String name, Watcher watcher) throws Exception { ImmutableList.Builder> builder = ImmutableList.builder(); String path = pathForName(name); List instanceIds; if ( watcher != null ) { instanceIds = getChildrenWatched(path, watcher, true); } else { try { instanceIds = client.getChildren().forPath(path); } catch ( KeeperException.NoNodeException e ) { instanceIds = Lists.newArrayList(); } } for ( String id : instanceIds ) { ServiceInstance instance = queryForInstance(name, id); if ( instance != null ) { builder.add(instance); } } return builder.build(); } @VisibleForTesting int debugServicesQty() { return services.size(); } private List getChildrenWatched(String path, Watcher watcher, boolean recurse) throws Exception { List instanceIds; try { instanceIds = client.getChildren().usingWatcher(watcher).forPath(path); } catch ( KeeperException.NoNodeException e ) { if ( recurse ) { try { client.create().creatingParentContainersIfNeeded().forPath(path); } catch ( KeeperException.NodeExistsException ignore ) { // ignore } instanceIds = getChildrenWatched(path, watcher, false); } else { throw e; } } return instanceIds; } @VisibleForTesting String pathForInstance(String name, String id) { return ZKPaths.makePath(pathForName(name), id); } @VisibleForTesting ServiceInstance getRegisteredService(String id) { Entry entry = services.get(id); return (entry != null) ? entry.service : null; } private void reRegisterServices() throws Exception { for ( final Entry entry : services.values() ) { synchronized(entry) { internalRegisterService(entry.service); } } } private CuratorCacheBridge makeNodeCache(final ServiceInstance instance) { if ( !watchInstances ) { return null; } CuratorCacheBridge cache = CuratorCache.bridgeBuilder(client, pathForInstance(instance.getName(), instance.getId())) .withOptions(CuratorCache.Options.SINGLE_NODE_CACHE) .withDataNotCached() .build(); CuratorCacheListener listener = CuratorCacheListener.builder() .afterInitialized() .forAll((__, ___, data) -> { if ( data != null ) { try { ServiceInstance newInstance = serializer.deserialize(data.getData()); Entry entry = services.get(newInstance.getId()); if ( entry != null ) { synchronized(entry) { entry.service = newInstance; } } } catch ( Exception e ) { log.debug("Could not deserialize: " + data.getPath()); } } else { log.warn("Instance data has been deleted for: " + instance); } }) .build(); cache.listenable().addListener(listener); cache.start(); return cache; } private void internalUnregisterService(final Entry entry) throws Exception { if ( entry != null ) { synchronized(entry) { if ( entry.cache != null ) { CloseableUtils.closeQuietly(entry.cache); entry.cache = null; } String path = pathForInstance(entry.service.getName(), entry.service.getId()); try { client.delete().guaranteed().forPath(path); } catch ( KeeperException.NoNodeException ignore ) { // ignore } } } } } ServiceProviderBuilderImpl.java000066400000000000000000000073151442004423600421100ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import com.google.common.collect.Lists; import org.apache.curator.x.discovery.DownInstancePolicy; import org.apache.curator.x.discovery.InstanceFilter; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceProvider; import org.apache.curator.x.discovery.ServiceProviderBuilder; import org.apache.curator.x.discovery.strategies.RoundRobinStrategy; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; /** * Builder for service providers */ class ServiceProviderBuilderImpl implements ServiceProviderBuilder { private ServiceDiscoveryImpl discovery; private String serviceName; private ProviderStrategy providerStrategy; private ThreadFactory threadFactory; private ExecutorService executorService; private List> filters = Lists.newArrayList(); private DownInstancePolicy downInstancePolicy = new DownInstancePolicy(); public ServiceProvider build() { return new ServiceProviderImpl(discovery, serviceName, providerStrategy, threadFactory, executorService, filters, downInstancePolicy); } ServiceProviderBuilderImpl(ServiceDiscoveryImpl discovery) { this.discovery = discovery; } /** * required - set the name of the service to be provided * * @param serviceName the name of the service * @return this */ @Override public ServiceProviderBuilder serviceName(String serviceName) { this.serviceName = serviceName; return this; } /** * optional - set the provider strategy. The default is {@link RoundRobinStrategy} * * @param providerStrategy strategy to use * @return this */ @Override public ServiceProviderBuilder providerStrategy(ProviderStrategy providerStrategy) { this.providerStrategy = providerStrategy; return this; } /** * optional - the thread factory to use for creating internal threads * * @param threadFactory factory to use * @return this */ @Override @Deprecated public ServiceProviderBuilder threadFactory(ThreadFactory threadFactory) { this.threadFactory = threadFactory; this.executorService = null; return this; } @Override public ServiceProviderBuilder downInstancePolicy(DownInstancePolicy downInstancePolicy) { this.downInstancePolicy = downInstancePolicy; return this; } @Override public ServiceProviderBuilder additionalFilter(InstanceFilter filter) { filters.add(filter); return this; } @Override public ServiceProviderBuilder executorService(ExecutorService executorService) { this.executorService = executorService; this.threadFactory = null; return this; } } ServiceProviderImpl.java000066400000000000000000000111711442004423600405740ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import com.google.common.collect.Lists; import org.apache.curator.x.discovery.DownInstancePolicy; import org.apache.curator.x.discovery.InstanceFilter; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceCache; import org.apache.curator.x.discovery.ServiceCacheBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceProvider; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; /** * The main interface for Service Discovery. Encapsulates the discovery service for a particular * named service along with a provider strategy. */ public class ServiceProviderImpl implements ServiceProvider { private final ServiceCache cache; private final InstanceProvider instanceProvider; private final ServiceDiscoveryImpl discovery; private final ProviderStrategy providerStrategy; private final DownInstanceManager downInstanceManager; public ServiceProviderImpl(ServiceDiscoveryImpl discovery, String serviceName, ProviderStrategy providerStrategy, ThreadFactory threadFactory, List> filters, DownInstancePolicy downInstancePolicy) { this(discovery, serviceName, providerStrategy, threadFactory, null, filters, downInstancePolicy); } protected ServiceProviderImpl(ServiceDiscoveryImpl discovery, String serviceName, ProviderStrategy providerStrategy, ThreadFactory threadFactory, ExecutorService executorService, List> filters, DownInstancePolicy downInstancePolicy) { this.discovery = discovery; this.providerStrategy = providerStrategy; downInstanceManager = new DownInstanceManager<>(downInstancePolicy); final ServiceCacheBuilder builder = discovery.serviceCacheBuilder().name(serviceName); if (executorService != null) { builder.executorService(executorService); } else { //noinspection deprecation builder.threadFactory(threadFactory); } cache = builder.build(); ArrayList> localFilters = Lists.newArrayList(filters); localFilters.add(downInstanceManager); localFilters.add(ServiceInstance::isEnabled); instanceProvider = new FilteredInstanceProvider<>(cache, localFilters); } /** * The provider must be started before use * * @throws Exception any errors */ @Override public void start() throws Exception { cache.start(); discovery.providerOpened(this); } /** * {@inheritDoc} */ @Override public void close() throws IOException { discovery.providerClosed(this); cache.close(); } /** * Return the current available set of instances IMPORTANT: users * should not hold on to the instance returned. They should always get a fresh list. * * @return all known instances * @throws Exception any errors */ @Override public Collection> getAllInstances() throws Exception { return instanceProvider.getInstances(); } /** * Return an instance for a single use. IMPORTANT: users * should not hold on to the instance returned. They should always get a fresh instance. * * @return the instance to use * @throws Exception any errors */ @Override public ServiceInstance getInstance() throws Exception { return providerStrategy.getInstance(instanceProvider); } @Override public void noteError(ServiceInstance instance) { downInstanceManager.add(instance); } } strategies/000077500000000000000000000000001442004423600345205ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discoveryRandomStrategy.java000066400000000000000000000032321442004423600403260ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/strategies/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.strategies; import org.apache.curator.x.discovery.details.InstanceProvider; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceInstance; import java.util.List; import java.util.Random; /** * This strategy always picks a random instance from the list */ public class RandomStrategy implements ProviderStrategy { private final Random random = new Random(); @Override public ServiceInstance getInstance(InstanceProvider instanceProvider) throws Exception { List> instances = instanceProvider.getInstances(); if ( instances.size() == 0 ) { return null; } int thisIndex = random.nextInt(instances.size()); return instances.get(thisIndex); } } RoundRobinStrategy.java000066400000000000000000000033341442004423600411720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/strategies/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.strategies; import org.apache.curator.x.discovery.details.InstanceProvider; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceInstance; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * This strategy rotates sequentially through the list of instances */ public class RoundRobinStrategy implements ProviderStrategy { private final AtomicInteger index = new AtomicInteger(0); @Override public ServiceInstance getInstance(InstanceProvider instanceProvider) throws Exception { List> instances = instanceProvider.getInstances(); if ( instances.size() == 0 ) { return null; } int thisIndex = Math.abs(index.getAndIncrement()); return instances.get(thisIndex % instances.size()); } } StickyStrategy.java000066400000000000000000000066311442004423600403620ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/strategies/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.strategies; import org.apache.curator.x.discovery.ProviderStrategy; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.details.InstanceProvider; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; /** * This strategy uses a master strategy to pick the initial instance. Once picked, * that instance is always returned. If, however, the currently selected instance * is no longer in the list, the master strategy is used to pick a new instance. */ public class StickyStrategy implements ProviderStrategy { private final ProviderStrategy masterStrategy; private final AtomicReference> ourInstance = new AtomicReference>(null); private final AtomicInteger instanceNumber = new AtomicInteger(-1); /** * @param masterStrategy the strategy to use for picking the sticky instance */ public StickyStrategy(ProviderStrategy masterStrategy) { this.masterStrategy = masterStrategy; } @Override public ServiceInstance getInstance(InstanceProvider instanceProvider) throws Exception { final List> instances = instanceProvider.getInstances(); { ServiceInstance localOurInstance = ourInstance.get(); if ( !instances.contains(localOurInstance) ) { ourInstance.compareAndSet(localOurInstance, null); } } if ( ourInstance.get() == null ) { ServiceInstance instance = masterStrategy.getInstance ( new InstanceProvider() { @Override public List> getInstances() throws Exception { return instances; } } ); if ( ourInstance.compareAndSet(null, instance) ) { instanceNumber.incrementAndGet(); } } return ourInstance.get(); } /** * Each time a new instance is picked, an internal counter is incremented. This way you * can track when/if the instance changes. The instance can change when the selected instance * is not in the current list of instances returned by the instance provider * * @return instance number */ public int getInstanceNumber() { return instanceNumber.get(); } } curator-apache-curator-5.5.0/curator-x-discovery/src/site/000077500000000000000000000000001442004423600235575ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/site/confluence/000077500000000000000000000000001442004423600257005ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/site/confluence/index.confluence000066400000000000000000000143511442004423600310560ustar00rootroot00000000000000h1. Service Discovery h2. Packaging Curator Service Discovery is in its own package in Maven Central: curator\-x\-discovery h2. What Is a Discovery Service? In SOA/distributed systems, services need to find each other. i.e. a web service might need to find a caching service, etc. DNS can be used for this but it is nowhere near flexible enough for services that are constantly changing. A Service Discovery system provides a mechanism for: * Services to register their availability * Locating a single instance of a particular service * Notifying when the instances of a service change h2. Curator Service Discovery h3. ServiceInstance A service instance is represented by the class: {{ServiceInstance}}. ServiceInstances have a name, id, address, port and/or ssl port, and an optional payload (user defined). ServiceInstances are serialized and stored in ZooKeeper in the following way: {noformat} base path |_______ service A name |__________ instance 1 id --> (serialized ServiceInstance) |__________ instance 2 id --> (serialized ServiceInstance) |__________ ... |_______ service B name |__________ instance 1 id --> (serialized ServiceInstance) |__________ instance 2 id --> (serialized ServiceInstance) |__________ ... |_______ ... {noformat} h3. ServiceProvider The main abstraction class is {{ServiceProvider}}. It encapsulates the discovery service for a particular named service along with a provider strategy. A provider strategy is a scheme for selecting one instance from a set of instances for a given service. There are three bundled strategies: Round Robin, Random and Sticky (always selects the same one). ServiceProviders are allocated by using a {{ServiceProviderBuilder}}. You obtain a ServiceProviderBuilder from the ServiceDiscovery (see below). The ServiceProviderBuilder allows you to set the service name and several other optional values. The ServiceProvider must be started by calling {{start()}}. When finished you should call {{close()}}. The only method in ServiceProvider is: {code} public ServiceInstance getInstance() throws Exception Return an instance for a single use. IMPORTANT: users should not hold on to the instance returned. A fresh instance should always be retrieved. Returns: the instance to use {code} *Note:* When using Curator 2.x (Zookeeper 3.4.x) it's essential that service provider objects are cached by your application and reused. Since the internal NamespaceWatcher objects added by the service provider cannot be removed in Zookeeper 3.4.x, creating a fresh service provider for each call to the same service will eventually exhaust the memory of the JVM. h3. ServiceDiscovery In order to allocate a ServiceProvider, you must have a ServiceDiscovery. It is created by a {{ServiceDiscoveryBuilder}}. You must call {{start()}} on the object and, when done with it, call {{close()}}. h3. Instance Stability If a particular instance has an I/O error, etc. you should call {{ServiceProvider.noteError()}} passing in the instance. The ServiceProvider will temporarily consider instances that have errors to be "down". The thresholds and timeouts for down instances are set via the {{DownInstancePolicy}} which can be passed to {{ServiceProviderBuilder}} (note: a default DownInstancePolicy is used if you don't specify one). ---- h3. Low Level APIs The ServiceProvider API is all you should need for most purposes. However, for finer grained control, you can use these methods: *Registering/Unregistering Services* Normally, you pass your application's service descriptor to the ServiceDiscovery constructor and it will get registered/unregistered automatically. If, though, you need to manually do this, use these methods: {code} public void registerService(ServiceInstance service) throws Exception Register/re-register/update a service instance Parameters: service - service to add {code} {code} public void unregisterService(ServiceInstance service) throws Exception Unregister/remove a service instance Parameters: service - the service {code} *Querying for Services* You can query for all service names, all instances of a particular service, or single service instance. {code} public Collection queryForNames() throws Exception Return the names of all known services Returns: list of service names {code} {code} public Collection> queryForInstances(String name) throws Exception Return all known instances for the given service Parameters: name - name of the service Returns: list of instances (or an empty list) {code} {code} public ServiceInstance queryForInstance(String name, String id) throws Exception Return a service instance POJO Parameters: name - name of the service id - ID of the instance Returns: the instance or null if not found {code} *Service Cache* Each of the above query methods calls ZooKeeper directly. If you need more than occasional querying of services you can use the {{ServiceCache}}. It caches in memory the list of instances for a particular service. It uses a Watcher to keep the list up to date. You allocate a ServiceCache via the builder returned by {{ServiceDiscovery.serviceCacheBuilder()}}. The ServiceCache object must be started by calling {{start()}} and, when done, you should call {{close()}}. You can get the currently known list of instances for the service by calling: {code} public Collection> getInstances() Return the current list of instances. NOTE: there is no guarantee of freshness. This is merely the last known list of instances. However, the list is updated via a ZooKeeper watcher so it should be fresh within a window of a second or two. {code} ServiceCache supports a listener that gets notified when Watcher has updated the list of instances: {code} /** * Listener for changes to a service cache */ public interface ServiceCacheListener extends ConnectionStateListener { /** * Called when the cache has changed (instances added/deleted, etc.) */ public void cacheChanged(); } {code} curator-apache-curator-5.5.0/curator-x-discovery/src/site/site.xml000066400000000000000000000026471442004423600252560ustar00rootroot00000000000000 ]]> curator-apache-curator-5.5.0/curator-x-discovery/src/test/000077500000000000000000000000001442004423600235725ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/000077500000000000000000000000001442004423600245135ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/000077500000000000000000000000001442004423600253025ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/000077500000000000000000000000001442004423600265235ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/000077500000000000000000000000001442004423600302025ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/000077500000000000000000000000001442004423600304515ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/000077500000000000000000000000001442004423600324605ustar00rootroot00000000000000ServiceCacheLeakTester.java000066400000000000000000000057671442004423600375730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.TestingServer; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.discovery.strategies.RandomStrategy; import org.junit.jupiter.api.Tag; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class ServiceCacheLeakTester { public static void main(String[] args) throws Exception { TestingServer testingServer = new TestingServer(); final CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(testingServer.getConnectString(), new RetryOneTime(1)); try { curatorFramework.start(); doWork(curatorFramework); System.gc(); System.out.println("Done - get dump"); Thread.currentThread().join(); } finally { CloseableUtils.closeQuietly(curatorFramework); CloseableUtils.closeQuietly(testingServer); } } private static void doWork(CuratorFramework curatorFramework) throws Exception { ServiceInstance thisInstance = ServiceInstance.builder().name("myservice").build(); final ServiceDiscovery serviceDiscovery = ServiceDiscoveryBuilder.builder(Void.class).client(curatorFramework.usingNamespace("dev")).basePath("/instances").thisInstance(thisInstance).build(); serviceDiscovery.start(); for ( int i = 0; i < 100000; i++ ) { final ServiceProvider s = serviceProvider(serviceDiscovery, "myservice"); s.start(); try { s.getInstance().buildUriSpec(); } finally { s.close(); } } } private static ServiceProvider serviceProvider(ServiceDiscovery serviceDiscovery, String name) throws Exception { return serviceDiscovery.serviceProviderBuilder().serviceName(name).providerStrategy(new RandomStrategy()).build(); } }TestJsonInstanceSerializer.java000066400000000000000000000205351442004423600405410ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.curator.x.discovery.details.JsonInstanceSerializer; import org.junit.jupiter.api.Test; public class TestJsonInstanceSerializer { @Test public void testBasic() throws Exception { JsonInstanceSerializer serializer = new JsonInstanceSerializer(String.class); ServiceInstance instance = new ServiceInstance("name", "id", "address", 10, 20, "payload", 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); byte[] bytes = serializer.serialize(instance); ServiceInstance rhs = serializer.deserialize(bytes); assertEquals(instance, rhs); assertEquals(instance.getId(), rhs.getId()); assertEquals(instance.getName(), rhs.getName()); assertEquals(instance.getPayload(), rhs.getPayload()); assertEquals(instance.getAddress(), rhs.getAddress()); assertEquals(instance.getPort(), rhs.getPort()); assertEquals(instance.getSslPort(), rhs.getSslPort()); assertEquals(instance.getUriSpec(), rhs.getUriSpec()); assertEquals(instance.isEnabled(), rhs.isEnabled()); } @Test public void testWrongPayloadType() throws Exception { JsonInstanceSerializer stringSerializer = new JsonInstanceSerializer(String.class); JsonInstanceSerializer doubleSerializer = new JsonInstanceSerializer(Double.class); byte[] bytes = stringSerializer.serialize(new ServiceInstance("name", "id", "address", 10, 20, "payload", 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true)); try { doubleSerializer.deserialize(bytes); fail(); } catch ( ClassCastException e ) { // correct } } @Test public void testNoPayload() throws Exception { JsonInstanceSerializer serializer = new JsonInstanceSerializer(Void.class); ServiceInstance instance = new ServiceInstance("name", "id", "address", 10, 20, null, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); byte[] bytes = serializer.serialize(instance); ServiceInstance rhs = serializer.deserialize(bytes); assertEquals(instance, rhs); assertEquals(instance.getId(), rhs.getId()); assertEquals(instance.getName(), rhs.getName()); assertEquals(instance.getPayload(), rhs.getPayload()); assertEquals(instance.getAddress(), rhs.getAddress()); assertEquals(instance.getPort(), rhs.getPort()); assertEquals(instance.getSslPort(), rhs.getSslPort()); assertEquals(instance.getUriSpec(), rhs.getUriSpec()); assertEquals(instance.isEnabled(), rhs.isEnabled()); } @Test public void testNoEnabledState() throws Exception { JsonInstanceSerializer serializer = new JsonInstanceSerializer(Void.class); byte[] bytes = "{}".getBytes("utf-8"); ServiceInstance instance = serializer.deserialize(bytes); assertTrue(instance.isEnabled(), "Instance that has no 'enabled' should be assumed enabled"); } @Test public void testPayloadAsList() throws Exception { JsonInstanceSerializer serializer = new JsonInstanceSerializer(Object.class, false); List payload = new ArrayList(); payload.add("Test value 1"); payload.add("Test value 2"); ServiceInstance instance = new ServiceInstance("name", "id", "address", 10, 20, payload, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false); byte[] bytes = serializer.serialize(instance); ServiceInstance rhs = serializer.deserialize(bytes); assertEquals(instance, rhs); assertEquals(instance.getId(), rhs.getId()); assertEquals(instance.getName(), rhs.getName()); assertEquals(instance.getPayload(), rhs.getPayload()); assertEquals(instance.getAddress(), rhs.getAddress()); assertEquals(instance.getPort(), rhs.getPort()); assertEquals(instance.getSslPort(), rhs.getSslPort()); assertEquals(instance.getUriSpec(), rhs.getUriSpec()); assertEquals(instance.isEnabled(), rhs.isEnabled()); } @Test public void testPayloadAsMap() throws Exception { JsonInstanceSerializer serializer = new JsonInstanceSerializer(Object.class, false); Map payload = new HashMap(); payload.put("1", "Test value 1"); payload.put("2", "Test value 2"); ServiceInstance instance = new ServiceInstance("name", "id", "address", 10, 20, payload, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false); byte[] bytes = serializer.serialize(instance); ServiceInstance rhs = serializer.deserialize(bytes); assertEquals(instance, rhs); assertEquals(instance.getId(), rhs.getId()); assertEquals(instance.getName(), rhs.getName()); assertEquals(instance.getPayload(), rhs.getPayload()); assertEquals(instance.getAddress(), rhs.getAddress()); assertEquals(instance.getPort(), rhs.getPort()); assertEquals(instance.getSslPort(), rhs.getSslPort()); assertEquals(instance.getUriSpec(), rhs.getUriSpec()); assertEquals(instance.isEnabled(), rhs.isEnabled()); } @Test public void testPayloadClass() throws Exception { JsonInstanceSerializer serializer = new JsonInstanceSerializer(Payload.class); Payload payload = new Payload(); payload.setVal("Test value"); ServiceInstance instance = new ServiceInstance("name", "id", "address", 10, 20, payload, 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); byte[] bytes = serializer.serialize(instance); ServiceInstance rhs = serializer.deserialize(bytes); assertEquals(instance, rhs); assertEquals(instance.getId(), rhs.getId()); assertEquals(instance.getName(), rhs.getName()); assertEquals(instance.getPayload(), rhs.getPayload()); assertEquals(instance.getAddress(), rhs.getAddress()); assertEquals(instance.getPort(), rhs.getPort()); assertEquals(instance.getSslPort(), rhs.getSslPort()); assertEquals(instance.getUriSpec(), rhs.getUriSpec()); assertEquals(instance.isEnabled(), rhs.isEnabled()); } public static class Payload { private String val; public String getVal() { return val; } public Payload() { } public Payload(String val) { this.val = val; } public void setVal(String val) { this.val = val; } @Override public boolean equals(Object other) { if (other == null || !(other instanceof Payload)) return false; String otherVal = ((Payload)other).getVal(); if (val == null) return val == otherVal; return val.equals(otherVal); } } } TestLocalIpFilter.java000066400000000000000000000042651442004423600366040ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.junit.jupiter.api.Test; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.List; public class TestLocalIpFilter { @Test public void testFilterEverything() throws SocketException { LocalIpFilter localIpFilter = ServiceInstanceBuilder.getLocalIpFilter(); try { ServiceInstanceBuilder.setLocalIpFilter ( new LocalIpFilter() { @Override public boolean use(NetworkInterface networkInterface, InetAddress address) throws SocketException { return false; } } ); List allLocalIPs = Lists.newArrayList(ServiceInstanceBuilder.getAllLocalIPs()); assertEquals(allLocalIPs.size(), 0); } finally { ServiceInstanceBuilder.setLocalIpFilter(localIpFilter); } List allLocalIPs = Lists.newArrayList(ServiceInstanceBuilder.getAllLocalIPs()); assertTrue(allLocalIPs.size() > 0); } } TestServiceCache.java000066400000000000000000000304351442004423600364350ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.ExecuteCalledWatchingExecutorService; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.utils.Compatibility; import org.apache.curator.x.discovery.details.ServiceCacheListener; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.io.Closeable; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestServiceCache extends BaseClassForTests { @Test public void testInitialLoad() throws Exception { List closeables = Lists.newArrayList(); try { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); closeables.add(client); client.start(); ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build(); closeables.add(discovery); discovery.start(); ServiceCache cache = discovery.serviceCacheBuilder().name("test").build(); closeables.add(cache); final CountDownLatch latch = new CountDownLatch(3); ServiceCacheListener listener = new ServiceCacheListener() { @Override public void cacheChanged() { latch.countDown(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; cache.addListener(listener); cache.start(); ServiceInstance instance1 = ServiceInstance.builder().payload("test").name("test").port(10064).build(); ServiceInstance instance2 = ServiceInstance.builder().payload("test").name("test").port(10065).build(); ServiceInstance instance3 = ServiceInstance.builder().payload("test").name("test").port(10066).build(); discovery.registerService(instance1); discovery.registerService(instance2); discovery.registerService(instance3); assertTrue(latch.await(10, TimeUnit.SECONDS)); ServiceCache cache2 = discovery.serviceCacheBuilder().name("test").build(); closeables.add(cache2); cache2.start(); assertEquals(cache2.getInstances().size(), 3); } finally { Collections.reverse(closeables); for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } @Test public void testViaProvider() throws Exception { Timing timing = new Timing(); List closeables = Lists.newArrayList(); try { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); closeables.add(client); client.start(); ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build(); closeables.add(discovery); discovery.start(); ServiceProvider serviceProvider = discovery.serviceProviderBuilder().serviceName("test").build(); closeables.add(serviceProvider); serviceProvider.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); discovery.registerService(instance); int count = 0; ServiceInstance foundInstance = null; while ( foundInstance == null ) { assertTrue(count++ < 5); foundInstance = serviceProvider.getInstance(); timing.sleepABit(); } assertEquals(foundInstance, instance); ServiceInstance instance2 = ServiceInstance.builder().address("foo").payload("thing").name("test").port(10064).build(); discovery.registerService(instance2); timing.sleepABit(); Collection> allInstances = serviceProvider.getAllInstances(); assertEquals(allInstances.size(), 2); } finally { Collections.reverse(closeables); for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } @Test public void testUpdate() throws Exception { List closeables = Lists.newArrayList(); try { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); closeables.add(client); client.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build(); closeables.add(discovery); discovery.start(); final CountDownLatch latch = new CountDownLatch(1); ServiceCache cache = discovery.serviceCacheBuilder().name("test").build(); closeables.add(cache); ServiceCacheListener listener = new ServiceCacheListener() { @Override public void cacheChanged() { latch.countDown(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; cache.addListener(listener); cache.start(); instance = ServiceInstance.builder().payload("changed").name("test").port(10064).id(instance.getId()).build(); discovery.updateService(instance); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertEquals(cache.getInstances().size(), 1); assertEquals(cache.getInstances().get(0).getPayload(), instance.getPayload()); } finally { Collections.reverse(closeables); for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } @Test public void testCache() throws Exception { List closeables = Lists.newArrayList(); try { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); closeables.add(client); client.start(); ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build(); closeables.add(discovery); discovery.start(); ServiceCache cache = discovery.serviceCacheBuilder().name("test").build(); closeables.add(cache); cache.start(); final Semaphore semaphore = new Semaphore(0); ServiceCacheListener listener = new ServiceCacheListener() { @Override public void cacheChanged() { semaphore.release(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; cache.addListener(listener); ServiceInstance instance1 = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); ServiceInstance instance2 = ServiceInstance.builder().payload("thing").name("test").port(10065).build(); discovery.registerService(instance1); assertTrue(semaphore.tryAcquire(10, TimeUnit.SECONDS)); discovery.registerService(instance2); assertTrue(semaphore.tryAcquire(3, TimeUnit.SECONDS)); ServiceInstance instance3 = ServiceInstance.builder().payload("thing").name("another").port(10064).build(); discovery.registerService(instance3); assertFalse(semaphore.tryAcquire(3, TimeUnit.SECONDS)); // should not get called for a different service } finally { Collections.reverse(closeables); for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } @Test public void testExecutorServiceIsInvoked() throws Exception { if ( Compatibility.hasPersistentWatchers() ) { return; // for ZK 3.6 the underlying cache ignores the executor } List closeables = Lists.newArrayList(); try { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); closeables.add(client); client.start(); ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build(); closeables.add(discovery); discovery.start(); ExecuteCalledWatchingExecutorService exec = new ExecuteCalledWatchingExecutorService(Executors.newSingleThreadExecutor()); assertFalse(exec.isExecuteCalled()); ServiceCache cache = discovery.serviceCacheBuilder().name("test").executorService(exec).build(); closeables.add(cache); cache.start(); final Semaphore semaphore = new Semaphore(0); ServiceCacheListener listener = new ServiceCacheListener() { @Override public void cacheChanged() { semaphore.release(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { } }; cache.addListener(listener); ServiceInstance instance1 = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); discovery.registerService(instance1); assertTrue(semaphore.tryAcquire(10, TimeUnit.SECONDS)); assertTrue(exec.isExecuteCalled()); } finally { Collections.reverse(closeables); for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } } TestStrategies.java000066400000000000000000000120451442004423600362200ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.apache.curator.x.discovery.details.InstanceProvider; import org.apache.curator.x.discovery.strategies.RandomStrategy; import org.apache.curator.x.discovery.strategies.RoundRobinStrategy; import org.apache.curator.x.discovery.strategies.StickyStrategy; import org.apache.commons.math.stat.descriptive.SummaryStatistics; import org.junit.jupiter.api.Test; import java.util.List; public class TestStrategies { private static class TestInstanceProvider implements InstanceProvider { private final List> instances; private TestInstanceProvider(int qty) throws Exception { this(qty, 0); } private TestInstanceProvider(int qty, int startingAt) throws Exception { instances = Lists.newArrayList(); for ( int i = 0; i < qty; ++i ) { ServiceInstanceBuilder builder = ServiceInstance.builder(); instances.add(builder.id(Integer.toString(i + startingAt)).name("foo").build()); } } @Override public List> getInstances() throws Exception { return instances; } } @Test public void testRandom() throws Exception { final int QTY = 10; final int ITERATIONS = 1000; TestInstanceProvider instanceProvider = new TestInstanceProvider(QTY, 0); ProviderStrategy strategy = new RandomStrategy(); long[] counts = new long[QTY]; for ( int i = 0; i < ITERATIONS; ++i ) { ServiceInstance instance = strategy.getInstance(instanceProvider); int id = Integer.parseInt(instance.getId()); counts[id]++; } SummaryStatistics statistic = new SummaryStatistics(); for ( int i = 0; i < QTY; ++i ) { statistic.addValue(counts[i]); } assertTrue(statistic.getStandardDeviation() <= (QTY * 2), "" + statistic.getStandardDeviation()); // meager check for even distribution } @Test public void testRoundRobin() throws Exception { final int QTY = 10; TestInstanceProvider instanceProvider = new TestInstanceProvider(QTY); ProviderStrategy strategy = new RoundRobinStrategy(); for ( int i = 0; i < QTY; ++i ) { ServiceInstance instance = strategy.getInstance(instanceProvider); assertEquals(instance.getId(), Integer.toString(i)); } for ( int i = 0; i < (1234 * QTY); ++i ) { ServiceInstance instance = strategy.getInstance(instanceProvider); assertEquals(instance.getId(), Integer.toString(i % QTY)); } } @Test public void testSticky() throws Exception { final int QTY = 10; TestInstanceProvider instanceProvider = new TestInstanceProvider(QTY); StickyStrategy strategy = new StickyStrategy(new RandomStrategy()); ServiceInstance theInstance = strategy.getInstance(instanceProvider); int instanceNumber = strategy.getInstanceNumber(); for ( int i = 0; i < 1000; ++i ) { assertEquals(strategy.getInstance(instanceProvider), theInstance); } // assert what happens when an instance goes down instanceProvider = new TestInstanceProvider(QTY, QTY); assertFalse(strategy.getInstance(instanceProvider).equals(theInstance)); assertFalse(instanceNumber == strategy.getInstanceNumber()); theInstance = strategy.getInstance(instanceProvider); for ( int i = 0; i < 1000; ++i ) { assertEquals(strategy.getInstance(instanceProvider), theInstance); } } } TestUriSpec.java000066400000000000000000000074471442004423600354720ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery; import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.common.collect.Maps; import org.junit.jupiter.api.Test; import java.util.Iterator; import java.util.Map; public class TestUriSpec { @Test public void testScheme() { UriSpec spec = new UriSpec("{scheme}://foo.com"); ServiceInstanceBuilder builder = new ServiceInstanceBuilder(); builder.id("x"); builder.name("foo"); builder.port(5); ServiceInstance instance = builder.build(); assertEquals(spec.build(instance), "http://foo.com"); builder.sslPort(5); instance = builder.build(); assertEquals(spec.build(instance), "https://foo.com"); } @Test public void testFromInstance() { ServiceInstanceBuilder builder = new ServiceInstanceBuilder(); builder.address("1.2.3.4"); builder.name("foo"); builder.id("bar"); builder.port(5); builder.sslPort(6); builder.registrationTimeUTC(789); builder.serviceType(ServiceType.PERMANENT); ServiceInstance instance = builder.build(); UriSpec spec = new UriSpec("{scheme}://{address}:{port}:{ssl-port}/{name}/{id}/{registration-time-utc}/{service-type}"); Map m = Maps.newHashMap(); m.put("scheme", "test"); assertEquals(spec.build(instance, m), "test://1.2.3.4:5:6/foo/bar/789/permanent"); } @Test public void testEscapes() { UriSpec spec = new UriSpec("{one}two-three-{[}four{]}-five{six}"); Iterator iterator = spec.iterator(); checkPart(iterator.next(), "one", true); checkPart(iterator.next(), "two-three-", false); checkPart(iterator.next(), "[", true); checkPart(iterator.next(), "four", false); checkPart(iterator.next(), "]", true); checkPart(iterator.next(), "-five", false); checkPart(iterator.next(), "six", true); Map m = Maps.newHashMap(); m.put("one", 1); m.put("six", 6); assertEquals(spec.build(m), "1two-three-{four}-five6"); } @Test public void testBasic() { UriSpec spec = new UriSpec("{one}{two}three-four-five{six}seven{eight}"); Iterator iterator = spec.iterator(); checkPart(iterator.next(), "one", true); checkPart(iterator.next(), "two", true); checkPart(iterator.next(), "three-four-five", false); checkPart(iterator.next(), "six", true); checkPart(iterator.next(), "seven", false); checkPart(iterator.next(), "eight", true); } private void checkPart(UriSpec.Part p, String value, boolean isVariable) { assertEquals(p.getValue(), value); assertEquals(p.isVariable(), isVariable); } } details/000077500000000000000000000000001442004423600340265ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discoveryTestDownInstanceManager.java000066400000000000000000000071401442004423600414220ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.x.discovery.DownInstancePolicy; import org.apache.curator.x.discovery.ServiceInstance; import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; public class TestDownInstanceManager { private static final DownInstancePolicy debugDownInstancePolicy = new DownInstancePolicy(1, TimeUnit.SECONDS, 1); private static final DownInstancePolicy debugMultiDownInstancePolicy = new DownInstancePolicy(1, TimeUnit.SECONDS, 2); @Test public void testBasic() throws Exception { ServiceInstance instance1 = ServiceInstance.builder().name("hey").id("1").build(); ServiceInstance instance2 = ServiceInstance.builder().name("hey").id("2").build(); DownInstanceManager downInstanceManager = new DownInstanceManager(debugDownInstancePolicy); assertTrue(downInstanceManager.apply(instance1)); assertTrue(downInstanceManager.apply(instance2)); downInstanceManager.add(instance1); assertFalse(downInstanceManager.apply(instance1)); assertTrue(downInstanceManager.apply(instance2)); } @Test public void testThreshold() throws Exception { ServiceInstance instance1 = ServiceInstance.builder().name("hey").id("1").build(); ServiceInstance instance2 = ServiceInstance.builder().name("hey").id("2").build(); DownInstanceManager downInstanceManager = new DownInstanceManager(debugMultiDownInstancePolicy); assertTrue(downInstanceManager.apply(instance1)); assertTrue(downInstanceManager.apply(instance2)); downInstanceManager.add(instance1); assertTrue(downInstanceManager.apply(instance1)); assertTrue(downInstanceManager.apply(instance2)); downInstanceManager.add(instance1); assertFalse(downInstanceManager.apply(instance1)); assertTrue(downInstanceManager.apply(instance2)); } @Test public void testExpiration() throws Exception { ServiceInstance instance1 = ServiceInstance.builder().name("hey").id("1").build(); ServiceInstance instance2 = ServiceInstance.builder().name("hey").id("2").build(); DownInstanceManager downInstanceManager = new DownInstanceManager(debugDownInstancePolicy); downInstanceManager.add(instance1); assertFalse(downInstanceManager.apply(instance1)); assertTrue(downInstanceManager.apply(instance2)); Thread.sleep(debugDownInstancePolicy.getTimeoutMs()); assertTrue(downInstanceManager.apply(instance1)); assertTrue(downInstanceManager.apply(instance2)); } } TestJsonInstanceSerializerCompatibility.java000066400000000000000000000150461442004423600447210ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceType; import org.apache.curator.x.discovery.TestJsonInstanceSerializer; import org.apache.curator.x.discovery.UriSpec; import org.junit.jupiter.api.Test; import java.net.URI; import java.util.Date; public class TestJsonInstanceSerializerCompatibility { @Test public void testCompatibilityMode() throws Exception { JsonInstanceSerializer serializer = new JsonInstanceSerializer(TestJsonInstanceSerializer.Payload.class, true, true); ServiceInstance instance = new ServiceInstance("name", "id", "address", 10, 20, new TestJsonInstanceSerializer.Payload("test"), 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), true); byte[] bytes = serializer.serialize(instance); OldServiceInstance oldInstance = new OldServiceInstance("name", "id", "address", 10, 20, new TestJsonInstanceSerializer.Payload("test"), 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}")); ObjectMapper mapper = new ObjectMapper(); byte[] oldBytes = mapper.writeValueAsBytes(oldInstance); assertArrayEquals(bytes, oldBytes, String.format("%s vs %s", new String(bytes), new String(oldBytes))); } @Test public void testBackwardCompatibility() throws Exception { JsonInstanceSerializer serializer = new JsonInstanceSerializer(TestJsonInstanceSerializer.Payload.class, true, true); ServiceInstance instance = new ServiceInstance("name", "id", "address", 10, 20, new TestJsonInstanceSerializer.Payload("test"), 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false); byte[] bytes = serializer.serialize(instance); instance = serializer.deserialize(bytes); assertTrue(instance.isEnabled()); // passed false for enabled in the ctor but that is lost with compatibleSerializationMode ObjectMapper mapper = new ObjectMapper(); JavaType type = mapper.getTypeFactory().constructType(OldServiceInstance.class); OldServiceInstance rawServiceInstance = mapper.readValue(bytes, type); TestJsonInstanceSerializer.Payload.class.cast(rawServiceInstance.getPayload()); // just to verify that it's the correct type //noinspection unchecked OldServiceInstance check = (OldServiceInstance)rawServiceInstance; assertEquals(check.getName(), instance.getName()); assertEquals(check.getId(), instance.getId()); assertEquals(check.getAddress(), instance.getAddress()); assertEquals(check.getPort(), instance.getPort()); assertEquals(check.getSslPort(), instance.getSslPort()); assertEquals(check.getPayload(), instance.getPayload()); assertEquals(check.getRegistrationTimeUTC(), instance.getRegistrationTimeUTC()); assertEquals(check.getServiceType(), instance.getServiceType()); assertEquals(check.getUriSpec(), instance.getUriSpec()); } @Test public void testForwardCompatibility() throws Exception { OldServiceInstance oldInstance = new OldServiceInstance("name", "id", "address", 10, 20, new TestJsonInstanceSerializer.Payload("test"), 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}")); ObjectMapper mapper = new ObjectMapper(); byte[] oldJson = mapper.writeValueAsBytes(oldInstance); JsonInstanceSerializer serializer = new JsonInstanceSerializer(TestJsonInstanceSerializer.Payload.class); ServiceInstance instance = serializer.deserialize(oldJson); assertEquals(oldInstance.getName(), instance.getName()); assertEquals(oldInstance.getId(), instance.getId()); assertEquals(oldInstance.getAddress(), instance.getAddress()); assertEquals(oldInstance.getPort(), instance.getPort()); assertEquals(oldInstance.getSslPort(), instance.getSslPort()); assertEquals(oldInstance.getPayload(), instance.getPayload()); assertEquals(oldInstance.getRegistrationTimeUTC(), instance.getRegistrationTimeUTC()); assertEquals(oldInstance.getServiceType(), instance.getServiceType()); assertEquals(oldInstance.getUriSpec(), instance.getUriSpec()); assertTrue(instance.isEnabled()); } @Test public void testFutureChanges() throws Exception { TestNewServiceInstance newInstance = new TestNewServiceInstance("name", "id", "address", 10, 20, "hey", 0, ServiceType.DYNAMIC, new UriSpec("{a}/b/{c}"), false, "what", 10101L, new Date(), new URI("http://hey")); byte[] newInstanceBytes = new ObjectMapper().writeValueAsBytes(newInstance); JsonInstanceSerializer serializer = new JsonInstanceSerializer(String.class); ServiceInstance instance = serializer.deserialize(newInstanceBytes); assertEquals(instance.getName(), "name"); assertEquals(instance.getPayload(), "hey"); assertEquals(instance.isEnabled(), false); } } TestNewServiceInstance.java000066400000000000000000000071231442004423600412730ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.google.common.base.Preconditions; import org.apache.curator.x.discovery.ServiceType; import org.apache.curator.x.discovery.UriSpec; import java.net.URI; import java.util.Date; class TestNewServiceInstance { private final String name; private final String id; private final String address; private final Integer port; private final Integer sslPort; private final T payload; private final long registrationTimeUTC; private final ServiceType serviceType; private final UriSpec uriSpec; private final boolean enabled; private final String new1; private final Long new2; private final Date new3; private final URI new4; public TestNewServiceInstance(String name, String id, String address, Integer port, Integer sslPort, T payload, long registrationTimeUTC, ServiceType serviceType, UriSpec uriSpec, boolean enabled, String new1, Long new2, Date new3, URI new4) { name = Preconditions.checkNotNull(name, "name cannot be null"); id = Preconditions.checkNotNull(id, "id cannot be null"); this.new1 = new1; this.new2 = new2; this.new3 = new3; this.new4 = new4; this.serviceType = serviceType; this.uriSpec = uriSpec; this.name = name; this.id = id; this.address = address; this.port = port; this.sslPort = sslPort; this.payload = payload; this.registrationTimeUTC = registrationTimeUTC; this.enabled = enabled; } /** * Inits to default values. Only exists for deserialization */ TestNewServiceInstance() { this("", "", null, null, null, null, 0, ServiceType.DYNAMIC, null, true, null, null, null, null); } public String getName() { return name; } public String getId() { return id; } public String getAddress() { return address; } public Integer getPort() { return port; } public Integer getSslPort() { return sslPort; } @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, defaultImpl = Object.class) public T getPayload() { return payload; } public long getRegistrationTimeUTC() { return registrationTimeUTC; } public ServiceType getServiceType() { return serviceType; } public UriSpec getUriSpec() { return uriSpec; } public boolean isEnabled() { return enabled; } public String getNew1() { return new1; } public Long getNew2() { return new2; } public Date getNew3() { return new3; } public URI getNew4() { return new4; } } TestServiceCacheRace.java000066400000000000000000000133501442004423600406520ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.state.ConnectionState; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableExecutorService; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.discovery.ServiceCache; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestServiceCacheRace extends BaseClassForTests { private final Timing timing = new Timing(); // validates CURATOR-452 which exposed a race in ServiceCacheImpl's start() method caused by an optimization whereby it clears the dataBytes of its internal PathChildrenCache @Test public void testRaceOnInitialLoad() throws Exception { List closeables = Lists.newArrayList(); try { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); closeables.add(client); client.start(); ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/discovery").client(client).build(); closeables.add(discovery); discovery.start(); CountDownLatch cacheStartLatch = new CountDownLatch(1); CountDownLatch cacheWaitLatch = new CountDownLatch(1); final ServiceCache cache = discovery.serviceCacheBuilder().name("test").build(); closeables.add(cache); ((ServiceCacheImpl)cache).debugStartLatch = cacheStartLatch; // causes ServiceCacheImpl.start to notify just after starting its internal PathChildrenCache ((ServiceCacheImpl)cache).debugStartWaitLatch = cacheWaitLatch; // causes ServiceCacheImpl.start to wait before iterating over its internal PathChildrenCache ServiceInstance instance1 = ServiceInstance.builder().payload("test").name("test").port(10064).build(); discovery.registerService(instance1); CloseableExecutorService closeableExecutorService = new CloseableExecutorService(Executors.newSingleThreadExecutor()); closeables.add(closeableExecutorService); final CountDownLatch startCompletedLatch = new CountDownLatch(1); Runnable proc = new Runnable() { @Override public void run() { try { cache.start(); startCompletedLatch.countDown(); } catch ( Exception e ) { LoggerFactory.getLogger(getClass()).error("Start failed", e); throw new RuntimeException(e); } } }; closeableExecutorService.submit(proc); assertTrue(timing.awaitLatch(cacheStartLatch)); // wait until ServiceCacheImpl's internal PathChildrenCache is started and primed final CountDownLatch cacheChangedLatch = new CountDownLatch(1); ServiceCacheListener listener = new ServiceCacheListener() { @Override public void cacheChanged() { cacheChangedLatch.countDown(); } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { // NOP } }; cache.addListener(listener); ServiceInstance instance2 = ServiceInstance.builder().payload("test").name("test").port(10065).build(); discovery.registerService(instance2); // cause ServiceCacheImpl's internal PathChildrenCache listener to get called which will clear the dataBytes assertTrue(timing.awaitLatch(cacheChangedLatch)); cacheWaitLatch.countDown(); assertTrue(timing.awaitLatch(startCompletedLatch)); } finally { Collections.reverse(closeables); for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } } TestServiceDiscovery.java000066400000000000000000000342161442004423600410270ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestServiceDiscovery extends BaseClassForTests { private static final Comparator> comparator = new Comparator>() { @Override public int compare(ServiceInstance o1, ServiceInstance o2) { return o1.getId().compareTo(o2.getId()); } }; @Test public void testCrashedServerMultiInstances() throws Exception { CuratorFramework client = null; ServiceDiscovery discovery = null; try { Timing timing = new Timing(); client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); final Semaphore semaphore = new Semaphore(0); ServiceInstance instance1 = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); ServiceInstance instance2 = ServiceInstance.builder().payload("thing").name("test").port(10065).build(); discovery = new ServiceDiscoveryImpl(client, "/test", new JsonInstanceSerializer(String.class), instance1, false) { @Override protected void internalRegisterService(ServiceInstance service) throws Exception { super.internalRegisterService(service); semaphore.release(); } }; discovery.start(); discovery.registerService(instance2); timing.acquireSemaphore(semaphore, 2); assertEquals(discovery.queryForInstances("test").size(), 2); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); server.stop(); server.restart(); timing.acquireSemaphore(semaphore, 2); assertEquals(discovery.queryForInstances("test").size(), 2); } finally { CloseableUtils.closeQuietly(discovery); CloseableUtils.closeQuietly(client); } } @Test public void testCrashedServer() throws Exception { CuratorFramework client = null; ServiceDiscovery discovery = null; try { Timing timing = new Timing(); client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); final Semaphore semaphore = new Semaphore(0); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); discovery = new ServiceDiscoveryImpl(client, "/test", new JsonInstanceSerializer(String.class), instance, false) { @Override protected void internalRegisterService(ServiceInstance service) throws Exception { super.internalRegisterService(service); semaphore.release(); } }; discovery.start(); timing.acquireSemaphore(semaphore); assertEquals(discovery.queryForInstances("test").size(), 1); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); server.stop(); server.restart(); timing.acquireSemaphore(semaphore); assertEquals(discovery.queryForInstances("test").size(), 1); } finally { CloseableUtils.closeQuietly(discovery); CloseableUtils.closeQuietly(client); } } @Test public void testCrashedInstance() throws Exception { CuratorFramework client = null; ServiceDiscovery discovery = null; try { Timing timing = new Timing(); client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); client.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); discovery = new ServiceDiscoveryImpl(client, "/test", new JsonInstanceSerializer(String.class), instance, false); discovery.start(); assertEquals(discovery.queryForInstances("test").size(), 1); client.getZookeeperClient().getZooKeeper().getTestable().injectSessionExpiration(); Thread.sleep(timing.multiple(1.5).session()); assertEquals(discovery.queryForInstances("test").size(), 1); } finally { CloseableUtils.closeQuietly(discovery); CloseableUtils.closeQuietly(client); } } @Test public void testMultipleInstances() throws Exception { final String SERVICE_ONE = "one"; final String SERVICE_TWO = "two"; CuratorFramework client = null; ServiceDiscovery discovery = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); ServiceInstance s1_i1 = ServiceInstance.builder().name(SERVICE_ONE).build(); ServiceInstance s1_i2 = ServiceInstance.builder().name(SERVICE_ONE).build(); ServiceInstance s2_i1 = ServiceInstance.builder().name(SERVICE_TWO).build(); ServiceInstance s2_i2 = ServiceInstance.builder().name(SERVICE_TWO).build(); discovery = ServiceDiscoveryBuilder.builder(Void.class).client(client).basePath("/test").build(); discovery.start(); discovery.registerService(s1_i1); discovery.registerService(s1_i2); discovery.registerService(s2_i1); discovery.registerService(s2_i2); assertEquals(Sets.newHashSet(discovery.queryForNames()), Sets.newHashSet(SERVICE_ONE, SERVICE_TWO)); List> list = Lists.newArrayList(); list.add(s1_i1); list.add(s1_i2); Collections.sort(list, comparator); List> queriedInstances = Lists.newArrayList(discovery.queryForInstances(SERVICE_ONE)); Collections.sort(queriedInstances, comparator); assertEquals(queriedInstances, list, String.format("Not equal l: %s - d: %s", list, queriedInstances)); list.clear(); list.add(s2_i1); list.add(s2_i2); Collections.sort(list, comparator); queriedInstances = Lists.newArrayList(discovery.queryForInstances(SERVICE_TWO)); Collections.sort(queriedInstances, comparator); assertEquals(queriedInstances, list, String.format("Not equal 2: %s - d: %s", list, queriedInstances)); } finally { CloseableUtils.closeQuietly(discovery); CloseableUtils.closeQuietly(client); } } @Test public void testBasic() throws Exception { CuratorFramework client = null; ServiceDiscovery discovery = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build(); discovery.start(); assertEquals(discovery.queryForNames(), Collections.singletonList("test")); List> list = Lists.newArrayList(); list.add(instance); assertEquals(discovery.queryForInstances("test"), list); } finally { CloseableUtils.closeQuietly(discovery); CloseableUtils.closeQuietly(client); } } @Test public void testNoServerOnStart() throws Exception { Timing timing = new Timing(); server.stop(); CuratorFramework client = null; ServiceDiscovery discovery = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build(); discovery.start(); server.restart(); timing.sleepABit(); assertEquals(discovery.queryForNames(), Collections.singletonList("test")); List> list = Lists.newArrayList(); list.add(instance); assertEquals(discovery.queryForInstances("test"), list); } finally { CloseableUtils.closeQuietly(discovery); CloseableUtils.closeQuietly(client); } } // CURATOR-164 @Test public void testUnregisterService() throws Exception { final String name = "name"; final CountDownLatch restartLatch = new CountDownLatch(1); InstanceSerializer slowSerializer = new JsonInstanceSerializer(String.class) { private boolean first = true; @Override public byte[] serialize(ServiceInstance instance) throws Exception { if ( first ) { System.out.println("Serializer first registration."); first = false; } else { System.out.println("Waiting for reconnect to finish."); // Simulate the serialize method being slow. // This could just be a timed wait, but that's kind of non-deterministic. restartLatch.await(); } return super.serialize(instance); } }; CuratorFramework client = null; ServiceDiscovery discovery = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name(name).port(10064).build(); discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).serializer(slowSerializer).watchInstances(true).build(); discovery.start(); assertFalse(discovery.queryForInstances(name).isEmpty(), "Service should start registered."); server.stop(); server.restart(); discovery.unregisterService(instance); restartLatch.countDown(); new Timing().sleepABit(); // Wait for the rest of registration to finish. assertTrue(discovery.queryForInstances(name).isEmpty(), "Service should have unregistered."); } finally { CloseableUtils.closeQuietly(discovery); CloseableUtils.closeQuietly(client); } } @Test public void testCleaning() throws Exception { CuratorFramework client = null; ServiceDiscovery discovery = null; try { client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); client.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build(); discovery.start(); discovery.unregisterService(instance); assertEquals(((ServiceDiscoveryImpl)discovery).debugServicesQty(), 0); } finally { CloseableUtils.closeQuietly(discovery); CloseableUtils.closeQuietly(client); } } } TestServiceDiscoveryBuilder.java000066400000000000000000000061541442004423600423360ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestServiceDiscoveryBuilder extends BaseClassForTests { @Test public void testDefaultSerializer() { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); ServiceDiscoveryBuilder builder = ServiceDiscoveryBuilder.builder(Object.class).client(client); ServiceDiscoveryImpl discovery = (ServiceDiscoveryImpl) builder.basePath("/path").build(); assertNotNull(discovery.getSerializer(), "default serializer not set"); assertTrue(discovery.getSerializer() instanceof JsonInstanceSerializer, "default serializer not JSON"); } @Test public void testSetSerializer() { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); ServiceDiscoveryBuilder builder = ServiceDiscoveryBuilder.builder(Object.class).client(client); builder.serializer(new InstanceSerializer() { @Override public byte[] serialize(ServiceInstance instance) { return null; } @Override public ServiceInstance deserialize(byte[] bytes) { return null; } }); ServiceDiscoveryImpl discovery = (ServiceDiscoveryImpl) builder.basePath("/path").build(); assertNotNull(discovery.getSerializer(), "default serializer not set"); assertFalse(discovery.getSerializer() instanceof JsonInstanceSerializer, "set serializer is JSON"); } } TestServiceProvider.java000066400000000000000000000107331442004423600406500ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.Closeable; import java.util.Collections; import java.util.List; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.compatibility.CuratorTestBase; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceProvider; import com.google.common.collect.Lists; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @Tag(CuratorTestBase.zk35TestCompatibilityGroup) public class TestServiceProvider extends BaseClassForTests { @Test public void testBasic() throws Exception { List closeables = Lists.newArrayList(); try { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); closeables.add(client); client.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(String.class).basePath("/test").client(client).thisInstance(instance).build(); closeables.add(discovery); discovery.start(); ServiceProvider provider = discovery.serviceProviderBuilder().serviceName("test").build(); closeables.add(provider); provider.start(); assertEquals(provider.getInstance(), instance); List> list = Lists.newArrayList(); list.add(instance); assertEquals(provider.getAllInstances(), list); } finally { Collections.reverse(closeables); for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } @Test public void testDisabledInstance() throws Exception { List closeables = Lists.newArrayList(); try { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); closeables.add(client); client.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).enabled(false).build(); InstanceSerializer serializer = new JsonInstanceSerializer<>(String.class, false); ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(String.class).serializer(serializer).basePath("/test").client(client).thisInstance(instance).build(); closeables.add(discovery); discovery.start(); ServiceProvider provider = discovery.serviceProviderBuilder().serviceName("test").build(); closeables.add(provider); provider.start(); assertEquals(provider.getInstance(), null); assertTrue(provider.getAllInstances().isEmpty(), "Disabled instance still appears available via service provider"); } finally { Collections.reverse(closeables); for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } } TestWatchedInstances.java000066400000000000000000000075301442004423600407650ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/java/org/apache/curator/x/discovery/details/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.curator.x.discovery.details; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import com.google.common.collect.Lists; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.BaseClassForTests; import org.apache.curator.test.Timing; import org.apache.curator.utils.CloseableUtils; import org.apache.curator.x.discovery.ServiceDiscovery; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceInstance; import org.junit.jupiter.api.Test; import java.io.Closeable; import java.util.Arrays; import java.util.Collections; import java.util.List; public class TestWatchedInstances extends BaseClassForTests { @Test public void testWatchedInstances() throws Exception { Timing timing = new Timing(); List closeables = Lists.newArrayList(); try { CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1)); closeables.add(client); client.start(); ServiceInstance instance = ServiceInstance.builder().payload("thing").name("test").port(10064).build(); ServiceDiscovery discovery = ServiceDiscoveryBuilder .builder(String.class) .basePath("/test") .client(client) .thisInstance(instance) .watchInstances(true) .build(); closeables.add(discovery); discovery.start(); assertEquals(discovery.queryForNames(), Arrays.asList("test")); List> list = Lists.newArrayList(); list.add(instance); assertEquals(discovery.queryForInstances("test"), list); ServiceDiscoveryImpl discoveryImpl = (ServiceDiscoveryImpl)discovery; ServiceInstance changedInstance = ServiceInstance.builder() .id(instance.getId()) .address(instance.getAddress()) .payload("different") .name(instance.getName()) .port(instance.getPort()) .build(); String path = discoveryImpl.pathForInstance("test", instance.getId()); byte[] bytes = discoveryImpl.getSerializer().serialize(changedInstance); client.setData().forPath(path, bytes); timing.sleepABit(); ServiceInstance registeredService = discoveryImpl.getRegisteredService(instance.getId()); assertNotNull(registeredService); assertEquals(registeredService.getPayload(), "different"); } finally { Collections.reverse(closeables); for ( Closeable c : closeables ) { CloseableUtils.closeQuietly(c); } } } } curator-apache-curator-5.5.0/curator-x-discovery/src/test/resources/000077500000000000000000000000001442004423600256045ustar00rootroot00000000000000curator-apache-curator-5.5.0/curator-x-discovery/src/test/resources/log4j.properties000066400000000000000000000021071442004423600307410ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. log4j.rootLogger=ERROR, console log4j.logger.org.apache.curator=DEBUG, console log4j.additivity.org.apache.curator=false log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%-5p %c %x %m [%t]%n curator-apache-curator-5.5.0/doap.rdf000066400000000000000000000047511442004423600175220ustar00rootroot00000000000000 2011-02-06 Apache Curator A set of Java libraries that make using Apache ZooKeeper much easier. Apache Curator is a Java/JVM client library for Apache ZooKeeper, a distributed coordination service. It includes a highlevel API framework and utilities to make using Apache ZooKeeper much easier and more reliable. It also includes recipes for common use cases and extensions such as service discovery and a Java 8 asynchronous DSL. Java curator-apache-curator-5.5.0/licenserc.toml000066400000000000000000000016201442004423600207360ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. headerPath = "Apache-2.0-ASF.txt" excludes = [ "DEPENDENCIES", "doap.rdf", "**/NOTICE", "**/*.confluence", ] curator-apache-curator-5.5.0/merge-pr.py000066400000000000000000000463711442004423600201760ustar00rootroot00000000000000#!/usr/bin/env python # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # Utility for creating well-formed pull request merges and pushing them to Apache. This script is a modified version # of the one created by the Spark project (https://github.com/apache/spark/blob/master/dev/merge_spark_pr.py). # # Usage: ./merge-pr.py (see config env vars below) # # This utility assumes you already have a local Curator git folder and that you # have added remotes corresponding to both: # (i) the github apache Curator mirror and # (ii) the apache Curator git repo. import json import os import re import subprocess import sys import urllib.request, urllib.error, urllib.parse import getpass try: import jira.client JIRA_IMPORTED = True except ImportError: JIRA_IMPORTED = False PROJECT_NAME = "curator" CAPITALIZED_PROJECT_NAME = PROJECT_NAME.upper() # Remote name which points to the GitHub site PR_REMOTE_NAME = os.environ.get("PR_REMOTE_NAME", "apache-github") # Remote name which points to Apache git PUSH_REMOTE_NAME = os.environ.get("PUSH_REMOTE_NAME", "apache") # ASF JIRA username JIRA_USERNAME = os.environ.get("JIRA_USERNAME", "") # ASF JIRA password JIRA_PASSWORD = os.environ.get("JIRA_PASSWORD", "") # OAuth key used for issuing requests against the GitHub API. If this is not defined, then requests # will be unauthenticated. You should only need to configure this if you find yourself regularly # exceeding your IP's unauthenticated request rate limit. You can create an OAuth key at # https://github.com/settings/tokens. This script only requires the "public_repo" scope. GITHUB_OAUTH_KEY = os.environ.get("GITHUB_OAUTH_KEY") GITHUB_USER = os.environ.get("GITHUB_USER", "apache") GITHUB_BASE = "https://github.com/%s/%s/pull" % (GITHUB_USER, PROJECT_NAME) GITHUB_API_BASE = "https://api.github.com/repos/%s/%s" % (GITHUB_USER, PROJECT_NAME) JIRA_BASE = "https://issues.apache.org/jira/browse" JIRA_API_BASE = "https://issues.apache.org/jira" # Prefix added to temporary branches TEMP_BRANCH_PREFIX = "PR_TOOL" # TODO Introduce a convention as this is too brittle RELEASE_BRANCH_PREFIX = "CURATOR" DEV_BRANCH_NAME = "master" DEFAULT_FIX_VERSION = os.environ.get("DEFAULT_FIX_VERSION", "branch-3.5") def get_json(url): try: request = urllib.request.Request(url) if GITHUB_OAUTH_KEY: request.add_header('Authorization', 'token %s' % GITHUB_OAUTH_KEY) return json.load(urllib.request.urlopen(request)) except urllib.error.HTTPError as e: if "X-RateLimit-Remaining" in e.headers and e.headers["X-RateLimit-Remaining"] == '0': print("Exceeded the GitHub API rate limit; see the instructions in " + \ "merge-pr.py to configure an OAuth token for making authenticated " + \ "GitHub requests.") else: print("Unable to fetch URL, exiting: %s" % url) sys.exit(-1) def fail(msg): print(msg) clean_up() sys.exit(-1) def run_cmd(cmd): print(cmd) if isinstance(cmd, list): return subprocess.check_output(cmd, encoding='utf8') else: return subprocess.check_output(cmd.split(" "), encoding='utf8') def continue_maybe(prompt): result = input("\n%s (y/n): " % prompt) if result.lower().strip() != "y": fail("Okay, exiting") def clean_up(): if original_head != get_current_branch(): print("Restoring head pointer to %s" % original_head) run_cmd("git checkout %s" % original_head) branches = run_cmd("git branch").replace(" ", "").split("\n") for branch in [x for x in branches if x.startswith(TEMP_BRANCH_PREFIX)]: print("Deleting local branch %s" % branch) run_cmd("git branch -D %s" % branch) def get_current_branch(): return run_cmd("git rev-parse --abbrev-ref HEAD").replace("\n", "") # merge the requested PR and return the merge hash def merge_pr(pr_num, target_ref, title, body, pr_repo_desc): pr_branch_name = "%s_MERGE_PR_%s" % (TEMP_BRANCH_PREFIX, pr_num) target_branch_name = "%s_MERGE_PR_%s_%s" % (TEMP_BRANCH_PREFIX, pr_num, target_ref.upper()) run_cmd("git fetch %s pull/%s/head:%s" % (PR_REMOTE_NAME, pr_num, pr_branch_name)) run_cmd("git fetch %s %s:%s" % (PUSH_REMOTE_NAME, target_ref, target_branch_name)) run_cmd("git checkout %s" % target_branch_name) had_conflicts = False try: run_cmd(['git', 'merge', pr_branch_name, '--squash']) except Exception as e: msg = "Error merging: %s\nWould you like to manually fix-up this merge?" % e continue_maybe(msg) msg = "Okay, please fix any conflicts and 'git add' conflicting files... Finished?" continue_maybe(msg) had_conflicts = True commit_authors = run_cmd(['git', 'log', 'HEAD..%s' % pr_branch_name, '--pretty=format:%an <%ae>']).split("\n") distinct_authors = sorted(set(commit_authors), key=lambda x: commit_authors.count(x), reverse=True) primary_author = input( "Enter primary author in the format of \"name \" [%s]: " % distinct_authors[0]) if primary_author == "": primary_author = distinct_authors[0] reviewers = input( "Enter reviewers in the format of \"name1 , name2 \": ").strip() commits = run_cmd(['git', 'log', 'HEAD..%s' % pr_branch_name, '--pretty=format:%h [%an] %s']).split("\n") if len(commits) > 1: result = input("List pull request commits in squashed commit message? (y/n): ") if result.lower().strip() == "y": should_list_commits = True else: should_list_commits = False else: should_list_commits = False merge_message_flags = [] merge_message_flags += ["-m", title] if body is not None: # We remove @ symbols from the body to avoid triggering e-mails # to people every time someone creates a public fork of the project. merge_message_flags += ["-m", body.replace("@", "")] authors = "\n".join(["Author: %s" % a for a in distinct_authors]) merge_message_flags += ["-m", authors] if (reviewers != ""): merge_message_flags += ["-m", "Reviewers: %s" % reviewers] if had_conflicts: committer_name = run_cmd("git config --get user.name").strip() committer_email = run_cmd("git config --get user.email").strip() message = "This patch had conflicts when merged, resolved by\nCommitter: %s <%s>" % ( committer_name, committer_email) merge_message_flags += ["-m", message] # The string "Closes #%s" string is required for GitHub to correctly close the PR close_line = "Closes #%s from %s" % (pr_num, pr_repo_desc) if should_list_commits: close_line += " and squashes the following commits:" merge_message_flags += ["-m", close_line] if should_list_commits: merge_message_flags += ["-m", "\n".join(commits)] run_cmd(['git', 'commit', '--author="%s"' % primary_author] + merge_message_flags) continue_maybe("Merge complete (local ref %s). Push to %s?" % ( target_branch_name, PUSH_REMOTE_NAME)) try: run_cmd('git push %s %s:%s' % (PUSH_REMOTE_NAME, target_branch_name, target_ref)) except Exception as e: clean_up() fail("Exception while pushing: %s" % e) merge_hash = run_cmd("git rev-parse %s" % target_branch_name)[:8] clean_up() print(("Pull request #%s merged!" % pr_num)) print(("Merge hash: %s" % merge_hash)) return merge_hash def cherry_pick(pr_num, merge_hash, default_branch): pick_ref = input("Enter a branch name [%s]: " % default_branch) if pick_ref == "": pick_ref = default_branch pick_branch_name = "%s_PICK_PR_%s_%s" % (TEMP_BRANCH_PREFIX, pr_num, pick_ref.upper()) run_cmd("git fetch %s %s:%s" % (PUSH_REMOTE_NAME, pick_ref, pick_branch_name)) run_cmd("git checkout %s" % pick_branch_name) try: run_cmd("git cherry-pick -sx %s" % merge_hash) except Exception as e: msg = "Error cherry-picking: %s\nWould you like to manually fix-up this merge?" % e continue_maybe(msg) msg = "Okay, please fix any conflicts and finish the cherry-pick. Finished?" continue_maybe(msg) continue_maybe("Pick complete (local ref %s). Push to %s?" % ( pick_branch_name, PUSH_REMOTE_NAME)) try: run_cmd('git push %s %s:%s' % (PUSH_REMOTE_NAME, pick_branch_name, pick_ref)) except Exception as e: clean_up() fail("Exception while pushing: %s" % e) pick_hash = run_cmd("git rev-parse %s" % pick_branch_name)[:8] clean_up() print(("Pull request #%s picked into %s!" % (pr_num, pick_ref))) print(("Pick hash: %s" % pick_hash)) return pick_ref def fix_version_from_branch(branch, versions): # Note: Assumes this is a sorted (newest->oldest) list of un-released versions if branch == DEV_BRANCH_NAME: versions = [x for x in versions if x == DEFAULT_FIX_VERSION] if len(versions) > 0: return versions[0] else: return None else: versions = [x for x in versions if x.startswith(branch)] if len(versions) > 0: return versions[-1] else: return None def resolve_jira_issue(merge_branches, comment, default_jira_id=""): asf_jira = jira.client.JIRA({'server': JIRA_API_BASE}, basic_auth=(JIRA_USERNAME, JIRA_PASSWORD)) jira_id = input("Enter a JIRA id [%s]: " % default_jira_id) if jira_id == "": jira_id = default_jira_id try: issue = asf_jira.issue(jira_id) except Exception as e: fail("ASF JIRA could not find %s\n%s" % (jira_id, e)) cur_status = issue.fields.status.name cur_summary = issue.fields.summary cur_assignee = issue.fields.assignee if cur_assignee is None: cur_assignee = "NOT ASSIGNED!!!" else: cur_assignee = cur_assignee.displayName if cur_status == "Resolved" or cur_status == "Closed": fail("JIRA issue %s already has status '%s'" % (jira_id, cur_status)) print(("=== JIRA %s ===" % jira_id)) print(("summary\t\t%s\nassignee\t%s\nstatus\t\t%s\nurl\t\t%s/%s\n" % ( cur_summary, cur_assignee, cur_status, JIRA_BASE, jira_id))) versions = asf_jira.project_versions(CAPITALIZED_PROJECT_NAME) versions = sorted(versions, key=lambda x: x.name, reverse=True) versions = [x for x in versions if x.raw['released'] is False] version_names = [x.name for x in versions] default_fix_versions = [fix_version_from_branch(x, version_names) for x in merge_branches] default_fix_versions = [x for x in default_fix_versions if x != None] default_fix_versions = ",".join(default_fix_versions) fix_versions = input("Enter comma-separated fix version(s) [%s]: " % default_fix_versions) if fix_versions == "": fix_versions = default_fix_versions fix_versions = fix_versions.replace(" ", "").split(",") def get_version_json(version_str): return [v for v in versions if v.name == version_str][0].raw jira_fix_versions = [get_version_json(v) for v in fix_versions] resolve = [a for a in asf_jira.transitions(jira_id) if a['name'] == "Resolve Issue"][0] resolution = [r for r in asf_jira.resolutions() if r.raw['name'] == "Fixed"][0] asf_jira.transition_issue( jira_id, resolve["id"], fixVersions = jira_fix_versions, comment = comment, resolution = {'id': resolution.raw['id']}) print("Successfully resolved %s with fixVersions=%s!" % (jira_id, fix_versions)) def resolve_jira_issues(title, merge_branches, comment): jira_ids = re.findall("%s-[0-9]{4,5}" % CAPITALIZED_PROJECT_NAME, title) if len(jira_ids) == 0: resolve_jira_issue(merge_branches, comment) for jira_id in jira_ids: resolve_jira_issue(merge_branches, comment, jira_id) def standardize_jira_ref(text): """ Standardize the jira reference commit message prefix to "PROJECT_NAME-XXX: Issue" """ jira_refs = [] components = [] # Extract JIRA ref(s): pattern = re.compile(r'(%s[-\s]*[0-9]{3,6})+' % CAPITALIZED_PROJECT_NAME, re.IGNORECASE) for ref in pattern.findall(text): # Add brackets, replace spaces with a dash, & convert to uppercase jira_refs.append(re.sub(r'\s+', '-', ref.upper())) text = text.replace(ref, '') # Extract project name component(s): # Look for alphanumeric chars, spaces, dashes, periods, and/or commas pattern = re.compile(r'(\[[\w\s,-\.]+\])', re.IGNORECASE) for component in pattern.findall(text): components.append(component.upper()) text = text.replace(component, '') # Cleanup any remaining symbols: pattern = re.compile(r'^\W+(.*)', re.IGNORECASE) if (pattern.search(text) is not None): text = pattern.search(text).groups()[0] # Assemble full text (JIRA ref(s), module(s), remaining text) jira_prefix = ' '.join(jira_refs).strip() if jira_prefix: jira_prefix = jira_prefix + ": " clean_text = jira_prefix + ' '.join(components).strip() + " " + text.strip() # Replace multiple spaces with a single space, e.g. if no jira refs and/or components were included clean_text = re.sub(r'\s+', ' ', clean_text.strip()) return clean_text def get_remote_repos(): repos = run_cmd("git remote -v").split() dict = {} for i in range(0, len(repos), 3): dict[repos[i]] = repos[i+1] return dict def check_git_remote(): repos = get_remote_repos() # check if all remote endpoints' URLs point to project git repo name = PROJECT_NAME + ".git" for url in list(repos.values()): if not url.endswith(name): fail("Error: not a %s git repo or at least one remote is invalid" % PROJECT_NAME) if not PR_REMOTE_NAME in repos: fail("Error: PR_REMOTE_NAME (%s) environment variable has not been set!" % PR_REMOTE_NAME) if not PUSH_REMOTE_NAME in repos: fail("Error: PUSH_REMOTE_NAME (%s) environment variable has not been set!" % PUSH_REMOTE_NAME) def check_jira_env(): global JIRA_PASSWORD if JIRA_IMPORTED: if JIRA_USERNAME.strip() != "" and JIRA_PASSWORD.strip() == "": inform_pwd = input("JIRA_USERNAME set but JIRA_PASSWORD is not. Want to inform it? ") if inform_pwd.strip() == "y": JIRA_PASSWORD = getpass.getpass('JIRA PASSWORD: ') if JIRA_USERNAME.strip() == "" or JIRA_PASSWORD.strip() == "": msg ="JIRA_USERNAME and/or JIRA_PASSWORD are not set. Want to continue? " continue_maybe(msg) else: msg = "JIRA lib not installed. Want to continue? " continue_maybe(msg) def main(): global original_head original_head = get_current_branch() check_jira_env() check_git_remote() branches = get_json("%s/branches" % GITHUB_API_BASE) branch_names = [x for x in [x['name'] for x in branches] if x.startswith(RELEASE_BRANCH_PREFIX)] # Assumes branch names can be sorted lexicographically latest_branch = sorted(branch_names, reverse=True)[0] pr_num = input("Which pull request would you like to merge? (e.g. 34): ") pr = get_json("%s/pulls/%s" % (GITHUB_API_BASE, pr_num)) pr_events = get_json("%s/issues/%s/events" % (GITHUB_API_BASE, pr_num)) url = pr["url"] pr_title = pr["title"] commit_title = input("Commit title [%s]: " % pr_title) if commit_title == "": commit_title = pr_title # Decide whether to use the modified title or not modified_title = standardize_jira_ref(commit_title) if modified_title != commit_title: print("I've re-written the title as follows to match the standard format:") print("Original: %s" % commit_title) print("Modified: %s" % modified_title) result = input("Would you like to use the modified title? (y/n): ") if result.lower().strip() == "y": commit_title = modified_title print("Using modified title:") else: print("Using original title:") print(commit_title) body = pr["body"] target_ref = pr["base"]["ref"] user_login = pr["user"]["login"] base_ref = pr["head"]["ref"] pr_repo_desc = "%s/%s" % (user_login, base_ref) # Merged pull requests don't appear as merged in the GitHub API; # Instead, they're closed by asfgit. merge_commits = \ [e for e in pr_events if e["actor"]["login"] == "asfgit" and e["event"] == "closed"] if merge_commits: merge_hash = merge_commits[0]["commit_id"] message = get_json("%s/commits/%s" % (GITHUB_API_BASE, merge_hash))["commit"]["message"] print("Pull request %s has already been merged, assuming you want to backport" % pr_num) commit_is_downloaded = run_cmd(['git', 'rev-parse', '--quiet', '--verify', "%s^{commit}" % merge_hash]).strip() != "" if not commit_is_downloaded: fail("Couldn't find any merge commit for #%s, you may need to update HEAD." % pr_num) print("Found commit %s:\n%s" % (merge_hash, message)) cherry_pick(pr_num, merge_hash, latest_branch) sys.exit(0) if not bool(pr["mergeable"]): msg = "Pull request %s is not mergeable in its current form.\n" % pr_num + \ "Continue? (experts only!)" continue_maybe(msg) print(("\n=== Pull Request #%s ===" % pr_num)) print(("PR title\t%s\nCommit title\t%s\nSource\t\t%s\nTarget\t\t%s\nURL\t\t%s" % ( pr_title, commit_title, pr_repo_desc, target_ref, url))) continue_maybe("Proceed with merging pull request #%s?" % pr_num) merged_refs = [target_ref] merge_hash = merge_pr(pr_num, target_ref, commit_title, body, pr_repo_desc) pick_prompt = "Would you like to pick %s into another branch?" % merge_hash while input("\n%s (y/n): " % pick_prompt).lower().strip() == "y": merged_refs = merged_refs + [cherry_pick(pr_num, merge_hash, latest_branch)] if JIRA_IMPORTED: if JIRA_USERNAME and JIRA_PASSWORD: continue_maybe("Would you like to update an associated JIRA?") jira_comment = "Issue resolved by pull request %s\n[%s/%s]" % (pr_num, GITHUB_BASE, pr_num) resolve_jira_issues(commit_title, merged_refs, jira_comment) else: print("JIRA_USERNAME and JIRA_PASSWORD not set") print("Exiting without trying to close the associated JIRA.") else: print("Could not find jira-python library. Run 'sudo pip install jira' to install.") print("Exiting without trying to close the associated JIRA.") if __name__ == "__main__": import doctest (failure_count, test_count) = doctest.testmod() if (failure_count): exit(-1) main() curator-apache-curator-5.5.0/mvnw000077500000000000000000000230651442004423600170160ustar00rootroot00000000000000#!/bin/sh # ---------------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # Apache Maven Wrapper startup batch script, version 3.1.1 # # Required ENV vars: # ------------------ # JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- if [ -z "$MAVEN_SKIP_RC" ] ; then if [ -f /usr/local/etc/mavenrc ] ; then . /usr/local/etc/mavenrc fi if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi if [ -f "$HOME/.mavenrc" ] ; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. cygwin=false; darwin=false; mingw=false case "`uname`" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME else JAVA_HOME="/Library/Java/Home"; export JAVA_HOME fi fi ;; esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then JAVA_HOME=`java-config --jre-home` fi fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" fi if [ -z "$JAVA_HOME" ]; then javaExecutable="`which javac`" if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=`which readlink` if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then if $darwin ; then javaHome="`dirname \"$javaExecutable\"`" javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" else javaExecutable="`readlink -f \"$javaExecutable\"`" fi javaHome="`dirname \"$javaExecutable\"`" javaHome=`expr "$javaHome" : '\(.*\)/bin'` JAVA_HOME="$javaHome" export JAVA_HOME fi fi fi if [ -z "$JAVACMD" ] ; then if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi else JAVACMD="`\\unset -f command; \\command -v java`" fi fi if [ ! -x "$JAVACMD" ] ; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" return 1 fi basedir="$1" wdir="$1" while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then wdir=`cd "$wdir/.."; pwd` fi # end of workaround done printf '%s' "$(cd "$basedir"; pwd)" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then echo "$(tr -s '\n' ' ' < "$1")" fi } BASE_DIR=$(find_maven_basedir "$(dirname $0)") if [ -z "$BASE_DIR" ]; then exit 1; fi MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR if [ "$MVNW_VERBOSE" = true ]; then echo $MAVEN_PROJECTBASEDIR fi ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. ########################################################################################## if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found .mvn/wrapper/maven-wrapper.jar" fi else if [ "$MVNW_VERBOSE" = true ]; then echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi if [ -n "$MVNW_REPOURL" ]; then wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" else wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" fi while IFS="=" read key value; do case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; esac done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" if [ "$MVNW_VERBOSE" = true ]; then echo "Downloading from: $wrapperUrl" fi wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" if $cygwin; then wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` fi if command -v wget > /dev/null; then QUIET="--quiet" if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" QUIET="" fi if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" else wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" fi [ $? -eq 0 ] || rm -f "$wrapperJarPath" elif command -v curl > /dev/null; then QUIET="--silent" if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" QUIET="" fi if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L else curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L fi [ $? -eq 0 ] || rm -f "$wrapperJarPath" else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" # For Cygwin, switch paths to Windows format before running javac if $cygwin; then javaSource=`cygpath --path --windows "$javaSource"` javaClass=`cygpath --path --windows "$javaClass"` fi if [ -e "$javaSource" ]; then if [ ! -e "$javaClass" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo " - Compiling MavenWrapperDownloader.java ..." fi # Compiling the Java class ("$JAVA_HOME/bin/javac" "$javaSource") fi if [ -e "$javaClass" ]; then # Running the downloader if [ "$MVNW_VERBOSE" = true ]; then echo " - Running MavenWrapperDownloader.java ..." fi ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") fi fi fi fi ########################################################################################## # End of extension ########################################################################################## MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"` [ -n "$MAVEN_PROJECTBASEDIR" ] && MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi # Provide a "standardized" way to retrieve the CLI args that will # work with both Windows and non-Windows executions. MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" export MAVEN_CMD_LINE_ARGS WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" curator-apache-curator-5.5.0/mvnw.cmd000066400000000000000000000153511442004423600175540ustar00rootroot00000000000000@REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Apache Maven Wrapper startup batch script, version 3.1.1 @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM set title of command window title %0 @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @REM ==== END VALIDATION ==== :init @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @REM This allows using the maven wrapper in projects that prohibit checking in binary data. if exist %WRAPPER_JAR% ( if "%MVNW_VERBOSE%" == "true" ( echo Found %WRAPPER_JAR% ) ) else ( if not "%MVNW_REPOURL%" == "" ( SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" ) if "%MVNW_VERBOSE%" == "true" ( echo Couldn't find %WRAPPER_JAR%, downloading it ... echo Downloading from: %WRAPPER_URL% ) powershell -Command "&{"^ "$webclient = new-object System.Net.WebClient;"^ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ "}"^ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ "}" if "%MVNW_VERBOSE%" == "true" ( echo Finished downloading %WRAPPER_JAR% ) ) @REM End of extension @REM Provide a "standardized" way to retrieve the CLI args that will @REM work with both Windows and non-Windows executions. set MAVEN_CMD_LINE_ARGS=%* %MAVEN_JAVA_EXE% ^ %JVM_CONFIG_MAVEN_PROPS% ^ %MAVEN_OPTS% ^ %MAVEN_DEBUG_OPTS% ^ -classpath %WRAPPER_JAR% ^ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%"=="on" pause if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% cmd /C exit /B %ERROR_CODE% curator-apache-curator-5.5.0/pom.xml000066400000000000000000001163241442004423600174170ustar00rootroot00000000000000 4.0.0 org.apache apache 21 org.apache.curator apache-curator 5.5.0 pom Apache Curator Curator is a set of Java libraries that make using Apache ZooKeeper much easier. https://curator.apache.org 2011 The Apache Software License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0.txt repo The Apache Software Foundation https://www.apache.org/ 1.8 1.8 false 5.4.0 UTF-8 UTF-8 UTF-8 8 1.${short-jdk-version} true 3.7.1 5.1.4 3.10.0 3.2.0 3.0.0 3.3.2 3.0.0-M5 1.11.1 3.24.1-GA 2.2 1.9.13 2.10.0 3.23.1 1.19.4 1.1.1 2.2.11 1.1.1 9.4.45.v20220203 1.0.2 2.3.5.Final 31.1-jre 1.0 1.0.1 5.6.2 0.23.1 3.2.4 1.7.25 2.8 3.2.5 1.1.7 3.3.0 4.1.1 https://github.com/apache/curator.git scm:git:https://gitbox.apache.org/repos/asf/curator.git scm:git:https://gitbox.apache.org/repos/asf/curator.git apache-curator-5.5.0 JIRA https://issues.apache.org/jira/browse/CURATOR Jenkins https://builds.apache.org/job/Curator/ mail true true false false
dev@curator.apache.org
apache.website.svnpub scm:svn:https://svn.apache.org/repos/asf/curator/site/trunk Users mailto:user-subscribe@curator.apache.org mailto:user-unsubscribe@curator.apache.org mailto:user@curator.apache.org https://mail-archives.apache.org/mod_mbox/curator-user/ Development mailto:dev-subscribe@curator.apache.org mailto:dev-unsubscribe@curator.apache.org mailto:dev@curator.apache.org https://mail-archives.apache.org/mod_mbox/curator-dev/ Commits mailto:commits-subscribe@curator.apache.org mailto:commits-unsubscribe@curator.apache.org mailto:commits@curator.apache.org https://mail-archives.apache.org/mod_mbox/curator-commits/ randgalt Jordan Zimmerman randgalt@apache.org -5 Committer PMC Member https://people.apache.org/~randgalt zarfide Jay Zarfoss zarfide@apache.org -8 Committer PMC Member https://www.linkedin.com/pub/jay-zarfoss/34/56/a19 cheddar Eric Tschetter cheddar@apache.org -6 Committer PMC Member ChedHeader iocanel Ioannis Canellos iocanel@apache.org +2 Committer PMC Member https://iocanel.blogspot.com cammckenzie Cameron McKenzie cammckenzie@apache.org +10 Committer PMC Member https://people.apache.org/~cammckenzie dragonsinth Scott Blum dragonsinth@apache.org -5 Committer PMC Member https://github.com/dragonsinth mdrob Mike Drob mdrob@apache.org -6 Committer PMC Member https://people.apache.org/~mdrob Patrick Hunt phunt1@gmail.com PMC Member -8 https://www.linkedin.com/pub/patrick-hunt/2/5b2/24a Mahadev Konar mahadev@apache.org PMC Member -8 https://www.linkedin.com/in/mahadevkonar Luciano Resende lresende@apache.org PMC Member -8 https://people.apache.org/~lresende Enis Söztutar enis@apache.org PMC Member -8 https://people.apache.org/~enis Fangmin Lyu fangmin@apache.org Committer PMC Member -8 https://people.apache.org/~fangmin shayshim Shay Shimony shayshim@apache.org +2 Committer PMC Member https://people.apache.org/~shayshim eolivelli Enrico Olivelli eolivelli@apache.org +1 Committer PMC Chair https://people.apache.org/~eolivelli tison Zili Chen tison@apache.org +8 Committer PMC Member https://github.com/tisonkun/ curator-client curator-test curator-framework curator-recipes curator-examples curator-x-discovery curator-x-discovery-server curator-x-async curator-test-zk35 curator-test-zk36 org.slf4j slf4j-api ${slf4j-version} org.slf4j slf4j-log4j12 ${slf4j-version} org.mockito mockito-core 1.9.5 org.assertj assertj-core ${assertj-version} org.apache.curator curator-client ${project.version} org.apache.curator curator-client test-jar ${project.version} org.apache.curator curator-framework ${project.version} org.apache.curator curator-framework test-jar ${project.version} org.apache.curator curator-recipes ${project.version} org.apache.curator curator-recipes ${project.version} test-jar org.apache.curator curator-test ${project.version} org.apache.curator curator-x-discovery ${project.version} org.apache.curator curator-x-discovery-server ${project.version} org.apache.curator curator-x-async ${project.version} org.apache.commons commons-math ${commons-math-version} com.fasterxml.jackson.core jackson-core ${jackson-version} com.fasterxml.jackson.core jackson-databind ${jackson-version} com.sun.jersey jersey-server ${jersey-version} com.sun.jersey jersey-servlet ${jersey-version} com.sun.jersey jersey-client ${jersey-version} com.sun.jersey jersey-core ${jersey-version} javax.ws.rs jsr311-api ${jsr311-api-version} javax.xml.bind jaxb-api ${jaxb-version} com.sun.xml.bind jaxb-core ${jaxb-version} com.sun.xml.bind jaxb-impl ${jaxb-version} javax.activation activation ${javax-activation-version} org.eclipse.jetty jetty-webapp ${jetty-version} net.sf.scannotation scannotation ${scannotation-version} org.jboss.resteasy resteasy-jaxrs ${resteasy-jaxrs-version} org.scannotation scannotation org.apache.zookeeper zookeeper ${zookeeper-version} com.sun.jmx jmxri com.sun.jdmk jmxtools javax.jms jms junit junit org.slf4j slf4j-log4j12 com.google.guava guava ${guava-version} com.google.guava listenablefuture ${guava-listenablefuture-version} com.google.guava failureaccess ${guava-failureaccess-version} org.junit.jupiter junit-jupiter-api ${junit-version} org.junit.jupiter junit-jupiter-engine ${junit-version} com.facebook.swift swift-codec ${swift-version} com.facebook.swift swift-service ${swift-version} io.dropwizard dropwizard-configuration ${dropwizard-version} io.dropwizard dropwizard-logging ${dropwizard-version} io.dropwizard.metrics metrics-core ${dropwizard-version} org.slf4j slf4j-api org.xerial.snappy snappy-java ${snappy-version} org.awaitility awaitility ${awaitility-version} test org.apache.maven.plugins maven-project-info-reports-plugin ${maven-project-info-reports-plugin-version} org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin-version} -J-Xmx1g false org.codehaus.mojo clirr-maven-plugin org/apache/curator/** org.apache.felix maven-bundle-plugin ${maven-bundle-plugin-version} org.apache.maven.plugins maven-release-plugin true ${project.artifactId}-${project.version} false true org.apache.maven.plugins maven-shade-plugin ${maven-shade-plugin-version} org.codehaus.mojo clirr-maven-plugin ${clirr-maven-plugin-version} org.codehaus.mojo build-helper-maven-plugin ${build-helper-maven-plugin-version} org.apache.maven.plugins maven-dependency-plugin ${maven-dependency-plugin-version} ${basedir} META-INF DISCLAIMER LICENSE NOTICE org.apache.felix maven-bundle-plugin true true ${project.name} ${project.artifactId} ${osgi.export.package} ${osgi.import.package} ${osgi.dynamic.import} ${osgi.private.package} ${osgi.require.bundle} ${osgi.activator} ${osgi.export.service} jar war bundle true bundle-manifest process-classes manifest org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin-version} 1 false ${redirectTestOutputToFile} 2 org.apache.maven.plugins maven-javadoc-plugin -J-Xmx1g false org.apache.maven.plugins maven-site-plugin en true org.apache.maven.doxia doxia-module-confluence ${doxia-module-confluence-version} site site org.apache.maven.plugins maven-scm-publish-plugin false Curator website deployment ${curator-website-checkout-path} scm-publish site-deploy publish-scm org.apache.maven.plugins maven-release-plugin -DskipTests forked-path org.codehaus.mojo clirr-maven-plugin false false org/apache/curator/** compile check org.apache.maven.plugins maven-shade-plugin apache-curator-guava-shader shade package false com.google org.apache.curator.shaded.com.google ${project.groupId}:${project.artifactId} com.google.guava:guava META-INF/** com.google.guava:listenablefuture META-INF/** com.google.guava:failureaccess META-INF/** org.apache.maven.plugins maven-antrun-plugin package run true org.codehaus.mojo build-helper-maven-plugin package attach-artifact ${project.build.directory}/original-${project.build.finalName}.jar jar osgi ${skip-attaching-original-artifact} org.apache.maven.plugins maven-dependency-plugin ${maven-dependency-plugin-version} jdk-8-minus (,1.8] org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin-version} ${jdk-version} ${jdk-version} jdk-9-plus [1.9,) org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin-version} ${short-jdk-version} staging-repo staging-repo https://repository.apache.org/content/groups/staging/
curator-apache-curator-5.5.0/src/000077500000000000000000000000001442004423600166625ustar00rootroot00000000000000curator-apache-curator-5.5.0/src/site/000077500000000000000000000000001442004423600176265ustar00rootroot00000000000000curator-apache-curator-5.5.0/src/site/confluence/000077500000000000000000000000001442004423600217475ustar00rootroot00000000000000curator-apache-curator-5.5.0/src/site/confluence/breaking-changes.confluence000066400000000000000000000024301442004423600272010ustar00rootroot00000000000000h1. Curator 5.0 Breaking Changes Curator 5.0 contains a few non\-backward compatible/breaking changes from previous versions. * ZooKeeper 3.4.x is no longer supported (the associated {{Compatibility}} classes/methods have been removed). If you still need to use Curator with ZooKeeper 3.4.x you will need to use a previous version. [[Click here for details|zk-compatibility-34.html]]. * The old {{ListenerContainer}} classes have been removed so as not to leak Guava classes into Curator APIs. Instead use the new {{StandardListenerManager}}. * Exhibitor support has been removed. * {{ConnectionHandlingPolicy}} and related classes have been removed. * The {{Reaper}} and {{ChildReaper}} classes/recipes have been removed. You should use ZooKeeper container nodes instead. * {{newPersistentEphemeralNode()}} and {{newPathChildrenCache()}} were removed from {{GroupMember}}. * {{ServiceCacheBuilder executorService(CloseableExecutorService executorService)}} was removed from {{ServiceCacheBuilder}}. * {{ServiceProviderBuilder executorService(CloseableExecutorService executorService)}} was removed from {{ServiceProviderBuilder}}. * {{static boolean shouldRetry(int rc)}} was removed from {{RetryLoop}}. * {{static boolean isRetryException(Throwable exception)}} was removed from {{RetryLoop}}. curator-apache-curator-5.5.0/src/site/confluence/compatibility.confluence000066400000000000000000000010031442004423600266550ustar00rootroot00000000000000h1. API Compatibility A [[Clirr|http://clirr.sourceforge.net/]] report is generated for each Curator module: * [[Curator Client Report|curator-client/clirr-report.html]] * [[Curator Framework Report|curator-framework/clirr-report.html]] * [[Curator Recipes Report|curator-recipes/clirr-report.html]] * [[Curator Async Report|curator-x-async/clirr-report.html]] * [[Curator Discovery Report|curator-x-discovery/clirr-report.html]] * [[Curator Discovery Server Report|curator-x-discovery-server/clirr-report.html]] curator-apache-curator-5.5.0/src/site/confluence/errors.confluence000066400000000000000000000074041442004423600253330ustar00rootroot00000000000000h1. Error Handling h2. Background ZooKeeper is a very low level system that requires users to do a lot of housekeeping. See: * [[https://wiki.apache.org/hadoop/ZooKeeper/FAQ]] The Curator [[Framework|curator-framework/index.html]] is designed to hide as much of the details/tedium of this housekeeping as is possible. h2. Connection Guarantees The Curator [[Framework|curator-framework/index.html]] constantly monitors the connection to the ZooKeeper ensemble. Further every operation is wrapped in a retry mechanism. Thus, the following guarantees can be made: * Every Curator operation properly waits until the ZooKeeper connection is established * Every Curator [[Framework|curator-framework/index.html]] operation (create, getData, etc.) is guaranteed to manage connection loss and/or session expiration per the currently set retry policy * If the connection is temporarily lost, Curator will attempt to retry the operation until it succeeds per the currently set retry policy * All Curator recipes attempt to deal with connection issues in an appropriate way h2. Notifications Curator exposes several listenable interfaces for clients to monitor the state of the ZooKeeper connection. {{ConnectionStateListener}} is called when there are connection disruptions. Clients can monitor these changes and take appropriate action. These are the possible state changes: |CONNECTED|Sent for the first successful connection to the server. NOTE: You will only get one of these messages for any CuratorFramework instance.| |READ_ONLY|The connection has gone into read\-only mode. This can only happen if you pass true for CuratorFrameworkFactory.Builder.canBeReadOnly(). See the ZooKeeper doc regarding read only connections: [[https://wiki.apache.org/hadoop/ZooKeeper/GSoCReadOnlyMode]]. The connection will remain in read only mode until another state change is sent.| |SUSPENDED|There has been a loss of connection. Leaders, locks, etc. should suspend until the connection is re\-established.| |RECONNECTED|A suspended or lost connection has been re\-established.| |LOST|Curator will set the LOST state when it believes that the ZooKeeper session has expired. ZooKeeper connections have a session. When the session expires, clients must take appropriate action. In Curator, this is complicated by the fact that Curator internally manages the ZooKeeper connection. Curator will set the LOST state when any of the following occurs: a) ZooKeeper returns a Watcher.Event.KeeperState.Expired or KeeperException.Code.SESSIONEXPIRED; b) Curator closes the internally managed ZooKeeper instance; c) The session timeout elapses during a network partition. It is possible to get a RECONNECTED state after this but you should still consider any locks, etc. as dirty/unstable. *NOTE*: The meaning of LOST has changed since Curator 3.0.0. Prior to 3.0.0 LOST only meant that the retry policy had expired.| {{UnhandledErrorListener}} is called when a background task, etc. catches an exception. In general, Curator users shouldn't care about these as they are logged. However, you can listen for them if you choose. h2. Error Policy Curator has a pluggable error policy. The default policy takes the conservative approach of treating connection states SUSPENDED and LOST the same way. i.e. when a recipe sees the state change to SUSPENDED it will assume that the ZooKeeper session is lost and will clean up any watchers, nodes, etc. You can choose, however, a more aggressive approach by setting the error policy to only treat LOST (i.e. true session loss) as an error state. Do this in the CuratorFrameworkFactory via: {{connectionStateErrorPolicy(new SessionConnectionStateErrorPolicy())}}. h2. Recipes In general, the recipes attempt to deal with errors and connection issues. See the doc for each recipe for details on how it deals with errors. curator-apache-curator-5.5.0/src/site/confluence/getting-started.confluence000066400000000000000000000054411442004423600271230ustar00rootroot00000000000000h1. Getting Started h2. Learn ZooKeeper Curator users are assumed to know ZooKeeper. A good place to start is here: [[https://zookeeper.apache.org/doc/current/zookeeperStarted.html]] h2. Using Curator The Curator JARs are available from Maven Central. The various artifacts are listed on the [[main page|index.html]]. Users of Maven, Gradle, Ant, etc. can easily include Curator into their build script. Most users will want to use one of Curator's pre\-built recipes. So, the curator\-recipes is the correct artifact to use. If you only want a wrapper around ZooKeeper that adds connection management and retry policies, use curator\-framework. h2. Getting a Connection Curator uses [[Fluent Style|https://en.wikipedia.org/wiki/Fluent%5Finterface]]. If you haven't used this before, it might seem odd so it's suggested that you familiarize yourself with the style. Curator connection instances ({{CuratorFramework}}) are allocated from the {{CuratorFrameworkFactory}}. You only need *one* {{CuratorFramework}} object for each ZooKeeper cluster you are connecting to: {code} CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy) {code} This will create a connection to a ZooKeeper cluster using default values. The only thing that you need to specify is the retry policy. For most cases, you should use: {code} RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3) CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy); client.start(); {code} The client must be started (and closed when no longer needed). h2. Calling ZooKeeper Directly Once you have a CuratorFramework instance, you can make direct calls to ZooKeeper in a similar way to using the raw {{ZooKeeper}} object provided in the ZooKeeper distribution. E.g.: {code} client.create().forPath("/my/path", myData) {code} The benefit here is that Curator manages the ZooKeeper connection and will retry operations if there are connection problems. h2. Recipes h3. Distributed Lock {code} InterProcessMutex lock = new InterProcessMutex(client, lockPath); if ( lock.acquire(maxWait, waitUnit) ) { try { // do some work inside of the critical section here } finally { lock.release(); } } {code} h3. Leader Election {code} LeaderSelectorListener listener = new LeaderSelectorListenerAdapter() { public void takeLeadership(CuratorFramework client) throws Exception { // this callback will get called when you are the leader // do whatever leader work you need to and only exit // this method when you want to relinquish leadership } } LeaderSelector selector = new LeaderSelector(client, path, listener); selector.autoRequeue(); // not required, but this is behavior that you will probably expect selector.start(); {code} curator-apache-curator-5.5.0/src/site/confluence/index.confluence000066400000000000000000000053211442004423600251220ustar00rootroot00000000000000h1. Welcome to Apache Curator h2. What is Curator? Curator _n ˈkyoor͝ˌātər_: a keeper or custodian of a museum or other collection \- A ZooKeeper Keeper. Apache Curator is a Java/JVM client library for [[Apache ZooKeeper|https://zookeeper.apache.org/]], a distributed coordination service. It includes a high-level API framework and utilities to make using Apache ZooKeeper much easier and more reliable. It also includes recipes for common use cases and extensions such as service discovery and a Java 8 asynchronous DSL. !images/ph-quote.png! h2. Download [[Download Apache Curator|releases.html]] from the release page. h2. Important Compatibility Updates Note: version 5.0 of Curator has a few breaking changes. Please read the details here: [[Curator 5.0 Breaking Changes|breaking-changes.html]]. h2. Getting Started See the page for quick start: [[Getting Started|getting-started.html]]. h2. Stack Overflow There's lots of great help on Stack Overflow: * [[Main "Apache Curator" tag|https://stackoverflow.com/questions/tagged/apache-curator]] h2. Nav Bar !images/arrow.png! Use the navigation links in the left\-nav menu for detailed information. h2. Maven / Artifacts Curator binaries are published to Maven Central. Curator consists of several artifacts. Which artifacts to use depends on your needs. For most users, the only artifact you need is curator\-recipes. For OSGi users, there's another set of artifacts of classifier osgi available in Maven Central. These artifacts are not shaded, thus no package relocation is performed inside, so hopefully they could start as bundles when their dependencies are fulfilled by dedicated bundles, e.g. guava. ||GroupID/Org||ArtifactID/Name||Description|| |org.apache.curator|curator\-recipes|All of the recipes. Note: this artifact has dependencies on client and framework and, so, Maven (or whatever tool you're using) should pull those in automatically.| |org.apache.curator|curator\-async|Asynchronous DSL with O/R modeling, migrations and many other features.| |org.apache.curator|curator\-framework|The Curator Framework high level API. This is built on top of the client and should pull it in automatically.| |org.apache.curator|curator\-client|The Curator Client \- replacement for the ZooKeeper class in the ZK distribution.| |org.apache.curator|curator\-test|Contains the TestingServer, the TestingCluster and a few other tools useful for testing.| |org.apache.curator|curator\-examples|Example usages of various Curator features.| |org.apache.curator|curator\-x\-discovery|A Service Discovery implementation built on the Curator Framework.| |org.apache.curator|curator\-x\-discovery\-server|A RESTful server that can be used with Curator Discovery.| curator-apache-curator-5.5.0/src/site/confluence/logging.confluence000066400000000000000000000014441442004423600254430ustar00rootroot00000000000000h1. Logging and Tracing h2. Details Curator is logging and tracing neutral. The Curator code is instrumented with logging and tracers but uses a driver mechanism that allows easy integration into your preferred logging and tracing frameworks. h2. Logging Curator uses SLF4J ([[https://www.slf4j.org/]]) for logging. SLF4J is a facade over logging that allows you to plug in any (or no) logging framework. See the SLF4J website for details. h2. Tracing Connect Curator tracing to your tracing framework via an instance of {{TracerDriver}} or {{AdvancedTracerDriver}}. Curator calls the various methods (e.g. addTrace() or addCount() ) and your instance proxies the calls to your tracing framework. Inform Curator of your tracing driver instance by calling {{CuratorZookeeperClient.setTracerDriver()}}. curator-apache-curator-5.5.0/src/site/confluence/releases.confluence000066400000000000000000000027771442004423600256320ustar00rootroot00000000000000h1. Apache Curator Releases h2. Download The current release can be [[downloaded using these links|#Current_Release]]. Older releases are available from the [[archive|https://archive.apache.org/dist/curator/]]. You can verify the integrity of a downloaded release using the PGP signatures and hashes hosted at the main Apache distribution site. For additional information, refer to the Apache documentation for [[verifying the integrity of Apache project releases|https://www.apache.org/info/verification.html]]. The binary artifacts for Curator are available from [[Maven Central|http://search.maven.org/#search%7Cga%7C1%7Corg.apache.curator]] and its mirrors. h2. Current Release * Current Release: [[apache\-curator\-${currentStableVersion}\-source\-release.zip|https://www.apache.org/dyn/closer.lua/curator/${currentStableVersion}/apache-curator-${currentStableVersion}-source-release.zip]] * PGP: [[apache\-curator\-${currentStableVersion}\-source\-release.zip.asc|https://downloads.apache.org/curator/${currentStableVersion}/apache-curator-${currentStableVersion}-source-release.zip.asc]] * SHA\-512: [[apache\-curator\-${currentStableVersion}\-source\-release.zip.sha512|https://downloads.apache.org/curator/${currentStableVersion}/apache-curator-${currentStableVersion}-source-release.zip.sha512]] * Keys: [[KEYS|https://downloads.apache.org/curator/KEYS]] h2. All Releases / History See [[Curator's Release Wiki|https://cwiki.apache.org/confluence/display/CURATOR/Releases]] for a detailed historical list of releases. curator-apache-curator-5.5.0/src/site/confluence/utilities.confluence000066400000000000000000000103001442004423600260170ustar00rootroot00000000000000h1. Utilities h2. Test Server In the curator\-test sub\-model the {{TestingServer}} class is provided. This class creates a local, in\-process ZooKeeper server that can be used for testing. h2. Test Cluster In the curator\-test sub\-model the {{TestingCluster}} class is provided. This class creates an internally running ensemble of ZooKeeper servers. h2. ZKPaths Various static methods to help with using ZooKeeper ZNode paths: * getNodeFromPath: Given a full path, return the node name. i.e. "/one/two/three" will return "three" * mkdirs: Make sure all the nodes in the path are created. * getSortedChildren: Return the children of the given path sorted by sequence number * makePath: Given a parent path and a child node, create a combined full path h2. Circuit Breaking ConnectionStateListener During network outages ZooKeeper can become very noisy sending connection/disconnection events in rapid succession. Curator recipes respond to these messages by resetting state, etc. E.g. LeaderLatch must delete its lock node and try to recreate it in order to try to re\-obtain leadership, etc. This noisy herding can be avoided by using the circuit breaking listener. When it receives ConnectionState.SUSPENDED, the circuit becomes "open" (based on the provided RetryPolicy) and will ignore future connection state changes until RetryPolicy timeout has elapsed. Note: however, if the connection goes from ConnectionState.SUSPENDED to ConnectionState.LOST the first LOST state is sent. When the circuit is closed, all connection state changes are forwarded to the managed listener. When the first disconnected state is received, the circuit becomes open. The state change that caused the circuit to open is sent to the managed listener and the RetryPolicy will be used to get a delay amount. While the delay is active, the circuit breaker will store state changes but will not forward them to the managed listener (except, however, the first time the state changes from SUSPENDED to LOST). When the delay elapses, if the connection has been restored, the circuit closes and forwards the new state to the managed listener. If the connection has not been restored, the RetryPolicy is checked again. If the RetryPolicy indicates another retry is allowed the process repeats. If, however, the RetryPolicy indicates that retries are exhausted then the circuit closes \- if the current state is different than the state that caused the circuit to open it is forwarded to the managed listener. You can enable the Circuit Breaking ConnectionStateListener during creation of your CuratorFramework instance. E.g. {code} ConnectionStateListenerManagerFactory factory = ConnectionStateListenerManagerFactory.circuitBreaking(...retry policy for circuit breaking...); CuratorFramework client = CuratorFrameworkFactory.builder() .connectionStateListenerManagerFactory(factory) ... etc ... .build(); // all connection state listeners set for "client" will get circuit breaking behavior {code} h2. Locker Curator's Locker uses Java 7's try\-with\-resources feature to making using Curator locks safer: {code} InterProcessMutex mutex = new InterProcessMutex(...) // or any InterProcessLock try ( Locker locker = new Locker(mutex, maxTimeout, unit) ) { // do work } {code} h2. BlockingQueueConsumer See: *[[DistributedQueue|curator-recipes/distributed-queue.html]]* and *[[DistributedPriorityQueue|curator-recipes/distributed-priority-queue.html]]* A queue consumer that provides behavior similar to a the JDK's BlockingQueue. h2. QueueSharder Due to limitations in ZooKeeper's transport layer, a single queue will break if it has more than 10K\-ish items in it. This class provides a facade over multiple distributed queues. It monitors the queues and if any one of them goes over a threshold, a new queue is added. Puts are distributed amongst the queues. h2. WatcherRemoveCuratorFramework Curator has a utility that makes it easy to set watchers and remove them at a later date. It is used for all Curator recipes. From your CuratorFramework instance, call newWatcherRemoveCuratorFramework(). When using this proxy instance any watchers that are set are recorded. You can then call removeWatchers() to remove those watchers. See the Curator source code for usage details. curator-apache-curator-5.5.0/src/site/confluence/zk-compatibility-34.confluence000066400000000000000000000037661442004423600275450ustar00rootroot00000000000000h1. ZooKeeper Version 3.4.x Compatibility ZooKeeper 3.4.x is now at end\-of\-life. Consequently, the latest versions of Curator have removed support for it. If you wish to use Curator with ZooKeeper 3.4.x you should pin to version 4.2.x of Curator. Curator 4.2.x supports ZooKeeper 3.4.x ensembles in a soft\-compatibility mode. To use this mode you must exclude ZooKeeper when adding Curator to your dependency management tool. _Maven_ {code} org.apache.curator curator-recipes 4.2.0 org.apache.zookeeper zookeeper {code} _Gradle_ {code} compile('org.apache.curator:curator-recipes:$curatorVersion') { exclude group: 'org.apache.zookeeper', module: 'zookeeper' } {code} You must add a dependency on ZooKeeper 3.4.x also. Curator will detect which ZooKeeper library is in use and automatically set ZooKeeper 3.4 compatibility mode as needed. In this mode, all features not supported by 3.4 are disabled. It is up to your application code to "do the right thing" and not use these features. Use the {{isZk34CompatibilityMode()}} method to determine which mode Curator is using at runtime. h2. Testing With ZooKeeper 3.4.x Note: If you wish to use Curator's {{TestingServer}} with ZooKeeper 3.4.x you must use the older version of it (in addition to the instructions above): _Maven_ {code} org.apache.curator curator-test 2.12.0 org.apache.zookeeper zookeeper test {code} _Gradle_ {code} testCompile('org.apache.curator:curator-test:2.12.0') { exclude group: 'org.apache.zookeeper', module: 'zookeeper' } {code} curator-apache-curator-5.5.0/src/site/resources/000077500000000000000000000000001442004423600216405ustar00rootroot00000000000000curator-apache-curator-5.5.0/src/site/resources/css/000077500000000000000000000000001442004423600224305ustar00rootroot00000000000000curator-apache-curator-5.5.0/src/site/resources/css/site.css000066400000000000000000000023601442004423600241070ustar00rootroot00000000000000/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ tt { background-color: #EEE; } .well, .breadcrumb { background-color: #EDF1F6; } a:link, a:visited, a:active { color: #316290; } a:hover { color: #001F67; } .nav-list .active a:link, .nav-list .active a:visited, .nav-list .active a:active, .nav-list .active a:hover { background-color: #316290; } .nav-list li a:hover { background-color: #DDDDDD; } .search-query { margin-top: 15px; width: 85%; } #banner { margin-bottom: 3px; } curator-apache-curator-5.5.0/src/site/resources/images/000077500000000000000000000000001442004423600231055ustar00rootroot00000000000000curator-apache-curator-5.5.0/src/site/resources/images/arrow.png000066400000000000000000000041571442004423600247540ustar00rootroot00000000000000PNG  IHDR`K}sRGB pHYs  YiTXtXML:com.adobe.xmp 1 L'YIDATxoTU{P[B_44L[JqJ'HbCuCbRuYƍӅAӘ&H`&&q1DLV miیv8wh{;s9s/> @@@@@@@@@@@@@@@@@@@@@@@@@@w.0C9yP(oɉ,=1mۓN>ѱV@ZFPYy,*Gɓ/\xqd޽{ᥥ`nn]z7luD@` X[ڰrJ;qDqgggxϞ=;w)..ˋkNʚyI9H ʻAoEW;vڵk-sss޽ݧ^jyyx:U5h4[__?811V^J}+7a?~\c7>U?^em{^yEgwk<Ѣ_m)e5"t,B;zhm[[ۛvbL33339# rO[H:Wإ1y:X'&O bƓ'Ç7LMMsNx~~3͚xF?ܫV6r+n萨?'''tk[[=K-(m᮲xϟ8s˷nj>ՙ%N}_ne5(,,Yfۏ#!ֺh,~ܹAMX(7IZ*;=V]]]M?O&؊dddllttk#GZ\ҫ@]AwaaRӁ*@{ X?l8/ U 6Xؘ樾w/\0аcQ+0hO忴=1]jv*ӉZթ}tjߩ n;l>!9\O]Auh l߾}X c |5wIpʪ7n5K!QR[Ϸa9;*4ݍ~yv?0F Ijv,=q&Γ]i9F3o lgg!OH:>>(ڪӪIB kGnHiE/_n}+ja5[2D:n=^C|"m%+cV]}a |",1OSzSNwuuwjkkϖ\Zzr<}{@ΫZ $vj΍:,HCCC9PEm~5,ذIwu{HC0766ݤ ;k;xwN?vռmvP>5On*K$}@bXwÕz)Ca#!VT,|ʏ}rRZ/؜vw ts @@@@@@@@@@@@@@@@@@@@@@@@@@@M(/xd1XIENDB`curator-apache-curator-5.5.0/src/site/resources/images/curator-logo.png000066400000000000000000000634231442004423600262400ustar00rootroot00000000000000PNG  IHDRZi$gAMA|Q AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYs.#.#x?v@IDATx}չ۷SeE b[FAc"b716"D_i+b4 "-[63;w.TA_=pwfΜ~lVGipW2m'_u{df GωZ>Gd4\MN9Y7; :i~O>?gp߭nYCFwk7tDӜ1ct?u`0^ UAjӦM룉؈Sns ttc5.ru1ekMiYm/?g=wd+f\c Qlp{F]_iէU[S,[%Ei1<^DIJ 5-{Ru>  ׅWSwWWWg^qւc2FeZߡJ7F玽oSh׆u(TVЯm~97EO+ԁ [r%izbP41oz :tR~kEc~Għ3f0QgsrM0>UU]~]Wq)bOw:]{f::MA9--[e'xO4sX9A:(.\ԷNhi,5B()ϫ{iC 01G03<7ԯb{Acp'8N 2tY :AeeXx<Ʌ VLޙ#']M/PT*bs~vP^yLKɘZ?&y@P"_c}Fǵ6>r_NHnc哯z urK{75GĢ]ƀ?g⁙OZ,54.٦n7 ;vؾcP(++7'/POD9'" $ulΕ:́LRl6tw|O:ytٺmg-uѨ]\}ĈΨ5~O:HQʪV">7pTjlD2@ܼ2L |-aSvSGߡc f:8#. qʫ-*謜:v 3O$juqD*HYpZ*Oy;BYU:W"5Y)N 3 jΉqo!xn/tꡍ;UW2O^wO/qɎYUqO(Wv 92-i,:G?nr΅[F{ ĜT8 ,zK`#UDoUv rB܆9/%/y=}&M 1OMuԮ8No0}ס~֣?m3OgA܎ LŌy|Jˎ|*'3m;o*/JLŔ3~??hŇ 볖fxcP8+9-5ϖՃǂ0 !1hTQp؀ϣ[ ̓֙,_9tDh1/\B,֡wD-u)R/^$z ׌˴jp ɏk+>xMu%?2-^0Zg\v쐕>ֆ!ĀkT1=KL4 ;_P#L4йv,LMay% DX&7h8-(AU<ޅ_!@.ݎ%ZJ,٠Y `BuC dŊ;VcW-{٧Š(cTA:u'X] +;#pָ5SPw̢EΚ9׎2F M+|dZ1@Y?^3^>.ߦ?q@꫃O>=ss9K.vY?RhgX 4̠$-.]~ kwR2Efvu1\R{t~*Ǝ;v9c8m^QsJr+p3 p]2DVPI&l\O_zeh8vIoNfnOw]OPm N /J2\&D(;VHRѰ \[Of/6Z9;A=WC9jXl_LN52L`-DsF4nѣK[zo0C0Gr_`q՗s@oa% 'QVUaa5@ J<1^ngLMQXpcWuk+dA+wT9]Qs\Ha1m úBE8Ǔ0l䠡1 k2ɑ΀q{e 8a{Y¾r\i4~,SRb[e̟#I̾?iÔ%V |I7ݥw(dR @d\ȕC4r䋯酻FӮEacΕq_]i1ݧvWU#x z\xO7ܷEmя\9r1ŭf<4'JX}]O+v -Xi$#%}ciԀwybJZ7qBS<`!Y>8gKi^]D7a6zpI"qU:O'}n^2M+pu(ִNܣ [>jDESKXj1_4t#90K'xc`0HDz{#5s1Gg˴jt̘rώ~b ʵ7Bj͎Z4wiH=@A@ RDr'\[M%zR#L)Z3"490_O?; uWO"y!hə8 TXŝ'?W{5ʞ=BrZpR^v֠<\erwU],é00ĕ32H|J)64Լ i#ؒ%貚7:Y <6:JJ;\ `z"VkC%-q(s?߇XB} r@Sb'ry9Erk6K[r0u!,j|?=vm fK/ ܠAdx꨼lwI]Z]xtg_#yp/\ֺ>Xd];qZu 2' ,KśB- ࠪuϛ+@NKlwf" ¸KSwoK %ݬ;t( PܢZn*s؋hnV}<кSXXި3&=,p\`^N=sQ4 r;CC_^qNjg5snAJ; ˓Z蟾ŕ*;Uqnt,g>K6_mWRl--|UzOݮJ@qy.XSJN67 +6|F#lCNAԢ"14\>#> 4 |`N:ͱn!b)-טE7ÉⵐAީVҰ v8ɴi 72p+26ނhh_~O  GsŅusc¿OQ=ˀ<;ri]?YshhlƣcҺ62tVH|Oe>{yk\9N긎b`Mp03wC ҄'O JY^'V_i^!n}84zT7]v}yl(}t"l!wNx'ͽޟtSĠl` DB(\Ht{+QP4@'lQ9R+|WSd,`9tt$Z\!aw?TIrb@ӝ%ˀxP!مߋC&?z&dVN0<pTkIa;4ƳqJ Tσo ;s` c&u/Ć]_kwc['mCoSLh0N8 JpBifVL{3Ykk cKWEn~Cمᱼ3sơxA35GhSvrw6H v"뭛Z,=eo,!c۞ ]Pwfð-{7J]ۂ5[%H`nDC 3@r5k4UZwͺ iFmUѵ J dN?S\[:#^ct;!IGU쑧vG֬J|SJA֪1UOcIX{@mի٥ T;1n`}m'Bi0)7ijHx^Jlj{qxyiEXi?WWޣ*%L=?nۍe92 5nap @'-暽7ap^-xm8\T@#iJːȵ7\(P+)F33;Z _ fOXٷ[ZZ%)U~|l&}BES.1#νT$K]S!ZgjeSy88]S|Qz5c~ &Oou*Pk9`N`Z8KaLTG ɥx̓r@Muq)[d)8'/Yd MCE&%(a8= B⽴X5Eߴ~ä9(s"} ^&qS=94d>-C^yE|%J㦏:rR74ͶNӋ&̹u+Di7N;)QisuҿI]ʢ(91>̯rW ;'O+]g$ ݺ0HJ0)8!<, ̹x6bnB؃:IhBifѲL FeR +RNu$҉n i,Z AZir,q6M};F=?4nglbޏtX=˔*?wG=ua2!8󦎩JПi¥&'nOilA=MNsRH 5Ly;7, WgфY}uo` *&TqTv3|RwW[4qimchc dW6@(~8#:iӶ! R t\Ή&O|9u1 -Odu4sA#F*L?>EiGL,kveWttq sdX A *:5g ICG"b$p$@IAZ$ÊD!q=Df9\9agß5'j؉(H\KՐ{C'p5HhO T@l9m\-  C[N!@=9%j|8&Zp@%0"ȟI0%&OC(ƒQIvQpZܧ|x3R`w|liHB>$Mmx@!L˹Q"4}NJ.9a༦~>y^瀐Ag*A 7i, >0`aXZ:,œ猂6/ƌQbtr9~{)*IP€u uj6)>&(9PaN tBk8E+aOuuc=($( N֑uwxjD PhP+Rk=KIi9k\`f$*Rvv&AΏAԟû Rq@%tPMj2G Wuppm.m$W˫bjgHB' 2]4?Ь0* :rz͹g~QLy-_v*xMp\&;V!| W6Y>Ώuqh<,1崩x%`S&dg=c,.P+nqҨ/0xl|+ oܐ.=1YׁU,"?H 5V#ߧxbLz>80fyYt!>)PK\M9  cSbEm?H-chf4LB7Q PNTE屐7ln%WWkumu|qWȉMraq4uej3'#݋44YԶVyp| "$.ٕ+; N[sTak!?WoDDH.e{wd0,b4QFBl SN(#  ؑ/Qo:zLdt@LLt=0fGv5 Ȅ1e-uXInsQ:p/% SuH#/6pP*9&5.,l:1AOA jˑ?cn&!MXvhL%L[1׷Ltb؃-Oǡ r3J1S_R3v垭7%y3ښAڪ{ꀂH}p(W?v[ȂT'Y7p@6y^Vj+( |aL?؃1e#$mIԅe攀dY74|yj́/k_ l Ѡ83t}+͞,`<߮8́7#P`0Qo,K 2g^q)@ΑBP%5F7=>Ag3^Qn(sF/i`(>meE ‘>%CuU ^܌2G%7$%TM5t> ^x)?b&1 $MX!j uοY {Yh D@ 1";_Ob)dj\/X$Fx % Wev[_L|5[?zczkU >#\r$jh ۠"g} Ү85MvSQ~%Mf6S1yvǧr*B~{..#+69l@av}㯭S?B2O/;5xܬF# hCmD k1!߂Ö.MY5kevYs5*tFSkjH7]X(qT_-$|1;Z\(rZpÀ pݝoyɺ+Ej77uQ̓ L'=G n\Y$̗m_Fk/s>& "BzYP9g#-'7vs{ i(U4V|2 UkfvҼgxQA%}>~:RGɵ~~m#xXxnt a HN`bFw?c޺e~ n=s&DuH?gb9S3) OL[2`# K.!\ujڲm;ew 4FH t?;Njcvlƀ؇%?J+tUt\+OU=3<ژ,x{ʌʼn U4Q\>^_u:_fH\+ 98g] Іrz9))Y,0inYJU N]6%5ГD]Ɓ{cySPo\[e\qiâ[O|ph&(#&v~xc7mJI#~-Il6dsRZ*6ϣĦ7Ek_f"pāu Ex}4!/8MU'1X86mQV;hq$A,! Dfqp|ѴYD7x>_ޮRUL3w\XDc<^x&I-9ijJywJZy\m?˟Pb ?8i}-5x&DM|_UI=쥢ysGy֨5 /!lJͧLFMXn5 6c]X0l=ڸi3۵GfWY8I6/A~G7TIM)#b 7$ɺ@zˊE6I"}SGZB.n-J$ :/m]sA䱍aLC8zľgJtב&Y$(ϩGL4-ԑ `ŭO^IL1_P{O!Њ;vp1A>!}I sz#2Νpk߈YCqưɋ֚ M6uVB 9(J]/6 #7j:Ou :03`CvJZvys#o A];gpI]ʏ\p;?b߶Dᄭ#]Dˁ:UglO띘BTaDpE9"gyㅸ)pqhmDv:/7$ m]pZN埞{/?p sRqܛ3L4nz|U\4SHQ(JaVɬݲ$hؘ°D.FX@N;{2WyrKu)#mv٬ĩ=5P]~_oީmY6㷭Fm]vⷫ:}n]c\0n.Z.߮zV};FsF_6ꞐW7`^p^_߫53i=_Y9 GKeKEl{/_];o-\9΅z8J_j$4An`AsЎ4O?cm ,l#T #c]Lhl`la7c# NTL*9NiQVې.g!bVA1~S$nELQ\F1眊"$ahwDu.mxH1iЮif;NC(Eٲ[.w Y=ʏm?TX%h :6y^8rB\1Vl g^@,~ 9刈bjMkۢMگkaȓ cQnk}[^ΛۈԌܣ keo/PRo_x[-vrrmS>#-8ˋ B91*}vZxcHX7/><|ƾ!@tA96T-9 )$3hH}'*)~BS#L L>NB!QÂ)'؞r2ޭa&c9-p"^*Ҋ/C˲ZI!.>,.}⽃K"j!˴5jXNN{xLXuo ~ԋSZo `pnT6]G {M%.7imF)<.I_e럝?vu N\s/DEFs|xk SyU9 'N@0%+WBE~iKv/}Z6ő>6r'c4{ai2tkf|Aj증4=DIٍ{oiWo+JzxGnԫyc000!h5^:QSp=2sZ.Llz#r䣀ylaAhǡ=}0ˆ ༑BaMsYx"C3yF#NĀs"e"Бv(uTb]RrRu"|͹~IVNSRR=z<+Xez1a8%_Q:hwt:A10AJa;!կ {?93g^0C`k IMs;]}PMaRڳ$<b? yn&rx"/]~LPqz<8ƅK{u"Ӈrp‘*KԮD#zW9QPA0@]z:Uy@T@nId<*|37\T`IX5dKb!XDaFthfV;FپJ]^PӠlx*M._w6G-CR7)kv)x4>\kpjKפ~(A)%R^mNO*qӽq莁FG<-F\%"TrRS+D^sp/b|#ࠃr hg`,f슯 b_*iET#AFN{Fe|3o̕!~}Oq[A 78 daT5XZQ_ׅ']2coa# ɚhiI4a[s+>{=q͕k޿]n^r@8@xrrVpľԐg% @.%#LajT`p@DmuK(ua:Ÿp,n a؜ԛ=%he5ngNXNC -5W$% ?hc{.(?s>viX. %;N >6% n$-d=B\Q0 u$:G+q \7 C _o}zų ţ|%i9vsoPLp nOJE9~Z+W6:GxGՎZP`u~xьGJ@?&͚ < oIB%Rd#ESdAAKP)=mo6r2HJLjz5's|R+ȍřq3l7%7>zj:zmZhɆŞ THm+,vtƣm^0qE]?7OsY<`S0.ML+YFlꖅ7}A)ӓ;SI_7 >/; MDL|^E]n cz$TEg0a6wiHۃ 1N}d5j`Id$ko`9{䧰B%Rҋe?K2؂{)+ 8oV{cJHLB'r!|d"uۂfT|8G{hNߤ)!\IDATV(ӳc(GOĊn }+mгM,`eߴd{^Y\˃=Yp9p D] ??70Wk01')5*ygFxQŹË]DA=+qWhWPgY'r'k8X$mhI.,9DtJ| y2. ĮsWTCӈy$ ȀDn@]VcxK쥆9cFqK~xgOMn۩"l\ㆃ=G x(|/t~A4цp)vKAt?2"ڴp? %%xw!.c~ \WEAcЀ;`sYw51ued si5 $81R[]>|1쾐$Hd4Qy؀pEm(nJȝ%!"x>)qn}m/k|NS\sQ9~8nJ6=IyޔJi4 %H_DX~0gDc|x]Qs꫔/09A8vM,u㺰3X& ݏ(ZofK.1D%;vgi5GṯKbK* bA念Yo c!"e4w[yGå 4h0r@̚!Ri%h%q-4 2O'U FZXUo {ʘ7څX={|߆趁eGBYCو=fNrF0muW]}qywn'F2dN*N= q֎h146//5fn{A?nmr@śOYޣUaAE1zdV m"%Hǒ-hK@9̖bm4fέh8а%6> 'X&uuk jY pPU NtzF 4,&ҎqM9aKdVyi;xLyz7Xc> ?DqH` >cI! u32N/X=7X0f6=EH%rlFq]EڬWllz+uʦ'ni/n}ڷs2/^ GmhT g qc/0а(Ґt "bh7Ibߏ[n(փ"7bt3{{ڨA:TW5#Wcy!p ?bo8PK;M|0PUɊq!{hBg nY0N۵uY~I'x uqRI [gTfM|Cd~]4q2pd*H/έ gY_I6VPP吓~рKNp80l 7r**.mYUsxXbÈ$nL|n1Z7份F f$i&, g%cfջJzγ|>>ظ^a_ dԆGoOĢ׋5mWol|j.n^HT5ԋ+CϘll^!uyIj&/:-IMlm|xWqr3hw]aK9ʪs_Y7ԐDnL 2#>a*t: bIQJ1> MO!#Bcu7Ҥa0}?=D:/ X*,˾%)nyMppN U c]8 m`*p,>˨ n3ndgCy݇2NH(*zbӑ&۷8hR8"5=^?i8,pfOd*Յ=XH ັo8`dRZE?Ͻnp`M'>)3 bɾad3fhtix/q14m _U߾ֶnBfSq2Kyh 1up3 c~ k o nNvX*> L!yC.gSgԽJ住a(u0_QeSc}<ԣi {T=:/ ؎"/чhӼ)4ob/%GO3DMi2sl]owmUX)sw$pu6 >Jpop''v1JS0]9{rxƏ不$AHsR^Aͩ11uRGcX՘OnD3tM?aUYhZDM/f6eh> SedrĮͪ%Cy~ܧ=0 rRsܳm)'D,TkĴh0b#zwtyCrWEd-G%E̚ME50bY`N$n d8l;NC ZhOj~hItl xHO2E"P~;ؼ,7}]TYf 9֭1*nz clXOΛ`@$c-p!q =UDGŚ mb+hwJ7utԏ޳:gű*WI[2HpR[eR+K"7T%c'VЮ X2xZR)ur "&^20&-%<v@B`uU4 wv<==ޣ!Eh>h2 +`{lA`=]F똞v^뾏7'F5xCh]&v%Ez㚰v`5U'qSQHf:!hq!?oaۻJkO_N, j65Sl{m01z6)!3=2MQ{jeKUպsJ]$Fq`5[]uiSִWMk8`Ǻuv[JĻrm[CQRd dp1?ͅǢ܋dQkS眐Vt&u.N&<6(-ʀ *j/,}ցĝiwjf̴@z {ea t<*h,=1:ER^(y|PѺ 6ϩcS2%g{42Kxäg]Gcu{熛+>5jيAUF`iF9>mpAOL~3M_M  HCZ< i# U3ݣzVn b 0`UL `CD5<0Gܪ =O; XzYPr=~p> A{e[^AzokćV95[ێ*(,?^@8 axX%Ю/22-p[`Á H+ڪeWWmmxphsb0y#m?G?.}q,P >оZbX/IN@a2?Xrλѷ7 tىP P]7wgj#m^c3Nbx\lk'm^0n $9p}2L 9[ O\|iK Avh-5&yi9|5v]vmC!"^ wsLOf^v FbD->_=.NA:]JYpml[ nfg:­,صoOEj3y7^JZ3>2-p$[Aȸ9[]4 [ju0O6~kTE 3,[m=5Dz=xrP d[~̵oal~cgxip8 ^91|)&(fgcy Y_̼!e$l5qN[@얼)'vӮ> )ia0 oPv7xw6gSgy;# a2\/Jj๪cǍ ܫSR萻vG71A7q*x0YP_C:?˴PEurod4Xtq+40jyQU!9da7vkm;^n^?dʋi y{|o Ër({F<ʋ5fQWEL062`s&pc 6#9#֘K +v>aeF /(5J } WΜb\[vB QBr3K}k㩆8^tJ&?rt2'U7-h s:ۍH"o Wlb@-5]ɸL |W-p@ i\Q`'k׮%_/P;7Sn4)Z}{]woy5+-JMт>_CZ5v@jz>_ 3 )gxi]cTgzlyln}̏ia v3?n텼5<|p_M+?n[8iIͦ>+ܯxŖ7jkڅ8jq%6'"9;ABdy$nAg`"yx}osp<޶J:˸L |Z1Xf{e]pX`Ԙ"vxɍ7o]n;ilqqȹT~ 䓖}qKaə .O3Dfn[!OG{i#tăȭ3.A^c~#a lp ZKG[!n5=m900 -qi2 ޹y+,,2Bfq@S r_*yǙ/Jpw0k~3 q=]JpM\ʈ^aqӚ:?u*(xt@A tpNP@Bɑނf-h#Xq`:+3!WhYWMV@ x6\wa1n3Nՙ 0K,]+Lkz[~9B(WIV&a@v[a 6(gc~Ob%H.v1Lo8IENDB`curator-apache-curator-5.5.0/src/site/resources/images/ph-quote.png000066400000000000000000001050031442004423600253540ustar00rootroot00000000000000PNG  IHDRWW DiCCPICC ProfileH WgTS޷$Л(JTa,$Pb VtT6:2Dݱ GTELBoZ7"Ah4F8S4 Tx0*q'+dze$A\..K@<ҀggJãF U,ILA다 \@i\-PG4["#SN|V^BPz> %Gc84 a^bX4 8(Qˀ Җ.o ( 8 HQYdObdȧ`h\*"b:BZOHCҟ%cP Ezk꤇Cp B:5HF61Nz`_ўZih7hm; d:E@1`(XhC"FAiTҏ<I7IrsG`(!m_k9XA?J5/9{Зߣ|HAI|O"'Q DpUuhA:Tii(Wb(7@P<?*fI Ahd‘#.NnԚNjw+]j9S^ #Oor/RQGR70т ``rr@1 Q%TOY0J jXa삽p(3p F;tKIG4?#$RHs%zrYK6ȇd7Ʀh>(ZmVAA;L;v[:Σ=ѿJϦϤ/o7[= Àc2BF cc*^MCTE-\-MM@BmqjOzյԭ}cE3ԗoWoPޮf0l|Z>i=k s oRyk5hxòg&Fk6md KS.g$'#Trj9W9/4544'kkVhҼ٥e%КUuDV6WY;V;O{n: k0Bm:ts 7+~=mץFfmsK֛WwLGyQ\2AMaÂ-oaKh731XaPgpߐ47o8piî} ?87#(h6KF=&ruƧLx&&&LtrMMLO>f~YlYYy-^,,-M-ZβJJb;kEu66Q6656lٶUv^v9vJaCU#n9k䍌`d,GZ1/NNN۝:8q^ERrݕ:׵mmwXEM=<=<:=-==7xZuΛ=B>:m3ZU /VD+v` uVaQeiMמ.~i38)4lYg:1i܅sE59?g /xmʷ [jJ8%[|m^}b)^(s*(D?oiҖe6-/-"`Ůr+Ǯ]_U)WUl^\\Ӷ6fm:u}Z/Y2r 7(xuS}7mEֈUUۊ=ٞ^?V0QNζ]񻚫=w^V(k:Lseo}/;̓ysb3j$umGijm8_v5;ZyLز (>(o:uqӔ&>ig _ ^@= j';09g.NDh7. h;4lB aS@]]b E0mM6`4|Vn4(o η,ΙsQj pHYs  iTXtXML:com.adobe.xmp 716 116 'q4@IDATx=`EӓB&zwPDQ@EQ '"(M@@Ei -Z %eW~w yw;;;3;;;v[=%K@.]Y" "Գ~%K@.]t  &%K@.]_,ݻaP\90PtM%K@.]#GGG\dddŋI&`HMM\2xzzBzzp>X%K@.]w% #G!d%K@^ %%Ew2D]t %K / pBV_p0;*+VL8K@.]t KŊdP߯A.]t %u &lNWub=M.]t %7S^%K@.]ODDĪ#%K@.]t'C]t %KH@w2Xut %K@d:K@.]t < NT.]t Н ]t %K@.'"x"bՑ%K@.].]t $'%gE40f ET:^]t %K W Pwl>e9ϖf2PbgȽ΄w;cJ}2^c"NG$]_{_ z4c ]*|©81> bЏ~DaMNF&DApĎ$ 4Sn-L6L4Yr'7)7eUN,I֝4y?*;X&1@[c ذ5D\Ґs5\y~Bw 9#sJND^ 3~ !)3%12hTզ4H'i>i4J7:,۝hyOf/Ì;x8k1, KO]d-0fm6w-&='d^1*<t "\9WB/+E!!M8}= GW|!&1] ? \?y_|$6D$7tqwx{pZvpk(@pp0pݻAApm S uMU6^>9 Wf#|0?s?Vbr~X cJ߄ 6>$O¬>4Nl2?"پ"nE_ 8'@&zfR|_Bҕ!?Ʀjشx!ahH2}ux+*!*2,QnXغq- kn߲ӹ3T&zwg'HPƵD$rC?Sw~8{8vJ\~>~UhU)QϣBHGÌ/WmYYr3}9\A2B&MZ^ugdHmUΏCDo}N7OC1*O8s8.~ 3nΚ)ݐr֔ v G:/! ~^Cb@XI/b 8<"F^[fhP0ҩL$5`e*a[v6l? M{td8qO*UFqkPYsE_*+EeЮ*T=f}M0Tl!>Bu>o*4Pȍ>dxE1-Y<v$@qdT?q{ⷯ +lȦYdmt[JNgo JԽu#'? ?ID4}"4pM\ )шеSsa+xRR; KHcz4GW6&:-"w̝ޣE +Mօ*&GÛAD"B655U eFes j$ͧY5{i9Py"ah4 NS&& qv|7@ P:kEIN;,A0|h#jÅhñ΅ =|7}*l?n O(\JA} "'HWei5e(,2k?9UeͩuǭHpg%y-I<} &P-9Ս@#ȜNT&2DÆ='Gu*^>nssW &<V she|pt>t)W9R"/Jz/;eT*)GJ/r&wxA*gt\>ea~fWx6aspvWe7%$D]37s~ng lD"? /_Ja=5)_L ^U-iT{4v ^V\G5zҚ3A0=b5cN\̯c,@63cŲ7˴T0ДKbŠ apS:DCɲP!RxV)/6(ʜ՘ Pҵwwe ʕ WO7^eQФ'j4Z2ɍ;R.RB**iX eSFH4Q8LKj4٠i޺NAMqƌtYOP +͕RLBš|f)5>BiS*Ue8 4HI1$Eo[]&r/2nh,VJ9OOs.HdcZGJՠD4FENuO2- K"Һnr7>~h ]S_3r=( %6,~}`kàaP0١ l;Šq%9s@ P|,!^0ҿ P!`Wai֩LmGgLYs2R  \/bn&JsvfVFR $!E0#v:SQyꎫ >8 C IEFr z|AF2Ьu P3&*hU ME>}2H6ŠfвE(ZMnB.g{Axǔu @o6 ܜLp:7fT!$E?εK$cE ^YrD +wݛm|vE odafMI9``J)+/>MiFJm 4V&=vkkz[px,+W#R 6f4-%V"E:2"BDWĴ`t WdEh޲9T.]Vm BPͽ0$~kC*os}[rIR/5B zeD;֭[&K<Cݑ t4iۃݹomk@czğIvzeLT|ï6$Q<jX4=c_n{`.m~Qx=A C%Dc?~=cWCEM$aT7$X%j|@LIlϲ`.Md%r_|lߴUN[|n؇Z(<8f8w돟ۀ BTdڻ2> Cj|[EQEIq!z}ztOwc>1M(7 *L~dq٤g>^@wawO_=w~!QRzܵ|ΓD!S| 6 7@-O8U䙾%0*݈awx9{? Zӱ&GݛuU+2Է^zop_l\]SuW|nخe'\"Q Z{0_= /[!ލfF%f6維p]>[U냻ߖT*ثq/z.@Kco>w+ˤZ#޿q ^M5kc"Sz={/Vo8 IoŋU;t(SYDF[c.f}WgSH)$m:nz/%ʪ꽅 MRb#r>'v !*0ҧ˸f ^bLjAWN㢙_8t'rL1%G۷KQ.!fMEnO+i!9Oa?k$PgS'>}b_cp hԂiQۇkuӾC<*{S%AOus z*c[-gѡ۸ky\;,kS:߱[[4$nZny |||8/7OP2- nLVvT5$*΀v|'򎘱#BQ#]Ha~Uu ~4\[U7/F/FsnriT('wb+:yW%G󫭇QP@NJ2Q{lc>5cOi? g~6u4)JPLM~~7Mkn\e]k=!wڬe@qHHxkxf2vhNi7pzL .1,`QsCnÙx 6y}t_TUYc8٦fX%u]GB%tdu.Q+8}~x9Ϥ%;1FqCt nX09|!#i(ljcHCeJ %6f-vSrĹt 9͗.rF +LV(#BS'ȗbHz ڍs&eh9s<~A?~7ӐĽGo>Ē_x\Ӣ uD>u˓q=i(^O cq7$C?(%'~i;x-8^v/f;.i3n,;^G_m*Mh!v8B%!ܘ=E9?cFqy+vi^ 'w-øiئ^M^*?-\Ee'h4I-G 댢+]_IYZ q+===d۰\^v_gocT?>lxќpU6qֽiXi|y1ǰw_DGq킷EYMO]Ǽa Ɣh\3Yuo|=j?K4켫U+/f= {y0<f-yJ/^kx#FP܅O7昜Kk~ skg*--1.|,'P{G$L}lkE.•g({ą\mKhm5f[=iybܷm >Ls m@e[f۱KSNԭCCU8iVImIѫh cKIZ78gO9ok7LF=bP Aȅje3o3=kiR}ְٜ`b#"l&pG]smm_ኳ؆Q>|KsAL0ƈdyZNs2쑩^;qKR߬;nwOP [x`Mxn:uƶNF&aHM>fgⱍrJg=Js'tuUNSw})8YI'!7c8)ɗa8)}ȸ"Cae,2E{ӇsV|i?vd0*AC4DϵمN]%R&C ~?'d[G\ǐDЯWĿ`?i %vk4i(v1ƫ(W,ֱ`6l%?-^CۇVgQ|)g+iI6qXG&95;b#bQ(rnqYlxLOf=\1U1a~>2_,Y^i$ʁfo @iZmE(dPv1p\Lmo\Ov4ڕtq؍qctig#. BhF_"m5 D:9<p3WBDҨ)5x AقNy5 $΅xez@!#-UHK^__:ұ mMh`'cO":@Zضc?lrxg`pn&mHIlcu"cHk0e-tSGҰ;+ZRz&C'ÑSW 1MrO9I1J1id`w%G|o1wWl䛕^{-x HMKe:X_.mhVy)96b2n$T*FHO`b$ziBB,]c_6gZ9Z]wϰSy3i$Ļ!eɻ5^iB]١rhI8eL߾~fvMOY >VT&_+aS L}:զ]IT6EfeϹ]K7!55Yn XFx8A8L|qlȚW`)%=6 Aڟ`܀֐NV얬01%5z5Մ%x-O} iE`ֹ"ADڎC~B& iCt~ >4 ^FPlI +9r$  Q4(ڙ m Zʆ0u 5 եt;x;гgp bǂϣ^ήG`>תւF oWõ7IS\Հ.u 疱:jat(G- L۰6u; 5!N 9{X2:ƫoXt(hݿ|PӓO#Qhhy(rϏ5R9iׁ>Օ%th;GQmCU}|nѓ>Z)MCѢ ;fŐQRս p=0v̳P ѹ6n.ДUxp)Hh)?2E c ʧ?AF/`ƣ },mMwA R;D#vhS>A"qh7+dG,3Mip`?`W[I#Yntpx8Aߞ[6L7ye:KtOj|<8xDKl<]Pzcxn0vD? }è@swRRiWAF_Uqqr0Hz B9RIpG$ePg@tBCJ[NuxT豻`@v BnqFHׯx'_BKo*"@SDO\ :V. Mۉ\ R"ςL1Zv_VJ(dhlNY5Sx>8-bB,ݖ *щs3 LL0f][o ;A"APg OTpy02D6<[%vW}p|N‰K{lj{sn,E@&6K 7jZb%i/C/d7:)gIFp׫>/e3ٮl^^B#mg[s靏h\"ph~Q_bCnF$>|`]pUcFVi b:,;V18:*7(<獲BNIdJPeSD(FZKA$cwdz 䓐+ͪX#HN/0o2jN<3LIo΄=D[a9^ hA"_? #\H$Л /HkQ ~ >q<\9c5bŋA^])O Q_[ЁL}AŒdڅG!Rq[g_COL pxvx{֧hfAt"{E<0MgF)|~ bH w\XSdֹuuPTCn4%#a_ \\s#͒LC^dhޘV0g^Q3>joh߱3ټH0uA2 ڑtxq8E7]paO.yT>MZ7 |nNL#T4&+ap0?^=t֭yw̄/]mkào{w7" 6*AeYs2!2*7±ŝ!-3.JąxBbܖ?C4{evm\3rRi W (vR`à  @^;BլTAKUT.['VkiƟv|An]2Ţc7zNZkz[w!  8u v5BšlyY7 (NB|0|;ԯRu"P8n.m4} ];h094H^uy[w )N'-5gCP1m >z-hҨl:sKz@6y7nP6$4s n3 `ˆeޛ;NLiܷ#Td#Ans7^uݞjO;ܹ| } n~ĈI),rBa\~4VZ![W6i*~W8qWK~%COgJ%/3v#b$J qJ5Ð\ l'-RFSH_vbK,8khHEm5?j6o~7$ܺ]F@=wk m"TCܵLnbٟhxA>?~\Zy Xi< ȉQ17ϗ [;qtm%8jZM- tI5!/–&ͬ#ɫ04K!qΌ~5fǎb߾?a<'p7',evb8i:Ԍh 4 x} 3>@mDsS~DH4,Z;{ n] /l^ R2r`WC:*Wt/ۯ^r Fj#eg[CT꾂oK D[ SU>erfO`}.#5X6F!l>^sȼJ֬m=(p\%aY av˃AS #SiK5`^ dY>0.xz lt <(t+AOI!ÇCJ~cW[Ym{?#7qwՁ˒u&D|N(Ӿޡ$KYPɈ?4YJd*Ϝv-:j FJ1 )Ѡdm58U7q]|#<m?dҐ'U)>Bkwf˾'ѽwdֱ$NX5RJ~Sf\ 'Z?לnr|JW{y&#@;\][/ [k ڦuZQkp@nx jKb ݳZxkr =‘C"Z՗ {}}ޟf͟r2ӦStt9骣XghW$?krKSv;3J|sɲQLxjUtƉqLBR2O8,hJ?///޽XzBS<|oU&@p-7F Cuz.-*NIH4VDi(l!^0I,UU4 !*HnYfbJ(հ#oXIL/2Rq_ާ ol^8NfD1GzCmᩦ5iMc8@߫m0)-;< [֥ Y f 7QP^S,#s+Ͱv@w(Ի T+ Wb]ػu=҇JU ][C+ţhrb7>4._dۢkq߼T.g*MSzVugDAX0vh7(FU̘O H @Az!yrvyQ+T0&;QШI} -q6ܣ-xVZzljw|?k63RbMx" gj[Z4z#rZDF@Sɢ;.E+@Y>/Lx!P81EkNH-œP~3hP$ܾpJCŧ9[r\8\nq]7T`ϖMv/9C5ṡáPYNB{>Ex h#1iOɄ?PӓA-\ zumE٨0^+ܼ>>m$hЪ ti {2pz!Pޕ>Ӑ BBhGD!Z+;H"+1JGN55/S q/[Z|oIbHaݪp.0;Cv}aԈм^Uz dK$Zݖ`e: es-?O| AJUg5T2!ˇ?!s?@‹ô@j)"DMjw?Rh]Y^TkZI-ݾȬ4<ӽfk(Q4~7@f#"'0~i-Jn8ՙ eS GkW;IDAT'yEZ ` )<9]5]ѐ*ZZ̩,"Ю6d٘-Ysğ >ˇ8k!gR̒`@%GVRZ̧dickr;KGXD;ؙd=ǫ^KrxL\c]cy%PJ|ςͩ8}6fY˪ 1 ~,cM,qlnT3+l)^Qp PdișcNFj,\6SdpIԂWK~+L5=4Xfϙk^gE"SI 6(-MOo37\Dì2.ϒ8c@屖SxlrTBeZ%إK[jKIRe#rFTpޭs[+fI#^+h7݊.^!*sms`rGkهR.._~Ә:-eK]y2?TҒ::27K処̵Y1sL| |4&{T'+shQ\ȟ=e]z䛵N {ּ`<=tT3U$6>N:0ɲ$<љOxL.]t xd :1y$V ?3\zF]t %𯑀zD/tjt/ &Dw2 ,2=.]t x utZtм?t'uu %K!^Gg߰nIJFw2tu܏Qr5a>YsvՆƣw&.//g_'\`!d_߶_ZԺO׀^~Pidzxk<o@yy%L~*(8keɿ>wZ_΢N#K@k\BIV]uE/7"g߇ttq8$ZO=)%=! EV2CcZb4EkK S>kyC<7Rj3'IjL30mKpRpO$hIKM?n) w>=>?e+>u@0ǥ xhqFA12gh\hx~Snn0\6w |n| u+ї)x͍J=8j6SFPVQy(+<< ΣQ|YjΘES~8|qyY(WNbCX`нMBB'-kWQ Iaptm!R)jC ̙Rjq9WZCh)%_ ׄn`H~ª)#NtԺU+F3̀4.vME1JƁ;t:qg@g:s<RxtGWK'ik9&Ud2lLT(wopHrp!'G9#P\=p g;N_IOIhL;2VeZlM Rie2X9I$AzvҜPr.ߊN6R\Ƕ} ǯd ->@.Nh\jBHEtf! ?U $5Q*J|pC1h9?|jx %nuvnMbYc!(/o1WVѓXLDI|6=J; ,okGBӳ 㹹 GVŶnXJNI삅~ܤ0-M5iQ]ؕ9T0}J.01hxTLcN[s.Q^y{& 4Чs)˓X)T h.Y|LgO@8Tàѥ`rR^Gtfa3Av %o24=NK9rw`Ȥ)8ii!?.Uj?Zј!ꍳ3LP|H~NzRÝFP,_qZ_{KtD?&2"ٺp#1&|$@n`i"\Y#9¯\1`*ނ3kܫ:%<w zu9T¥d¢PܮְmGRA$s(+e꫿"VEP-8خX>9ԽOF,nk\7 k_Xvu!>R D0!r5@s3 N)$0X CX[H[ t'6 P;)ioY]Ձ!C߂dz޿GNFm%E6萑 QL#2pT,W\Jȇa)B߃h2@Q P+%҈49U(CDs}"A]UzQxf \9u΅gzA 78u0>vvХSШF%H4L3)> ]mN)}h\a.5 >@4%aUD ĝ.@\S"Wg P n= ~{H/} ^_i4.jY~T[6[=E7%&3{k/h,iwCl_8 x^g2I`J Ư4"C'8\KD9ߙM` R<}dIwsGdWKy{o8V{<;`'/N6m;|?(ЮfV7̛7O<}$nt'JZ?'MMo4'cA鴘Or >/|;vW_=/4Za-0n'׈֭b٢.Lض]+q?u _Qd+p<@=hSS:SLg;U୰8|xxv0ⵃt*<' tǜ~T[v2O{iN[!ᕣ8<]qFYߍmD KsJ][hՌS=b^H[@S0oi"^?`zMCڃLŇxG:^e hcbjqfU7a5Y=oZs-xމ8xv2K:Pn _$=خ˱j>af #JK\5{uqXٵ \t[C\2i*FgclZQJ9 O\ SzclVuo/1Yw!\sv6^val7 fLgL_Ա[RE A};:]ԇ?aDԭGfxc'Zv>][S|{֨.ZޕtUlȢ .V̬gX,3f.m2wQs_0WQA1A6ٙ{/b_Μ93rfN]Ka8Y /L\T@4"۷O8zMZW!(o wH[ 8#7wѸu[ѡ#:"+ҟ Z*=hD S:W'{kOrL!â@BȌs^S;2Ċ_R5UoEEZiϕt!mtgAr>m;lKRĒ)d|G}$b3$qVAJ]4 8ϊA+9%mfC4ܧ8.2.c*wJ"EaQa| w6@ɍue.)E'bjt׃s5ڈMi:8:NSGζ?I&JIeIPDY؆'Du63|zR@t.%)E_.'v }ˆGCz,B'y< gNh_|^P:=zGP2Y-|c&SȨ# ;^g4jlhա?Kӱ  {_xS{R v& ۹l9@iw`NFEebvlCFeN%#)'XkԐ _ٹDB&:>kc롯DO? mEWNuo[[Gq5r$И4!-b'^5".ð Ak͹Q# CzhOjRŇ3%"u4ՓSoUFcXk' 0s0ɑ1m_XZէm\J]4/4[}@=ި@ҥJꀶR3Ԝ+3u'ѽGSET)/G upoVhܽ&\I_⧙3m[. q{(~ ŝχ >]=}:E1~?bM? K:aUe-{Y^m]QG lr "[k5̈ڲ\ 0EO]HNG ATw˭;\>bEdCв17;Ҁac~i !6I,;Xj41'ixol8,flh|J{GI jt(é&uA>嫢`|Vɟ6͇pKm1jᔐ"~gҎhۛ)d6L_wh\AѲ]0^}~~fG67F4rϧaS4mp֠F~g맚Ó/!.)ɕFZuelcQv+{ᚪ5,ܴP%wvk}z9 Qá*/WbIj0/CLF&`cqyՇx'}߆A5!_Y +'ى<$|p'DLz!I-iIiH:LN!%WH~!UK>NvE.NBϺJ (NoNGZaKܼDAG6aewSghV; l6磎J;W:uԖfBTdG9d_~fgaĴUtBmM;f j:CfRä-縃Ug'`p_RV4"-=cDÃ`ʼҶwMp(q@=|ص}GA rB]JT̍]oNt;TwT;zIoΣiyB"mDnf:^&,fIu"U^/' lH~5^GEeE :qyDUrcay=e;iZҒ^ijɊ]Y\5V0ۆpgPŞ&(ΧqơYPW<ܽR#gQ- T0"7A39\T8Y!]oS:%=%OyQ5}%Mr4;4u@lR/ы)CgY NO3*ct$= !KƿkFǑg"UrZnk#JH=(ћ+\Z6$9g| ]umt>}$^pC~g&zǡᆻ=#ep>rh x0#ivb-UgRBWMx00'ǚhJ üw"S[rRYpӽ+$ J,` V{3@~tJ"R/K'9ov.xȡ/|7lٌ7G=7g4 شL7yh] .p%crܚg':v$VJ7Эcsu\>u<>.vyYB[l*\s&)FGc+>j2<n5]AVՈPEnǕʱ^${U|u' GΧCnǑt2YZޠX  9I$D V偒kSI+SMY)_enTo-Eʛt|r*>|kց!5INƾMs/:X4)?Bժ?DD <܎[i6dLFbyӟmM_/c70k Q[3kӍ;1[ilQ>b#rK^ʐtv5*Cu7[q5>[#ªjQH:O:@ʦXM;P~eirht*CI[?j#;}/jdʹ,޹afro.:f_5+?!ANl6ItHwX$rOT =ݍ$F3 f 2S `\0'!|?捜BcC kػ}`߾}8t ߋaDl<~gsEn%M$LNVbd,?Inޣ\ߪ||g(.SBdqh6ՊrQB{nSTsϾsr1~Xr)Q3/?.i(hƍUh%N،Wνwp!!xb%9*:MFT ꀰxـl/!a&$uD`Tb_LG5\HpF3ǡox_O,;%xp/3VEzӃ ,NXQGuN½Y32ӰHCb;$gGG+TִBJ2ZZu!},^TY%%Ԗҗ\_4 z.CZ~Òb$bC帆bcػyGmsP2WtBAV2BCVbS>eRDvGoOxvG 5Tø$=Zy,s2U:I+DCed5 :Ti&}&,ij~a^7T1{S&-(b}A?au'?U<.}^CO];#_e<d7aiKC/ Qd#>ϊݫIumGI(:˳ bxA}YaR6u6~uƏUv牸Ҹ姊yO*ˡh%*32Oډ5u ]"2K-^ɩ8]l#Ne[1gd8,0`'էAbiƮPi q8&퍲 VSv'M=VSD:QVyRn84.$f&.px{i(l.]wKRÔ4B /O/"~ΰ~z>ˈ z! MY)g9+ח2yX߹\wzTߵG/QL48>c̙(HGl?kMX\(iTk⬦EK :ùmuv~ϋ=arY,!)ϫpn8a^; jػa}R*ӒsMK{t|8e:q*n鐥Z6zcⰱ{YR'zhֺ\kW%G!5hm5_zAч}Fp5s<@ u.xҜFNߪZ4(D6~֐g3W%0ePԯqRGO>kkGcCxh-krEuZ/ȿAG.#'Pj$/UO OrkoGy~wy954~#œKIxkOR0O'8uIs c³J$G \=#];G(]>{S-6Fn'ѹMS `*n f_tҠh؏fnPI5ӑߴugYͽ!;1ovR־@ߠD ?>׬"K8rYvο˯;~/8^©K2?~zKv%-|(/c{CGm7ZOዖ]ihWm8eVmpp6nZ3j{\\jʧscx)Q3d]$MS?ʯ*.60}LQ.qzNi_L5lHnu9'<&:g=6MHxŮZZ̧iY[14#^lX]n ]vW^rJZF#2yTUU KZx0CFG:>_teI&"SDytE-4 H+_jbY3-P pQ\Bڠ&-/XWx;ED& 2t Jwp?3CFe_^n$(u$pYǓ6i>_e$:ӽ bi'sH)pA`xȫ/{TsPk+%祀փSc,zJ[] OWj(ҼݲScq62Zhzh$dTG]tjg&c ll"a=t퓛lJH4lU,tY)BryMjZX9/{qJ s߃ڡjG8'cBp<*'޽Ђpn0s e"b*tKlD${9t73}CJ8j{z{Kz,J|1Xi ^LjC{…V!)-+.{y\is[wU㗓!pZi7=F>6U_BQK֥;zҽ.11QzPyQ(\uE@}o0Π! d ➞ qH̱=)}fb^/DD\g""<hB() ,/􅔄k({<܌Ae U+1Hȃ[OR լB#:4.N, ozsŤI@NJM60"hی?SDws#כ],m/Tn)"U-#_̋0~@w,;L_>@wTH-54η &nry1ƄoE݉!hW&DQ52꒐6FҚoA[Xyƨ7PSnYǪRA~>eŸiq$Olp mam53p"h ?֯F#M}a~N-e' 7 lO X45os9/k~(?ܐR~JiˋtfK^hEv %MbhgPơwֈ̀t2ya͖-ze0p+`,k%?>A2i"\v -Nk6+ [Źfzki<*Wi/z|tZY}'͵N149!+OU]?*s| nf1ʹz47-Z0MTe>)nVqDޞdHU*ޒ{)j*B ۡT h))L[E<ʳ/vyTT*nx(;S-ï)dT)DD ݠmUʓnH|pX^6*-1;B DDx궢 UF@MF]fN2A2f(&& Po%MJqq7V#.**e`:NL!whz700o@NHq d0000De)K{DN"`dItM&&&?IFŢad2LB2f8&&&&&1L!/frMLLLLq610000!`  7k"`"`"`"`"pIDWq2՝Vw#2k02 uὝMG3&&&&&&&"H+֩?_ti"`"`"`"`"`"`"g .S4h"I` Z?ᛖ?uDDDDDDு <- Ck@`DDDDDDn @R/NIENDB`curator-apache-curator-5.5.0/src/site/site.xml000066400000000000000000000137401442004423600213210ustar00rootroot00000000000000 org.apache.maven.skins maven-fluido-skin 1.7 ApacheCurator true false ${project.url} apache/curator right black Apache Curator /images/curator-logo.png /index.html Apache http://apache.org/images/feather-small.gif http://apache.org